All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.algart.arrays.MappedDataStorages Maven / Gradle / Ivy

Go to download

Open-source Java libraries, supporting generalized smart arrays and matrices with elements of any types, including a wide set of 2D-, 3D- and multidimensional image processing and other algorithms, working with arrays and matrices.

There is a newer version: 1.4.23
Show newest version
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
 *
 * 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 net.algart.arrays;

import java.io.IOError;
import java.nio.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;

/**
 * 

A set of internal classes and static methods used for implementation of {@link DataStorage} * for a case of {@link LargeMemoryModel}. * * @author Daniel Alievsky */ class MappedDataStorages { private MappedDataStorages() {} static final Set allNonFinalizedMappedStorages = Collections.synchronizedSet(new HashSet<>()); private static boolean tempCleanerInstalled = false; static void installTempCleaner() { synchronized(LargeMemoryModel.allUsedDataFileModelsWithAutoDeletion) { if (!tempCleanerInstalled) { InternalUtils.addShutdownTask(new TempCleaner(), null); tempCleanerInstalled = true; } } } // LOGGER methods may not work in shutdown hook; // in this case, we use system console instead private static void severeEvenInHook(String msg, Throwable thrown) { if (!shutdownInProgress) { LargeMemoryModel.LOGGER.log(Level.SEVERE, msg, thrown); } System.err.println("SEVERE: " + msg); // to be on the safe side, always double the message on console thrown.printStackTrace(); } private static final boolean configLoggable = LargeMemoryModel.LOGGER.isLoggable(Level.CONFIG); private static void configEvenInHook(String msg) { if (!shutdownInProgress) { LargeMemoryModel.LOGGER.config(msg); } else if (configLoggable) { System.err.println("CONFIG: " + msg); } } private static final boolean warningLoggable = LargeMemoryModel.LOGGER.isLoggable(Level.WARNING); private static void warningEvenInHook(String msg) { if (!shutdownInProgress) { LargeMemoryModel.LOGGER.warning(msg); } else if (warningLoggable) { System.err.println("WARNING: " + msg); } } private static final boolean finestLoggable = LargeMemoryModel.LOGGER.isLoggable(Level.FINEST); private static void finestEvenInHook(String msg) { if (!shutdownInProgress) { LargeMemoryModel.LOGGER.finest(msg); } else if (finestLoggable) { System.out.println("FINEST: " + msg); // not System.err here! } } private static String finalizationTasksInfo() { return LargeMemoryModel.globalArrayFinalizer.activeTasksCount() + " array tasks, " + LargeMemoryModel.globalMappingFinalizer.activeTasksCount() + " mapping tasks, " + LargeMemoryModel.globalStorageFinalizer.activeTasksCount() + " storage tasks"; } static volatile boolean shutdownInProgress = false; private static class TempCleaner implements Runnable { public void run() { shutdownInProgress = true; int lastNumberOfFiles = -1; // some impossible value for (int attemptCount = 1; attemptCount <= LargeMemoryModel.DELETION_LOOPS_IN_CLEANER; attemptCount++) { Set all; synchronized (allNonFinalizedMappedStorages) { all = new HashSet<>(allNonFinalizedMappedStorages); } /* // for possible debugging Set models = LargeMemoryModel.allUsedDataFileModelsWithAutoDeletion(); for (DataFileModel m : models) { Set files = m.allTemporaryFiles(); System.err.println(" " + files.size() + " files in " + m); // for (DataFile f : files) System.err.println(" " + f); } System.err.println(" TOTALLY " + all.size()); */ int failedCount = 0; for (MappedStorage mappedStorage : all) { boolean ok = false; try { mappedStorage.dispose(MappedStorage.DisposeCaller.SHUTDOWN_HOOK); ok = true; } catch (Throwable ex) { // this exception was already logged by dispose method } if (ok) { allNonFinalizedMappedStorages.remove(mappedStorage); } else { failedCount++; } } if (failedCount == 0) { break; } int successfulCount = all.size() - failedCount; warningEvenInHook(failedCount + " storage files were not deleted in the attempt #" + attemptCount + (successfulCount > 0 ? " (but " + successfulCount + " were deleted)" : "") + "; we shall sleep for " + LargeMemoryModel.DELETION_SLEEP_DELAY + " ms and try again"); if (all.size() == lastNumberOfFiles) { break; } lastNumberOfFiles = all.size(); // so, we shall repeat iterations, that cannot delete nothing, 2 times as a maximum try { Thread.sleep(LargeMemoryModel.DELETION_SLEEP_DELAY); } catch (InterruptedException ex) { // nothing to do here: it is the termination of application } } } public String toString() { return "Standard AlgART shutdown cleaner"; } } private static boolean deleteWithSeveralAttempts(DataFileModel dfm, DataFile df, int timeoutInMillis) { if (timeoutInMillis < 0) { throw new IllegalArgumentException("Negative timeout"); } // We should not use System.currentTimeMillis() below, but need to count calls of Thread.sleep: // the system time, theoretically, can be changed during this loop for (; ; timeoutInMillis -= LargeMemoryModel.DELETION_SLEEP_DELAY) { if (timeoutInMillis <= 0) { return dfm.delete(df); // no catching: just return } Error error; try { return dfm.delete(df); } catch (Error e) { error = e; } try { //noinspection BusyWait Thread.sleep(Math.min(LargeMemoryModel.DELETION_SLEEP_DELAY, timeoutInMillis)); } catch (InterruptedException ex) { throw error; // return the last exception if interrupted } } } static class MappingSettings

{ final DataFileModel

dataFileModel; // necessary for deletion and LargeMemoryModel.getDataFileModel final DataFile existingDataFile; // may be null: it means it should be created as temporary final long dataFileStartOffset; final long dataFileEndOffset; // may be Long.MAX_VALUE final boolean temporary; final boolean readOnly; final boolean unresizable; final int numberOfBanks; // must be >=2 final long bankSizeInElements; // must be 2^k (for bits - in bits, not in longs) final int bankSizeInElementsLog; // must be 2^k (for bits - in bits, not in longs) final int bankSizeInBytes; final int bankSizeInBytesLog; final PArray lazyFillingPattern; private final Class arrayElementClass; private MappingSettings( DataFileModel

dataFileModel, DataFile dataFile, long dataFileStartOffset, long dataFileEndOffset, boolean temporary, boolean readOnly, boolean unresizable, Class arrayElementClass, PArray lazyFillingPattern) { Objects.requireNonNull(dataFileModel, "Null dataFileModel argument"); int numberOfBanks = dataFileModel.recommendedNumberOfBanks(); int bankSizeInBytes = dataFileModel.recommendedBankSize(unresizable); checkBankArguments(numberOfBanks, bankSizeInBytes); this.dataFileModel = dataFileModel; this.existingDataFile = dataFile; this.dataFileStartOffset = dataFileStartOffset; this.dataFileEndOffset = dataFileEndOffset; this.temporary = temporary; this.readOnly = readOnly; this.unresizable = unresizable; this.numberOfBanks = numberOfBanks; this.bankSizeInBytes = bankSizeInBytes; this.bankSizeInBytesLog = 31 - Integer.numberOfLeadingZeros(bankSizeInBytes); assert this.bankSizeInBytes == 1 << this.bankSizeInBytesLog; this.arrayElementClass = arrayElementClass; this.lazyFillingPattern = lazyFillingPattern == null ? null : lazyFillingPattern.asImmutable(); // asImmutable is necessary, in particular, to provide the fixed length in this.lazyFillingPattern if (lazyFillingPattern != null && arrayElementClass != lazyFillingPattern.elementType()) { throw new AssertionError("Illegal arrayElementClass argument: " + arrayElementClass + " (must be equal to the element type of the pattern array)"); } this.bankSizeInElements = arrayElementClass == boolean.class ? ((long)bankSizeInBytes) << 3 : arrayElementClass == char.class ? bankSizeInBytes >> DataStorage.BYTES_PER_CHAR_LOG : arrayElementClass == byte.class ? bankSizeInBytes : arrayElementClass == short.class ? bankSizeInBytes >> DataStorage.BYTES_PER_SHORT_LOG : arrayElementClass == int.class ? bankSizeInBytes >> DataStorage.BYTES_PER_INT_LOG : arrayElementClass == long.class ? bankSizeInBytes >> DataStorage.BYTES_PER_LONG_LOG : arrayElementClass == float.class ? bankSizeInBytes >> DataStorage.BYTES_PER_FLOAT_LOG : arrayElementClass == double.class ? bankSizeInBytes >> DataStorage.BYTES_PER_DOUBLE_LOG : -1; if (bankSizeInElements == -1) { throw new AssertionError("Illegal arrayElementClass argument: " + arrayElementClass); } this.bankSizeInElementsLog = 63 - Long.numberOfLeadingZeros(bankSizeInElements); assert this.bankSizeInElements == 1L << this.bankSizeInElementsLog; } static

MappingSettings

getInstanceForExistingFile( DataFileModel

dataFileModel, DataFile dataFile, long dataFileStartOffset, long dataFileEndOffset, boolean readOnly, Class arrayElementClass) { Objects.requireNonNull(dataFile, "Null dataFile argument"); return new MappingSettings<>(dataFileModel, dataFile, dataFileStartOffset, dataFileEndOffset, false, readOnly, true, arrayElementClass, null); } static

MappingSettings

getInstanceForTemporaryFile( DataFileModel

dataFileModel, Class arrayElementClass, boolean unresizable, PArray lazyFillingPattern) { Objects.requireNonNull(dataFileModel, "Null dataFileModel argument"); return new MappingSettings<>(dataFileModel, null, Math.max(0, dataFileModel.recommendedPrefixSize()), Long.MAX_VALUE, true, false, unresizable, arrayElementClass, lazyFillingPattern); } MappingSettings

getCompatibleInstanceForNewTemporaryFile(boolean unresizable) { return new MappingSettings<>(this.dataFileModel, null, this.dataFileStartOffset, Long.MAX_VALUE, true, false, unresizable, this.arrayElementClass, null); } void finalizationNotify(Object dataFilePath, boolean isApplicationShutdown) { this.dataFileModel.finalizationNotify(InternalUtils.

cast(dataFilePath), isApplicationShutdown); } static void checkBankArguments(int numberOfBanks, int bankSizeInBytes) { if (numberOfBanks < 2) { throw new IllegalArgumentException("Number of banks should be 2 or greater"); } if (bankSizeInBytes < 256) { throw new IllegalArgumentException("Bank size should be 256 or greater"); } if ((bankSizeInBytes & (bankSizeInBytes - 1)) != 0) { throw new IllegalArgumentException("Bank size should be 2^k"); } // bankSizeInBytes is positive and 2^k, so, bankSizeInBytes<=2^30 } static int nearestCorrectNumberOfBanks(int numberOfBanks) { return Math.max(numberOfBanks, 2); } static int nearestCorrectBankSize(int bankSizeInBytes) { return Integer.highestOneBit(Math.max(bankSizeInBytes, 256)); } } /** * The complex implementation of data storage, based on mapped {@link DataFile data files}. * This implementation contains a swapping manager allowing to access large files by blocks. */ static abstract class MappedStorage extends DataStorage { /** * Mapping settings used by this storage. */ final MappingSettings ms; /** * Will be this file automatically deleted while finalization or shutdown hook. */ private volatile boolean autoDeleted; /** * High index bits: all other (low) index describes the position inside a bank. */ long indexHighBits; /** * Current number of used banks: 0..bh.length-1. * For all unused banks bh[k]==null. */ private int bankCount = 0; /** * Current set of memory banks. */ final DataFile.BufferHolder[] bh; /** * Specific buffers (ByteBuffer[], CharBuffer[], etc.) corresponding to bh[] elements. * {@code null} for empty banks. */ final Buffer[] specBufs; // Should be final to avoid partial object initialization in shutdown hook! /** * The array element #bankPos[k] corresponds to * element (bit, char, etc.) #0 in the bank #k. * Do not include subarray offsets. * -1 indicates empty banks. */ final long[] bankPos; /** * Work memory for {@link #flushResources(long, long, boolean)} and {@link #freeResources(Array, boolean)}. */ private final int[] bankIndexes; /** * All banks, starting since {@link MappingSettings#dataFileStartOffset}, * are supposed to be lazy filled and are not loaded from the file, * if the corresponding bits in this array is 1. * It is the simple AlgART array, growing together with the storage. */ private MutableBitArray lazyFillMapOfBanks; /** * All data since the byte #lazyFillPosInBytes * are supposed to be lazy filled and are not loaded from the file. * Start padding ({@link MappingSettings#dataFileStartOffset}) is included into this value: * it is the offset from the file begin, not from dataFileStartOffset. */ private long lazyFillPosInBytes; /** * If true, only first and second banks will be used and all the file will be mapped always. * Is set in the constructor for existing files and in allocate method for temporary unresizable file. */ boolean singleMapping = false; /** * true if all methods must be synchronized via {@link #lock}. * May be cleared to false by {@link #setSingleMappingModeIfNecessary()} method * (only in a case when {@link #singleMapping} is true). */ boolean syncNecessary = true; /** * If {@link #syncNecessary} is false, this field is filled by a copy of the * only {@link #specBufs specific buffer}, used for file mapping, or by {@code null} * in a case of releasing resources, every time when the file is mapped or released * (in a synchronized block). Non-synchronized access method must check this field * to be sure that they "see" the actual, fully initialized buffer object. */ volatile Object validSingleSpecBufForThisThread = new Object(); /** * The number of non-finalized attached Array instances. * If 0, then the {@link #autoDeleted auto-deleted} file will be planned to be deleted. */ private int arrayCounter = 0; /** * The number of non-finalized mappings. * If 0, the {@link #autoDeleted auto-deleted} file will be deleted (if arrayCounter==0). */ private int mappingCounter = 0; /** * The number of ByteBuffers that are in use. * Used if UNSAFE_UNMAP_ON_EXIT to not try unmap files that are in use now. */ private final AtomicInteger mappingInUseCounter = new AtomicInteger(0); /** * Indicates whether mappingInUseCounter was overflown at least once. */ private volatile boolean mappingInUseCounterOverflow = false; /** * The data file (an instance of {@link DataFile} class) used by this storage. * This field is filled in the constructor and set to {@code null} * before file deletion; no any other modifications of this field are not allowed. */ private volatile DataFile dataFile = null; // volatile is necessary because this field is set to {@code null} while deletion a parralel finalization /** * This field is the same as {@link #dataFile} with the only exception: * if this data storage was finalized, but an error was occurred attemt to delete this file * while finalization, then {@link #dataFile} will be assigned to {@code null}, * but this field will not. */ private volatile DataFile dataFileForDeletion = null; /** * This field is set to true after successful disposing via {@link #dispose()} method * or standard standard shutdown cleanup procedure. It informs finalizers that there is no * sense to try to delete the data file. */ private boolean disposeCalled = false; /** * This flag informs that this storage was already attempted to be finalized, * but finalization failed due to some exceptions while file deletion. * For logging goals only. (Not used in current implementation.) */ private volatile boolean deletionErrorWhileFinalization = false; /** * The number of failed attempts to delete a file while finalization. */ private int countOfFailedDeletionsWhileFinalization = 0; /** * This flag informs whether {@link #dataFile} is {@code null} as a result of standard shutdown * cleanup procedure, but not as a result of direct calling {@link #dispose()} method. * For logging goals only. (Not used in current implementation.) */ private volatile boolean deletedWhileShutdown = false; /** * The current length of {@link #dataFile}. */ private long dataFileLength; /** * The name of {@link #dataFile}. Used for "postmortem" logging and for finalization notification * when dataFile is already null. */ private final Object dataFilePath; /** * true if this storage is unresizable. * It is set by {@link #allocate} method, not in the constructor. */ private boolean unresizable = false; /** * The lock used for most operations. */ final ReentrantLock lock = new ReentrantLock(); /** * The lock used for counters in finalizers only. Locked for very short time only. */ private final ReentrantLock lockForGc = new ReentrantLock(); private volatile Object finalizationHolder = null; private final boolean finerLoggable = LargeMemoryModel.LOGGER.isLoggable(Level.FINER); private final boolean finestLoggable = LargeMemoryModel.LOGGER.isLoggable(Level.FINEST); private final ArrayComparator bankOrderComparator = new ArrayComparator() { public boolean less(long firstIndex, long secondIndex) { int first = (int)firstIndex, second = (int)secondIndex; return bh[first] != null && (bh[second] == null || bankPos[first] < bankPos[second]); } }; MappedStorage(MappingSettings ms) { assert ms.temporary == (ms.existingDataFile == null) : "ms.temporary != (ms.existingDataFile == null)"; if (ms.temporary) { assert !ms.readOnly : "ms.readOnly cannot be true for temporary files"; } this.ms = ms; this.indexHighBits = ~(ms.bankSizeInElements - 1); this.bh = new DataFile.BufferHolder[ms.numberOfBanks]; if (this instanceof MappedBitStorage) { this.specBufs = new LongBuffer[ms.numberOfBanks]; } else if (this instanceof MappedByteStorage) { this.specBufs = new ByteBuffer[ms.numberOfBanks]; } else if (this instanceof MappedCharStorage) { this.specBufs = new CharBuffer[ms.numberOfBanks]; } else if (this instanceof MappedShortStorage) { this.specBufs = new ShortBuffer[ms.numberOfBanks]; } else if (this instanceof MappedIntStorage) { this.specBufs = new IntBuffer[ms.numberOfBanks]; } else if (this instanceof MappedLongStorage) { this.specBufs = new LongBuffer[ms.numberOfBanks]; } else if (this instanceof MappedFloatStorage) { this.specBufs = new FloatBuffer[ms.numberOfBanks]; } else if (this instanceof MappedDoubleStorage) { this.specBufs = new DoubleBuffer[ms.numberOfBanks]; } else { throw new AssertionError("Illegal inheritor " + this.getClass()); } this.bankPos = new long[ms.numberOfBanks]; for (int k = 0; k < this.bankPos.length; k++) { this.bankPos[k] = -1; // unmatched positions } this.bankIndexes = new int[ms.numberOfBanks]; final ReentrantLock lock = this.lock; lock.lock(); // synchronization with possible accesses via allNonFinalizedMappedStorages synchronized(allNonFinalizedMappedStorages) { // atomic creating and deleting temporary files try { if (shutdownInProgress) { throw new IOError(new IllegalStateException( "Cannot allocate new AlgART array: shutdown is in progress")); } if (!ms.temporary) { this.dataFileForDeletion = this.dataFile = ms.existingDataFile; this.unresizable = true; this.dataFile.open(ms.readOnly); // lazyFillPosInBytes will not be used } else { this.dataFileForDeletion = this.dataFile = ms.dataFileModel.createTemporary(ms.unresizable); this.dataFile.open(false); this.dataFile.length(ms.dataFileStartOffset); // - if custom implementation of createTemporary has returned an old file, // we truncate it here to avoid nonaligned lazyFillPosInBytes; // if it has returned non-existing file (has not really create it), // we create it by open(false) this.lazyFillPosInBytes = ms.dataFileStartOffset; this.lazyFillMapOfBanks = SimpleMemoryModel.getInstance().newEmptyBitArray(1024); // - the capacity 1024 requires only 16 longs and already allows to check 2GB for 2MB banks/ // Note: it's capacity, not length! Starting size of the lazy map is 0. LargeMemoryModel.LOGGER.fine("Temporary array storage file " + dataFile + " is successfully created"); } this.dataFilePath = ms.dataFileModel.getPath(this.dataFile); this.dataFileLength = this.dataFile.length(); if (!ms.temporary) { setSingleMappingModeIfNecessary(); // for temporary files, will be set later after setting capacity } this.autoDeleted = ms.temporary; allNonFinalizedMappedStorages.add(this); } finally { lock.unlock(); } } scheduleFinalizationForAllStorage(); } public boolean isAutoDeleted() { return this.autoDeleted; } public void setAutoDeleted(boolean value) { DataFile df = this.dataFile; this.autoDeleted = value; if (df != null) { ms.dataFileModel.setTemporary(df, value); } } public DataFileModel getDataFileModel() { return ms.dataFileModel; } public Object getDataFilePath() { return ms.dataFileModel.getPath(dataFile); } public String toString() { return "mapped" + (ms.lazyFillingPattern == null ? "" : " lazy") + " file (" + dataFilePath + (dataFile == null ? ": DISPOSED" : "") + (isAutoDeleted() ? ", temporary)" : ", external)"); } MemoryModel newCompatibleMemoryModel() { return LargeMemoryModel.getInstance(InternalUtils.>cast(ms.dataFileModel)); // - avoding unchecked warning: dataFileModel is generalized, but MappedStored is not } /** * Returns index&~indexHighBits. * The same offset will be returned by {@link #translateIndex(long)} method. * * @param index the index of some data element in the source array (not subarray). * @return the offset of this element in the bank. */ final long offset(long index) { return index & ~indexHighBits; // not "~(ms.bankSizeInElements - 1)": single mapping possible } /** * Equivalent to {@link #translateIndex(long, boolean) translateIndex(index, false)}. * * @param index the index of the data element in the source array (not subarray). * @return the index of the data element in the bank #0. */ final long translateIndex(long index) { if ((index & indexHighBits) == bankPos[0]) { assert this instanceof MappedBitStorage || index - bankPos[0] <= Integer.MAX_VALUE; return index - bankPos[0]; } else { return translateFailedIndex(index, false, LoadingMode.DEFAULT); } } /** * Returns the index of the element of the first or second * {@link #specBufs specific buffer} * (bank #0 or #1), depending on useSecondBank argument, * which contains the data element #index. * If this data element is not in the first / second bank, * changes order of banks and, if necessary, remaps the first / second bank to assure * that the element #index is in it. * *

If useSecondBank is true, the content of the bank #0 * is not changed. So, you may call:

         * long i1 = translateIndex(index, false);
         * long i2 = translateIndex(index, true);
         * 
* and be sure that the i1 will remain actual after the second "translateIndex". * *

Important: the reverse statement is not true! If you call:

         * long i2 = translateIndex(index, true);
         * long i1 = translateIndex(index, false);
         * 
* then the second "translateIndex" may replace the bank #1 loaded by the first call. * *

For bits, returns the index of the bit * (it is the only case when it can be greater than 2^31). * * @param index the index of the data element in the source array (not subarray). * @param useSecondBank if true, works with bank #1 and doesn't correct bank #0; * else works with bank #0. * @return the index of the data element in the bank #0 or #1. */ final long translateIndex(long index, boolean useSecondBank) { int bank = useSecondBank ? 1 : 0; if ((index & indexHighBits) == bankPos[bank]) { assert this instanceof MappedBitStorage || index - bankPos[bank] <= Integer.MAX_VALUE; return index - bankPos[bank]; } else { return translateFailedIndex(index, useSecondBank, LoadingMode.DEFAULT); } } /** * Equivalent to {@link #translateIndex(long)} that, when possible, * skips reading data from the data file: previous content * of the corresponding file block will be ignored. * Used for optimization of large multi-bank writing operations. * Never used in the {@link #singleMapping single mapping mode}. * * @param index the index of the data element in the source array (not subarray). * @return the index of the data element in the bank #0. */ final long translateIndexWO(long index) { if ((index & indexHighBits) == bankPos[0]) { assert this instanceof MappedBitStorage || index - bankPos[0] <= Integer.MAX_VALUE; return index - bankPos[0]; } else { return translateFailedIndex(index, false, LoadingMode.NOT_LOAD_DATA_FROM_FILE); } } /** * Equivalent to {@link #translateIndex(long, boolean)} that, when possible, * skips reading data from the data file: previous content * of the corresponding file block will be ignored. * Used for optimization of large multi-bank writing operations. * Never used in the {@link #singleMapping single mapping mode}. * * @param index the index of the data element in the source array (not subarray). * @param useSecondBank if true, works with bank #1 and doesn't correct bank #0; * else works with bank #0. * @return the index of the data element in the bank #0 or #1. */ final long translateIndexWO(long index, boolean useSecondBank) { int bank = useSecondBank ? 1 : 0; if ((index & indexHighBits) == bankPos[bank]) { assert this instanceof MappedBitStorage || index - bankPos[bank] <= Integer.MAX_VALUE; return index - bankPos[bank]; } else { return translateFailedIndex(index, useSecondBank, LoadingMode.NOT_LOAD_DATA_FROM_FILE); } } /** * Equivalent to {@link #translateIndex(long, boolean)} if notLoadDataFromFile * is false or {@link #translateIndexWO(long, boolean)} if it is true. * * @param index the index of the data element in the source array (not subarray). * @param useSecondBank if true, works with bank #1 and doesn't correct bank #0; * else works with bank #0. * @param notLoadDataFromFile if true and there is no required bank in memory, * the data may be not really loaded from the data file: the old content * of the corresponding file block will be ignored. * @return the index of the data element in the bank #0 or #1. */ final long translateIndex(long index, boolean useSecondBank, boolean notLoadDataFromFile) { int bank = useSecondBank ? 1 : 0; if ((index & indexHighBits) == bankPos[bank]) { assert this instanceof MappedBitStorage || index - bankPos[bank] <= Integer.MAX_VALUE; return index - bankPos[bank]; } else { return translateFailedIndex(index, useSecondBank, notLoadDataFromFile ? LoadingMode.NOT_LOAD_DATA_FROM_FILE : LoadingMode.DEFAULT); } } enum LoadingMode {DEFAULT, PRELOAD_DATA_FROM_FILE, NOT_LOAD_DATA_FROM_FILE} /** * Reduced version of {@link #translateIndex(long, boolean)} and * {@link #translateIndexWO(long, boolean)} that works only * if the required element is not currently in the bank. * (Full method calls this one.) * * @param index the index of the data element in the source array (not subarray). * @param useSecondBank if true, works with bank #1 and doesn't correct bank #0; * else works with bank #0. * @param loadingMode if {@link LoadingMode#NOT_LOAD_DATA_FROM_FILE} and * there is no required bank in memory, * the data may be not really loaded from the data file: the old content * of the corresponding file block will be ignored; * if {@link LoadingMode#PRELOAD_DATA_FROM_FILE} and * there is no required bank in memory, * calls {@link DataFile.BufferHolder#load()} after loading the bank. * @return the index of the data element in the bank #0 or #1. */ final long translateFailedIndex(long index, boolean useSecondBank, LoadingMode loadingMode) { assert index >= 0 : "Negative index " + index; assert bh.length >= 2 : "Too low total number of banks: " + bh.length; final ReentrantLock lock = this.lock; lock.lock(); // All corrections of bh, specBuf, bankPos and bankCount MUST be synchronized always, // even if this.syncNecessary was cleared: we still may call freeResources that changes them. try { long result; final boolean firstTwoBanksAreSame, holeAt0; boolean bankWasLoaded = false; // The bank set is always supported in the following state: // non-empty or null bank #0 // non-empty bank #1 // ... // non-empty bank #bankCount-1 // null // null // ... // null (at position #bh.length-1) // The first bank (#0) can be null (a "hole") if we load data in the second bank many times: // this operation, according the contract, does not destroy the bank #0 // (though it could do it without any risk). // // The banks #0 and #1 may be the same - but only these ones. // All other bank pairs are always different. // // The unmap() method is always called for bh[bb.length-1] only, excepting the only case // of releaseFileAndMapping(), where it is called for all banks // (including #0, if it is not equal to #1). main: { final int firstBank = useSecondBank ? 1 : 0; if ((index & indexHighBits) == bankPos[firstBank]) { // The index is not really failed. This situation is possible if !this.syncNecessary, // when releaseFileAndMapping() is called and then two threads perform // simultaneous non-synchronized access (for example, by translateIndex method): // the first thread calls this method (translateFailedIndex), // it restores the mapping, and then the second thread calls this method. return index - bankPos[firstBank]; } // Now we are sure that the required bank is not firstBank int b = firstBank + 1; long foundBankPos = -1; DataFile.BufferHolder foundBh = null; Buffer foundSpecBuf = null; while (b < bankCount) { foundBankPos = bankPos[b]; if ((index & indexHighBits) == foundBankPos) { // It is a suitable bank. foundBh = bh[b]; foundSpecBuf = specBufs[b]; break; // If the bank was found, b is its index (AFTER firstBank). } b++; // If the bank was not found, b is max(bankCount,firstBank+1). } // Usually, we now shift all banks #firstBank..#b-1 to positions #firstBank+1..#b, // to free the position #firstBank, and then: // A. unmap the last bank if the required bank was not found, or // B. move the found bank #b to the required position #firstBank // ("rotating" array range #firstBank..#b) firstTwoBanksAreSame = bankPos[0] == bankPos[1]; // (may be -1: empty banks) holeAt0 = !useSecondBank && bh[0] == null; if (firstTwoBanksAreSame // However, it is a special situation: two identical (and unsuitable) buffers // in banks #0 and #1. // We guarantee that duplicates are possible only in banks #0 and #1: // it's necessary to avoid prohibited twice unmapping (the case A). // Now we just need to forget (replace) the bank #firstBank. || holeAt0) { // It is also a special situation: we need to place the required bank at index #0 // (firstBank=0), and here is a hole, so we don't need to free position #firstBank if (foundBh != null) { // The bank was found: it is foundBh. Storing it at #firstBank. bh[firstBank] = foundBh; specBufs[firstBank] = foundSpecBuf; bankPos[firstBank] = foundBankPos; // Now we MUST clear the found bank #b to avoid duplicating. // To do this, we need to shift all array and reduce its length. // Here bankCount >= firstBank+2 >= 2 (because we found the bank #b >= firstBank+1). assert bankCount >= firstBank + 2 : "bankCount < firstBank + 2"; if (b == 1) { // so, firstBank==0 and we've found required bank at #1: sitation with a hole at #0 assert firstBank == 0 : "firstBank != 0"; assert holeAt0 : "here should be hole at #0"; result = index - foundBankPos; break main; // do not destroy the bank #1: let it stay a duplicate of #0 } bankCount--; for (; b < bankCount; b++) { bh[b] = bh[b + 1]; specBufs[b] = specBufs[b + 1]; bankPos[b] = bankPos[b + 1]; } bh[bankCount] = null; specBufs[bankCount] = null; bankPos[bankCount] = -1; result = index - foundBankPos; } else { // The bank was not found: just loading new bank at #firstBank bankCount = Math.max(bankCount, firstBank + 1); // bankCount must be corrected BEFORE loadBank that checks it mapBank(index & indexHighBits, useSecondBank, loadingMode); bankWasLoaded = true; result = index - bankPos[firstBank]; } } else { // Ordinary situation: bank #0 and #1 are different, // and (when firstBank=0) there is no "hole" at position #0. // Now b = index of the found bank or, if not found, b = max(bankCount, firstBank+1). assert foundBh != null || b == Math.max(bankCount, firstBank + 1) : "bank not found, but b=" + b + " != max(bankCount,firstBank+1)=max(" + bankCount + "," + (firstBank + 1) + ")"; if (foundBh == null && b == bh.length) { // The bank was not found, but the bank set is full: the last element must be removed. if (unmapBank(--b, ReleaseMode.DATA_MUST_STAY_ALIVE, false)) { --bankCount; // we MUST unmap all lost buffers: some data file models will not work without it } assert b == bankCount; assert b == bh.length - 1; } // Here it's possible that b=firstBank (not >=firstBank+1) even when foundBh=null, // in the only situation when bb.length=2 and firstBank=1: // we have unmapped the unsuitable bank #1 in the previous operation // (or, maybe, bankCount was 1 and bank #1 was free). // Shifting array: the last element or found bank #b will be replaced. // So, popular banks will be always at the beginning of array. for (; b > firstBank; b--) { bh[b] = bh[b - 1]; specBufs[b] = specBufs[b - 1]; bankPos[b] = bankPos[b - 1]; } // The first position now contains duplicate information. if (foundBh != null) { // The bank was found: it is foundBh. Storing it at the first position. bh[firstBank] = foundBh; specBufs[firstBank] = foundSpecBuf; bankPos[firstBank] = foundBankPos; result = index - foundBankPos; break main; } if (useSecondBank && (index & indexHighBits) == bankPos[0]) { // We MUST use bank #0 in this case instead of the mapping: // twice mapping may not work in some data file models. bh[1] = bh[0]; bankPos[1] = bankPos[0]; specBufs[1] = specBufs[0]; bankCount++; result = index - bankPos[1]; break main; } // There is no required bank in bh[] array. bankCount++; // bankCount must be corrected BEFORE loadBank that checks it mapBank(index & indexHighBits, useSecondBank, loadingMode); bankWasLoaded = true; result = index - bankPos[firstBank]; } } // main: assert result >= 0; assert this instanceof MappedBitStorage || result <= Integer.MAX_VALUE; AssertionError ae; assert!finestLoggable || bankWasLoaded || (ae = logAndCheckBanks( index, useSecondBank, firstTwoBanksAreSame ? LogAllBanksCallPlace.translateFailedIndexSame : holeAt0 ? LogAllBanksCallPlace.translateFailedIndexHole : LogAllBanksCallPlace.translateFailedIndex, Level.FINEST)) == null: ae.getMessage(); // "assert" keyword allows to automatically remove this logging // if !finestLoggable (usual situation), logAndCheckBanks will not be called even if assertion enabled // due to the standard semantics of || operator in Java // if bankWasLoaded, logAndCheckBanks is not needed: it is called inside loadBank // logAllBanks always returns true, so no assertion error is possible here return result; } finally { lock.unlock(); } } ByteOrder byteOrder() { return dataFile.byteOrder(); } void allocate(long capacity, boolean unresizable) { lock.lock(); try { changeCapacity(capacity, 0, 0, unresizable); } finally { lock.unlock(); } } DataStorage changeCapacity(long newCapacity, long offset, long length) { lock.lock(); try { changeCapacity(newCapacity, offset, length, false); return this; } finally { lock.unlock(); } } //[[Repeat.SectionStart mapped_getData_method_impl]] void getData(long pos, Object destArray, int destArrayOffset, int count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: getDataFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; int len = singleMapping ? count : (int)Math.min(count, bse - ofs); if (isBankLazyAndNotFilledYet(pos)) { getUninitializedLazyDataFromBank(pos, destArray, destArrayOffset, len); } else { //EndOfLazy !! this comment is necessary for preprocessing by Repeater !! incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = specBufs[0]; } finally { lock.unlock(); } getDataFromFirstBank(buf, ofs, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = (int)Math.min(count, bse); if (isBankLazyAndNotFilledYet(pos)) { getUninitializedLazyDataFromBank(pos, destArray, destArrayOffset, len); } else { //EndOfLazy !! this comment is necessary for preprocessing by Repeater !! incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = specBufs[0]; } finally { lock.unlock(); } getDataFromFirstBank(buf, 0, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.SectionEnd mapped_getData_method_impl]] void setData(long pos, Object srcArray, int srcArrayOffset, int count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: setDataInFirstBank access the specBuf, // that can become null at any moment final long bse = ms.bankSizeInElements; long ofs = offset(pos); assert ofs >= 0; int len = singleMapping ? count : (int)Math.min(count, bse - ofs); if (singleMapping || ofs > 0) { // in another case, all tasks can be performed by the loop incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long o = translateIndex(pos); assert o == ofs; buf = specBufs[0]; } finally { lock.unlock(); } setDataInFirstBank(buf, ofs, srcArray, srcArrayOffset, len); } finally { decrementMappingInUseCounter(); } srcArrayOffset += len; pos += len; count -= len; } for (; count > 0; pos += bse, srcArrayOffset += bse, count -= bse) { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { if (count >= bse) { len = (int)bse; ofs = translateIndexWO(pos); } else { len = count; ofs = translateIndex(pos); } assert ofs == 0; buf = specBufs[0]; } finally { lock.unlock(); } setDataInFirstBank(buf, 0, srcArray, srcArrayOffset, len); } finally { decrementMappingInUseCounter(); } } } void fillData(long pos, long count, Object fillerWrapper) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; lock.lock(); // synchronization is necessary always: fillDataInFirstBank access the specBuf, // that can become null at any moment try { final long bse = ms.bankSizeInElements; long ofs = offset(pos); assert ofs >= 0; long len = singleMapping ? count : Math.min(count, bse - ofs); if (singleMapping || ofs > 0) { // in another case, all tasks can be performed by the loop long o = translateIndex(pos); assert o == ofs; fillDataInFirstBank(ofs, len, fillerWrapper); pos += len; count -= len; } for (; count > 0; pos += bse, count -= bse) { if (count >= bse) { len = bse; ofs = translateIndexWO(pos); } else { len = count; ofs = translateIndex(pos); } assert ofs == 0; fillDataInFirstBank(0, len, fillerWrapper); } } finally { lock.unlock(); } } boolean copy(DataStorage src, long srcPos, long destPos, long count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return true; } if (src == this && srcPos == destPos) { return true; } if (!(src instanceof MappedStorage stor)) { return false; } boolean useSecondBank = src == this; // it is better to avoid extra mappings when possible lock.lock(); stor.lock.lock(); // synchronization is necessary always: copyFirstBank access the specBuf, // that can become null at any moment try { assert srcPos >= 0; assert destPos >= 0; assert count >= 0; long srcBlock = stor.singleMapping ? Long.MAX_VALUE : stor.ms.bankSizeInElements; long destBlock = singleMapping ? Long.MAX_VALUE : ms.bankSizeInElements; if (src == this && srcPos <= destPos && srcPos + count > destPos) { // copy in reverse order srcPos += count; destPos += count; long srcOfs = stor.translateIndex(srcPos - 1); long destOfs = offset(destPos - 1); long destO = translateIndex(destPos - 1, true, destOfs == destBlock - 1 && count >= destBlock); assert destO == destOfs; for (; ; ) { assert srcOfs >= 0; assert destOfs >= 0; long srcLen = srcOfs + 1; long destLen = destOfs + 1; assert srcLen >= 0; assert destLen >= 0; if (srcLen < destLen) { if (srcLen >= count) { copyFirstBank(stor, srcLen - count, destLen - count, count, true, true); break; } else { assert !stor.singleMapping; copyFirstBank(stor, 0, destLen - srcLen, srcLen, true, true); srcPos -= srcLen; destPos -= srcLen; count -= srcLen; srcOfs = stor.translateIndex(srcPos - 1); assert srcOfs == srcBlock - 1; destOfs -= srcLen; translateIndex(destPos, true); // - previous translateIndex could destroy the second bank; // this call should not lead to any mappings } } else { if (destLen >= count) { copyFirstBank(stor, srcLen - count, destLen - count, count, true, true); break; } else { assert !singleMapping; copyFirstBank(stor, srcLen - destLen, 0, destLen, true, true); srcPos -= destLen; destPos -= destLen; count -= destLen; if (srcLen == destLen) { srcOfs = stor.translateIndex(srcPos - 1); } else { srcOfs -= destLen; } destOfs = translateIndex(destPos - 1, true, count >= destBlock); assert destOfs == destBlock - 1; } } } } else { // copy in normal order long srcOfs = stor.translateIndex(srcPos); long destOfs = offset(destPos); long destO = translateIndex(destPos, useSecondBank, destOfs == 0 && count >= destBlock); assert destO == destOfs; for (; ; ) { assert srcOfs >= 0; assert destOfs >= 0; long srcLen = srcBlock - srcOfs; long destLen = destBlock - destOfs; assert srcLen >= 0; assert destLen >= 0; if (srcLen < destLen) { if (srcLen >= count) { copyFirstBank(stor, srcOfs, destOfs, count, false, useSecondBank); break; } else { assert !stor.singleMapping; copyFirstBank(stor, srcOfs, destOfs, srcLen, false, useSecondBank); srcPos += srcLen; destPos += srcLen; count -= srcLen; srcOfs = stor.translateIndex(srcPos); assert srcOfs == 0; destOfs += srcLen; if (useSecondBank) { translateIndex(destPos, true); } // - previous translateIndex could destroy the second bank; // this call should not lead to any mappings } } else { if (destLen >= count) { copyFirstBank(stor, srcOfs, destOfs, count, false, useSecondBank); break; } else { assert !singleMapping; copyFirstBank(stor, srcOfs, destOfs, destLen, false, useSecondBank); srcPos += destLen; destPos += destLen; count -= destLen; if (srcLen == destLen) { srcOfs = stor.translateIndex(srcPos); } else { srcOfs += destLen; } destOfs = translateIndex(destPos, useSecondBank, count >= destBlock); assert destOfs == 0; } } } } } finally { stor.lock.unlock(); lock.unlock(); } return true; } boolean swap(DataStorage another, long anotherPos, long thisPos, long count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return true; } if (another == this && anotherPos == thisPos) { return true; } if (!(another instanceof MappedStorage stor)) { return false; } boolean useSecondBank = another == this; // it is better to avoid extra mappings when possible lock.lock(); stor.lock.lock(); // synchronization is necessary always: swapDataInFirstBank access the specBuf, // that can become null at any moment try { assert anotherPos >= 0; assert thisPos >= 0; assert count >= 0; long anotherBlock = stor.singleMapping ? Long.MAX_VALUE : stor.ms.bankSizeInElements; long thisBlock = singleMapping ? Long.MAX_VALUE : ms.bankSizeInElements; long anotherOfs = stor.translateIndex(anotherPos); long thisOfs = translateIndex(thisPos, useSecondBank); for (; ; ) { assert anotherOfs >= 0; assert thisOfs >= 0; long anotherLen = anotherBlock - anotherOfs; long thisLen = thisBlock - thisOfs; assert anotherLen >= 0; assert thisLen >= 0; if (anotherLen < thisLen) { if (anotherLen >= count) { swapFirstBank(stor, anotherOfs, thisOfs, count, useSecondBank); break; } else { assert !stor.singleMapping; swapFirstBank(stor, anotherOfs, thisOfs, anotherLen, useSecondBank); anotherPos += anotherLen; thisPos += anotherLen; count -= anotherLen; anotherOfs = stor.translateIndex(anotherPos); assert anotherOfs == 0; thisOfs += anotherLen; translateIndex(thisPos, true); // - previous translateIndex could destroy the second bank } } else { if (thisLen >= count) { swapFirstBank(stor, anotherOfs, thisOfs, count, useSecondBank); break; } else { assert !singleMapping; swapFirstBank(stor, anotherOfs, thisOfs, thisLen, useSecondBank); anotherPos += thisLen; thisPos += thisLen; count -= thisLen; if (anotherLen == thisLen) { anotherOfs = stor.translateIndex(anotherPos); } else { anotherOfs += thisLen; } thisOfs = translateIndex(thisPos, useSecondBank); assert thisOfs == 0; } } } } finally { stor.lock.unlock(); lock.unlock(); } return false; } //[[Repeat(INCLUDE_FROM_FILE, THIS_FILE, mapped_getData_method_impl) // if\s*\(isBankLazyAndNotFilledYet.*?\{\s*\/\/EndOfLazy.*?((?:\r(?!\n)|\n|\r\n)\s*) ==> {$1;; // getData ==> minData !! Auto-generated: NOT EDIT !! ]] void minData(long pos, Object destArray, int destArrayOffset, int count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: minDataFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; int len = singleMapping ? count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = specBufs[0]; } finally { lock.unlock(); } minDataFromFirstBank(buf, ofs, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = specBufs[0]; } finally { lock.unlock(); } minDataFromFirstBank(buf, 0, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.IncludeEnd]] //[[Repeat(INCLUDE_FROM_FILE, THIS_FILE, mapped_getData_method_impl) // if\s*\(isBankLazyAndNotFilledYet.*?\{\s*\/\/EndOfLazy.*?((?:\r(?!\n)|\n|\r\n)\s*) ==> {$1;; // getData ==> maxData !! Auto-generated: NOT EDIT !! ]] void maxData(long pos, Object destArray, int destArrayOffset, int count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: maxDataFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; int len = singleMapping ? count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = specBufs[0]; } finally { lock.unlock(); } maxDataFromFirstBank(buf, ofs, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = specBufs[0]; } finally { lock.unlock(); } maxDataFromFirstBank(buf, 0, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.IncludeEnd]] //[[Repeat(INCLUDE_FROM_FILE, THIS_FILE, mapped_getData_method_impl) // if\s*\(isBankLazyAndNotFilledYet.*?\{\s*\/\/EndOfLazy.*?((?:\r(?!\n)|\n|\r\n)\s*) ==> {$1;; // getData ==> addData;; // Object\s+destArray ==> int[] destArray !! Auto-generated: NOT EDIT !! ]] void addData(long pos, int[] destArray, int destArrayOffset, int count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: addDataFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; int len = singleMapping ? count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = specBufs[0]; } finally { lock.unlock(); } addDataFromFirstBank(buf, ofs, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = specBufs[0]; } finally { lock.unlock(); } addDataFromFirstBank(buf, 0, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.IncludeEnd]] //[[Repeat(INCLUDE_FROM_FILE, THIS_FILE, mapped_getData_method_impl) // if\s*\(isBankLazyAndNotFilledYet.*?\{\s*\/\/EndOfLazy.*?((?:\r(?!\n)|\n|\r\n)\s*) ==> {$1;; // getData ==> addData;; // (,\s*int\s+count) ==> $1, double mult;; // (,\s*len|,\s*bse\))(?=\);) ==> $1, mult;; // Object\s+destArray ==> double[] destArray !! Auto-generated: NOT EDIT !! ]] void addData(long pos, double[] destArray, int destArrayOffset, int count, double mult) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: addDataFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; int len = singleMapping ? count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = specBufs[0]; } finally { lock.unlock(); } addDataFromFirstBank(buf, ofs, destArray, destArrayOffset, len, mult); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = specBufs[0]; } finally { lock.unlock(); } addDataFromFirstBank(buf, 0, destArray, destArrayOffset, len, mult); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.IncludeEnd]] //[[Repeat(INCLUDE_FROM_FILE, THIS_FILE, mapped_getData_method_impl) // if\s*\(isBankLazyAndNotFilledYet.*?\{\s*\/\/EndOfLazy.*?((?:\r(?!\n)|\n|\r\n)\s*) ==> {$1;; // getData ==> subtractData;; // (,\s*int\s+count) ==> $1, boolean truncateOverflows;; // (,\s*len|,\s*bse\))(?=\);) ==> $1, truncateOverflows !! Auto-generated: NOT EDIT !! ]] void subtractData(long pos, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: subtractDataFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; int len = singleMapping ? count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = specBufs[0]; } finally { lock.unlock(); } subtractDataFromFirstBank(buf, ofs, destArray, destArrayOffset, len, truncateOverflows); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = specBufs[0]; } finally { lock.unlock(); } subtractDataFromFirstBank(buf, 0, destArray, destArrayOffset, len, truncateOverflows); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.IncludeEnd]] //[[Repeat(INCLUDE_FROM_FILE, THIS_FILE, mapped_getData_method_impl) // if\s*\(isBankLazyAndNotFilledYet.*?\{\s*\/\/EndOfLazy.*?((?:\r(?!\n)|\n|\r\n)\s*) ==> {$1;; // getData ==> absDiffData;; // (,\s*int\s+count) ==> $1, boolean truncateOverflows;; // (,\s*len|,\s*bse\))(?=\);) ==> $1, truncateOverflows !! Auto-generated: NOT EDIT !! ]] void absDiffData(long pos, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: absDiffDataFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; int len = singleMapping ? count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = specBufs[0]; } finally { lock.unlock(); } absDiffDataFromFirstBank(buf, ofs, destArray, destArrayOffset, len, truncateOverflows); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { Buffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = specBufs[0]; } finally { lock.unlock(); } absDiffDataFromFirstBank(buf, 0, destArray, destArrayOffset, len, truncateOverflows); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.IncludeEnd]] @Override void attachArray(Array a) { lockForGc.lock(); try { arrayCounter++; } finally { lockForGc.unlock(); } if (finerLoggable) { LargeMemoryModel.LOGGER.finer("++++ Array " + Integer.toHexString(System.identityHashCode(a)) + " is attached to " + this + ": " + arrayCounter + " arrays, " + mappingCounter + " mappings" + " (" + finalizationTasksInfo() + ") [" + a + "]"); } } @Override void forgetArray(int arrayIdentityHashCode) { decreaseArrayOrMappingCounter(CounterKind.ARRAY_COUNTER); if (finerLoggable) { LargeMemoryModel.LOGGER.finer("---- Array " + Integer.toHexString(arrayIdentityHashCode) + " is deattached from " + this + ": " + arrayCounter + " arrays, " + mappingCounter + " mappings" + " (" + finalizationTasksInfo() + ")"); } } @Override void actualizeLazyFilling(ArrayContext context, long fromIndex, long toIndex) { if (fromIndex == toIndex) // necessary check: fromIndex can refer to the end of the file { return; } if (!ms.temporary) { return; // so, we can access lazyFillMapOfBanks below } final ReentrantLock lock = this.lock; final long bankFromIndex = fromIndex >> ms.bankSizeInElementsLog; final long bankToIndex = ((toIndex - 1) >>> ms.bankSizeInElementsLog) + 1; // - ">>>" here is used to avoid overflow final long bankAfterMapIndex = Math.min(bankToIndex, lazyFillMapOfBanks.length()); final long lazyFillBankIndex = Math.max(bankAfterMapIndex, // - to be on the safe side: needless in current implementation (lazyFillPosInBytes - ms.dataFileStartOffset) >> ms.bankSizeInBytesLog); final long totalCount = Math.max(0, bankAfterMapIndex - bankFromIndex) + Math.max(0, bankToIndex - lazyFillBankIndex); for (long k = bankFromIndex; k < bankAfterMapIndex; k++) { boolean filled = false; lock.lock(); try { if (lazyFillMapOfBanks.getBit(k)) { translateIndex(k << ms.bankSizeInElementsLog); // This access guarantees that all elements in this bank will be initialized. if (lazyFillMapOfBanks.getBit(k)) { throw new AssertionError("The lazyFillMapOfBanks[" + k + "] bit was not cleared!"); } filled = true; } } finally { lock.unlock(); } if (context != null && (filled || k == bankToIndex - 1)) { // call context not too often context.checkInterruptionAndUpdateProgress(ms.arrayElementClass, (k - bankFromIndex) << ms.bankSizeInElementsLog, totalCount << ms.bankSizeInElementsLog); // we don't use the number of one-bits in lazyFillMapOfBanks to estimate the percents, // because it may be changed a in parallel threads } } for (long k = lazyFillBankIndex; k < bankToIndex; k++) { lock.lock(); try { // After processing lazyFillMapOfBanks, it is better to actualize banks from the file start to end: // OS will fill the file faster than if we will not access to the end of file at the beginning. // Instead of this, we could access the last element at the beginning of this method, // but the first writing even one bank to the end of file may require a lot of time. translateIndex(k << ms.bankSizeInElementsLog); } finally { lock.unlock(); } if (context != null) { context.checkInterruptionAndUpdateProgress(ms.arrayElementClass, (Math.max(0, bankAfterMapIndex - bankFromIndex) + (k - lazyFillBankIndex)) << ms.bankSizeInElementsLog, totalCount << ms.bankSizeInElementsLog); } } } /** * This implementation just loads 1 element at the specified position fromIndex and * calls {@link DataFile.BufferHolder#load()} for the bank containing this element. * The toIndex argument is ignored by this method, excepting the only case * when it is equal to fromIndex (when this method does nothing). * * @param fromIndex start index (inclusive) in the stored AlgART array (not subarray). * @param toIndex end index (exclusive) in the stored AlgART array (not subarray). */ @Override void loadResources(long fromIndex, long toIndex) { if (fromIndex == toIndex) // necessary check: fromIndex can refer to the end of the file { return; } assert fromIndex < toIndex; lock.lock(); // there are no sense to preload data of the same file from several threads simultaneously try { if ((fromIndex & indexHighBits) == bankPos[0]) { assert this instanceof MappedBitStorage || fromIndex - bankPos[0] <= Integer.MAX_VALUE; } else { translateFailedIndex(fromIndex, false, LoadingMode.DEFAULT); } bh[0].load(); } finally { lock.unlock(); } } @Override void flushResources(long fromIndex, long toIndex, boolean forcePhysicalWriting) { assert fromIndex <= toIndex; long t1 = System.nanoTime(), t2 = t1; final ReentrantLock lock = this.lock; int count = 0; lock.lock(); try { checkIsDataFileDisposedOrShutdownInProgress(); if (forcePhysicalWriting) { dataFile.force(); // It's better to flush possible internal cache in dataFile BEFORE flushing bh: // then all buffers, in a case of DefaultDataFileModel, will probably written in a good order. } t2 = System.nanoTime(); for (int k = 0; k < bankIndexes.length; k++) { bankIndexes[k] = k; } ArraySorter.getQuickSorter().sortIndexes(bankIndexes, 0, bankIndexes.length, bankOrderComparator); // sorting by increasing file offset for (int k : bankIndexes) { if (bh[k] != null) { long fromByte = this instanceof MappedBitStorage ? fromIndex >>> 3 : fromIndex << bytesPerBufferElementLog(); long toByte = this instanceof MappedBitStorage ? (toIndex + 7) >>> 3 : toIndex << bytesPerBufferElementLog(); DataFile.Range r = bh[k].range(); if (toByte <= r.position() || fromByte >= r.position() + r.length()) { // bh[k] and the required region do not overlap continue; } bh[k].flush(forcePhysicalWriting); count++; } } } finally { lock.unlock(); } long t3 = System.nanoTime(); LargeMemoryModel.LOGGER.fine(this + " is successfully flushed (" + count + " banks, " + String.format(Locale.US, "%.3f", (t2 - t1) * 1e-6) + " ms flushing data file, " + String.format(Locale.US, "%.3f", (t3 - t2) * 1e-6) + " ms flushing active banks)"); } @Override void freeResources(Array a, boolean forcePhysicalWriting) { long t1 = System.nanoTime(); final ReentrantLock lock = this.lock; lock.lock(); try { checkIsDataFileDisposedOrShutdownInProgress(); if (forcePhysicalWriting) { dataFile.force(); // It's better to flush possible internal cache in dataFile BEFORE flushing bh: // then all buffers, in a case of DefaultDataFileModel, will probably written in a good order. } releaseFileAndMapping(ReleaseMode.DATA_MUST_STAY_ALIVE, forcePhysicalWriting); // unmap() calls in releaseFileAndMapping() performs // the same operations as in flushResources method } finally { lock.unlock(); } long t2 = System.nanoTime(); LargeMemoryModel.LOGGER.fine(this +" is successfully released (" + String.format(Locale.US, "%.3f", (t2 - t1) * 1e-6) + " ms)"); } @Override void dispose() { dispose(DisposeCaller.DISPOSE_METHOD); } final void incrementMappingInUseCounter() { if (DefaultDataFileModel.UNSAFE_UNMAP_ON_EXIT) { int counter = mappingInUseCounter.incrementAndGet(); if (counter < 0) // overflow { mappingInUseCounterOverflow = true; } } } final void decrementMappingInUseCounter() { if (DefaultDataFileModel.UNSAFE_UNMAP_ON_EXIT) { mappingInUseCounter.decrementAndGet(); } } final int mappingInUseCounter() { return mappingInUseCounterOverflow ? Integer.MAX_VALUE : mappingInUseCounter.get(); } private enum DisposeCaller { SHUTDOWN_HOOK("AlgART cleaner", LargeMemoryModel.DELETION_TIMEOUT_IN_CLEANER), DISPOSE_METHOD("dispose method", LargeMemoryModel.DELETION_TIMEOUT_IN_DISPOSE); private final String name; final int timeout; DisposeCaller(String name, int timeout) { this.name = name; this.timeout = timeout; } public String toString() { return name; } } private void dispose(DisposeCaller caller) { int timeout = caller.timeout; Object dfp = null; final ReentrantLock lock = this.lock; lock.lock(); try { releaseFileAndMapping(ReleaseMode.DATA_MAY_BE_LOST, false); DataFile df = this.dataFileForDeletion; this.deletedWhileShutdown = caller == DisposeCaller.SHUTDOWN_HOOK; // deletedWhileShutdown is a volatile field: all other threads will see the reason correctly this.dataFile = null; // dataFile is a volatile field: block any access this storage if (df == null) // the storage is already disposed { return; } dfp = ms.dataFileModel.getPath(df); if (autoDeleted) { if (df instanceof DefaultDataFileModel.MappableFile mdf && !(df instanceof StandardIODataFileModel.StandardIOFile)) // there are no ways to override DefaultDataFileModel.MappableFile outside this package { boolean unmapped = false; if ((caller == DisposeCaller.SHUTDOWN_HOOK ? DefaultDataFileModel.UNSAFE_UNMAP_ON_EXIT : DefaultDataFileModel.UNSAFE_UNMAP_ON_DISPOSE) && mdf.exists()) // not try to unmap already deleted files { if (mappingInUseCounter() != 0) { warningEvenInHook(caller + " cannot unmap array storage file " + df + " (some ByteBuffer instances are in use now)"); } else { try { long t1 = System.nanoTime(); unmapped = mdf.unsafeUnmapAll(); if (unmapped) { configEvenInHook( caller + " has unmapped (unsafe operation) all regions in " + df + " (" + String.format(Locale.US, "%.3f", (System.nanoTime() - t1) * 1e-6) + " ms)"); } else { warningEvenInHook(caller + " cannot unmap array storage file " + df + " (some arrays are not released yet)"); } } catch (Exception ex) { if (caller == DisposeCaller.SHUTDOWN_HOOK) { severeEvenInHook( caller + " cannot perform unmapping (unsafe operation) in " + df, ex); return; // - no any other actions, that could prevent normal shutdown } else { throw new InternalError("Error while calling undocumented " + "cleaning methods of DirectByteBuffer!", ex); } } } } if (caller == DisposeCaller.SHUTDOWN_HOOK && !unmapped) { timeout = 0; // if not using unsafe clean, timeout can extremely slow down JVM exit here } } try { if (deleteWithSeveralAttempts(ms.dataFileModel, df, timeout)) { configEvenInHook(caller + " has successfully deleted temporary array storage file " + df); } else { configEvenInHook(caller + " has not found temporary array storage file " + df); } } catch (Throwable e) { if (caller == DisposeCaller.SHUTDOWN_HOOK) { configEvenInHook(caller + " cannot delete temporary array storage file " + df + (caller.timeout > 0 ? " in " + caller.timeout + " ms" : "") + " (" + e + ")"); } Arrays.throwUncheckedException(e); } } // - the next operators will not be performed in a case of exception while releasing or file deletion this.disposeCalled = true; // block deletion attempts while finalization this.dataFileForDeletion = null; } finally { lock.unlock(); } // - the next operator will not be performed in a case of exception while releasing or file deletion if (caller == DisposeCaller.SHUTDOWN_HOOK && dfp != null) { ms.finalizationNotify(dfp, true); } } abstract void setSpecificBuffer(int bank); abstract void getDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count); abstract void setDataInFirstBank(Buffer buf, long firstBankOffset, Object srcArray, int srcArrayOffset, int count); abstract void fillDataInFirstBank(long destOffset, long count, Object fillerWrapper); abstract void copyFirstBank(MappedStorage src, long srcOffset, long destOffset, long count, boolean reverseOrder, boolean copyToSecondBank); abstract void swapFirstBank(MappedStorage another, long anotherOffset, long thisOffset, long count, boolean swapWithSecondBank); abstract void minDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count); abstract void maxDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count); abstract void addDataFromFirstBank(Buffer buf, long firstBankOffset, int[] destArray, int destArrayOffset, int count); abstract void addDataFromFirstBank(Buffer buf, long firstBankOffset, double[] destArray, int destArrayOffset, int count, double mult); abstract void subtractDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows); abstract void absDiffDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows); final boolean isBankLazyAndNotFilledYet(long index) { if (!(DO_LAZY_INIT && ms.temporary)) { return false; } index &= indexHighBits; final long mappingPosition = ms.dataFileStartOffset + (this instanceof MappedBitStorage ? index >>> 3 : index << bytesPerBufferElementLog()); if (mappingPosition >= lazyFillPosInBytes) { return true; } final long bankIndex = singleMapping ? 0 : index >> ms.bankSizeInElementsLog; // if ms.temporary, then always lazyFillMapOfBanks != null if (bankIndex < lazyFillMapOfBanks.length() && lazyFillMapOfBanks.getBit(bankIndex)) { return true; } return false; } void actualizeLazyFillingBank(ByteBuffer bankBB, long elementIndex) { // default implementation actualizeLazyZeroFillingBank(bankBB, 0, bankBB.limit()); } final void actualizeLazyZeroFillingBank(ByteBuffer bankByteBuffer, int from, int to) { assert from <= to; JBuffers.fillByteBuffer(bankByteBuffer, from, to - from, ZERO_INIT_FILLER); } private void getUninitializedLazyDataFromBank(long pos, Object destArray, int destArrayOffset, int count) { long length = ms.lazyFillingPattern == null ? 0 : ms.lazyFillingPattern.length(); if (count > length - pos) { int newCount = (int)(length < pos ? 0 : length - pos); // maybe count = 0, then newCount = 0 also JArrays.zeroFillArray(destArray, destArrayOffset + newCount, count - newCount); count = newCount; } if (count > 0) { // so, length > pos and ms.lazyFillingPattern != null ms.lazyFillingPattern.getData(pos, destArray, destArrayOffset, count); } } private void setSingleMappingModeIfNecessary() { if (this.unresizable && Math.min(dataFileLength, ms.dataFileEndOffset) <= ms.dataFileStartOffset + ms.dataFileModel.recommendedSingleMappingLimit()) { lock.lock(); try { this.indexHighBits = 0; // always match! this.singleMapping = true; // Preloading banks here is not a good idea: // they may be unloaded at any time by freeResources call, // so we still should check bh[0/1]==null in getXxx/setXxx methods if (!DefaultDataFileModel.UNSAFE_UNMAP_ON_EXIT) { this.syncNecessary = false; } } finally { lock.unlock(); } if (LargeMemoryModel.LOGGER.isLoggable(Level.FINE)) { LargeMemoryModel.LOGGER.fine("Single mapping mode is set for data file " + dataFile + " (its length " + dataFileLength + " or the specified end offset " + ms.dataFileEndOffset + " does not exceed the limit " + ms.dataFileModel.recommendedSingleMappingLimit() + (ms.dataFileStartOffset == 0 ? "" : " after start offset " + ms.dataFileStartOffset) + ")"); } } } private void changeCapacity( final long newCapacity, final long offset, final long length, final boolean unresizable) { assert newCapacity >= 0; assert offset >= 0; assert length >= 0; assert lock.isHeldByCurrentThread() : "changeCapacity is called from non-synchronized code"; if (this.unresizable) { throw new InternalError("Internal error in Buffer/LargeMemoryModel implementation " + "(unallowed changeCapacity)"); } final long requiredElements = offset + newCapacity; if (requiredElements < 0) { throw new TooLargeArrayException("Too large desired capacity: " + ((double)offset + (double)newCapacity) + " > 2^63-1"); } if (!(this instanceof MappedBitStorage) && requiredElements > (Long.MAX_VALUE >> bytesPerBufferElementLog())) { throw new TooLargeArrayException("Too large desired capacity: " + requiredElements + " > 2^" + (63 - bytesPerBufferElementLog()) + "-1"); } long requiredBytes = this instanceof MappedBitStorage ? (requiredElements + 7) >>> 3 : // requiredElements + 7 can be negative here! requiredElements << bytesPerBufferElementLog(); final long maxBytes = Long.MAX_VALUE - ms.dataFileStartOffset - ms.bankSizeInBytes + 1; // -2^31 < maxBytes < 2^63, 0 <= requiredBytes < 2^63 if (requiredBytes > maxBytes) { throw new TooLargeArrayException("Too large desired capacity in bytes: " + ms.dataFileStartOffset + " + " + requiredBytes + " > " + (Long.MAX_VALUE - ms.bankSizeInBytes + 1)); } // now Long.MAX_VALUE - ms.dataFileStartOffset >= maxBytes >= requiredBytes >= 0, // so, ms.dataFileStartOffset + requiredBytes is a correct long value if (unresizable) { requiredBytes = (requiredBytes + 31) & ~31; } else { requiredBytes = (requiredBytes + ms.bankSizeInBytes - 1) & ~(ms.bankSizeInBytes - 1); } final long newFileLength = ms.dataFileStartOffset + requiredBytes; final long oldFileLength = this.dataFileLength; boolean autoResizing = ms.dataFileModel.autoResizingOnMapping(); if (newFileLength > oldFileLength) { checkIsDataFileDisposedOrShutdownInProgress(); flushResources(0, Long.MAX_VALUE, false); // to be on the safe side dataFile.open(false); if (!autoResizing) { dataFile.length(newFileLength); } this.dataFileLength = newFileLength; } if (newFileLength > oldFileLength ? finerLoggable : finestLoggable) { LargeMemoryModel.LOGGER.log(newFileLength > oldFileLength ? Level.FINER : Level.FINEST, "Data file " + dataFile + (newFileLength <= oldFileLength ? " is not resized (current length is " + oldFileLength + "=0x" + Long.toHexString(oldFileLength) + " bytes, required is " + newFileLength + "=0x" + Long.toHexString(newFileLength) + " bytes)" : autoResizing ? " will be automatically resized from " + oldFileLength + "=0x" + Long.toHexString(oldFileLength) + " to " + newFileLength + "=0x" + Long.toHexString(newFileLength) + " bytes" : " is resized from " + oldFileLength + "=0x" + Long.toHexString(oldFileLength) + " to " + newFileLength + "=0x" + Long.toHexString(newFileLength) + " bytes") + " (desired capacity is " + newCapacity + ")"); } this.unresizable = unresizable; setSingleMappingModeIfNecessary(); } private void mapBank(final long position, final boolean loadIntoSecondBank, final LoadingMode loadingMode) { assert (position & ~indexHighBits) == 0 : "loadBank is called for not-aligned position " + position + " (mask " + indexHighBits + "=0x" + Long.toHexString(indexHighBits) + ")"; assert!singleMapping || position == 0; assert lock.isHeldByCurrentThread() : "mapBank is called from non-synchronized code"; final int bank = loadIntoSecondBank ? 1 : 0; long t1 = 0; if (finerLoggable) { t1 = System.nanoTime(); } checkIsDataFileDisposedOrShutdownInProgress(); dataFile.open(ms.readOnly); final long mappingPosition = ms.dataFileStartOffset + (this instanceof MappedBitStorage ? position >>> 3 : position << bytesPerBufferElementLog()); final long mappingSize = singleMapping ? Math.min(dataFileLength, ms.dataFileEndOffset) - ms.dataFileStartOffset : Math.min(ms.bankSizeInBytes, Math.min(dataFileLength, ms.dataFileEndOffset) - mappingPosition); assert mappingPosition >= 0 && mappingSize > 0 && mappingSize <= Integer.MAX_VALUE: "Illegal mappingPosition " + mappingPosition + " or mappingSize " + mappingSize + " (position = " + position + ", bankSizeInBytes = " + ms.bankSizeInBytes + ", dataFileLength = " + dataFileLength + ", dataFileStartOffset = " + ms.dataFileStartOffset + ", singleMapping = " + singleMapping + ")"; assert ((mappingPosition - ms.dataFileStartOffset) & (ms.bankSizeInBytes - 1)) == 0: "non-aligned mapping position " + mappingPosition + " (bank size in bytes is " + ms.bankSizeInBytes + "=0x" + Long.toHexString(ms.bankSizeInBytes) + ")"; if (ms.temporary) { assert ((lazyFillPosInBytes - ms.dataFileStartOffset) & (ms.bankSizeInBytes - 1)) == 0: "non-aligned lazyFillPosInBytes-ms.dataFileStartOffset=" + (lazyFillPosInBytes - ms.dataFileStartOffset) + " (bank size in bytes is " + ms.bankSizeInBytes + "=0x" + Long.toHexString(ms.bankSizeInBytes) + ")"; assert ms.dataFileStartOffset + (lazyFillMapOfBanks.length() << ms.bankSizeInBytesLog) <= lazyFillPosInBytes: "too large lazyFillMapOfBanks: " + lazyFillMapOfBanks.length() + " bits" + " (bank size in bytes is " + ms.bankSizeInBytes + "=0x" + Long.toHexString(ms.bankSizeInBytes) + ")"; } final long thisBankIndex; if (DO_LAZY_INIT && ms.temporary && !singleMapping) { thisBankIndex = (mappingPosition - ms.dataFileStartOffset) >> ms.bankSizeInBytesLog; } else { thisBankIndex = 0; } if (DO_LAZY_INIT && ms.temporary && lazyFillPosInBytes < mappingPosition) { t1 = System.nanoTime(); assert !singleMapping : "illegal singleMapping with low lazyFillPosInBytes=" + lazyFillPosInBytes; // - when singleMapping, position=0 always: see asserts above final long needfulFillMapSize = thisBankIndex; assert lazyFillMapOfBanks.length() < needfulFillMapSize: "illegal lazyFillMapOfBanks.length()=" + lazyFillMapOfBanks.length(); // - because lazyFillPosInBytes < mappingPosition: see the assert above final long newFillMapSize = Math.min(needfulFillMapSize, LargeMemoryModel.MAX_NUMBER_OF_BANKS_IN_LAZY_FILL_MAP); lazyFillMapOfBanks.length(newFillMapSize); final long lazyFillBankIndex = (lazyFillPosInBytes - ms.dataFileStartOffset) >> ms.bankSizeInBytesLog; if (lazyFillBankIndex < newFillMapSize) { // System.err.println("Request for filling " + lazyFillBankIndex + ".." + newFillMapSize); lazyFillMapOfBanks.subArray(lazyFillBankIndex, newFillMapSize).fill(true); // request for future lazy filling } lazyFillPosInBytes = Math.max(lazyFillPosInBytes, ms.dataFileStartOffset + (newFillMapSize << ms.bankSizeInBytesLog)); final long lazyFillPos = this instanceof MappedBitStorage ? (lazyFillPosInBytes - ms.dataFileStartOffset) << 3 : (lazyFillPosInBytes - ms.dataFileStartOffset) >> bytesPerBufferElementLog(); assert lazyFillPos >= 0: "negative array index " + lazyFillPos + ", corresponding to lazy fill position " + lazyFillPosInBytes; for (long p = lazyFillPos, mp = lazyFillPosInBytes; mp < mappingPosition; p += ms.bankSizeInElements, mp += ms.bankSizeInBytes) // direct filling: cannot do it in future due to too small lazyFillMapOfBanks { // System.err.println("Filling " + p + "/" + mp); assert newFillMapSize < needfulFillMapSize : "extra lazy filling loop"; assert p < position : "p>=position"; // System.out.printf("\r%d: %d / %d", mp/ms.bankSizeInBytes, mp/1048576, mappingPosition/1048576); DataFile.BufferHolder tempBh = dataFile.map(DataFile.Range.valueOf(mp, ms.bankSizeInBytes), true); // this bank was never loaded before; so, we can ignore standard translateFailedIndex logic scheduleFinalizationForMapping(tempBh); actualizeLazyFillingBank(tempBh.data(), p); // p is passed only for a case of filling by the pattern array, // to inform where the necessary fragment of the pattern begins tempBh.unmap(false); checkIsDataFileDisposedOrShutdownInProgress(); // we need to leave long synchronized block } long t2 = System.nanoTime(); if (finerLoggable) { LargeMemoryModel.LOGGER.finer( "**** The region #" + Long.toHexString(lazyFillPosInBytes) + "h.." + Long.toHexString(mappingPosition) + "h of " + dataFilePath + "is cleared (" + String.format(Locale.US, "%.3f", (t2 - t1) * 1e-6) + " ms)" + " (" + finalizationTasksInfo() + ")"); } t1 = t2; } boolean lazyFillBit = false; final boolean thisBankMustBeFilled = DO_LAZY_INIT && ms.temporary && (mappingPosition >= lazyFillPosInBytes || (lazyFillBit = thisBankIndex < lazyFillMapOfBanks.length() && lazyFillMapOfBanks.getBit(thisBankIndex))); // if ms.temporary, then always lazyFillMapOfBanks != null bh[bank] = dataFile.map( DataFile.Range.valueOf(mappingPosition, mappingSize), loadingMode == LoadingMode.NOT_LOAD_DATA_FROM_FILE || thisBankMustBeFilled); if (loadingMode == LoadingMode.PRELOAD_DATA_FROM_FILE) { bh[bank].load(); } if (thisBankMustBeFilled) { // System.err.println("Filling " + position); actualizeLazyFillingBank(bh[bank].data(), position); if (lazyFillBit) { lazyFillMapOfBanks.clearBit(thisBankIndex); } } lazyFillPosInBytes = Math.max(lazyFillPosInBytes, mappingPosition + ms.bankSizeInBytes); bankPos[bank] = position; setSpecificBuffer(bank); if (!syncNecessary) { assert singleMapping; // in this case, only two banks 0/1 are used and bh[0]/bh[1] are always set to the same value validSingleSpecBufForThisThread = specBufs[bank]; assert validSingleSpecBufForThisThread != null; } final boolean fromCache = bh[bank].isLoadedFromCache(); final boolean loggable = fromCache ? finestLoggable : finerLoggable; final int id = System.identityHashCode(bh[bank].mappingObject()); if (loggable) { long t2 = System.nanoTime(); LargeMemoryModel.LOGGER.log(fromCache ? Level.FINEST : Level.FINER, "**** The bank #" + bank + " is mapped" + (fromCache ? " (quickly): " + bh[bank] : ": " + bh[bank] + " @" + Integer.toHexString(id)) + " (" + String.format(Locale.US, "%.3f", (t2 - t1) * 1e-6) + " ms)" + " (" + finalizationTasksInfo() + ")"); } AssertionError ae = logAndCheckBanks(position, loadIntoSecondBank, LogAllBanksCallPlace.loadBank, fromCache ? Level.FINEST : Level.FINER); if (ae != null) { throw ae; } scheduleFinalizationForMapping(bh[bank]); // the following call is necessary when bh[bank].mappingObject() is null, as in StandardIODataFileModel scheduleFinalizationForAllStorage(); } private enum ReleaseMode {DATA_MUST_STAY_ALIVE, DATA_MAY_BE_LOST} // Returns false if bh[bank] is empty (null) private boolean unmapBank(int bank, ReleaseMode mode, boolean forcePhysicalWriting) { // may be called from non-synchronized code: finalizing by decreaseArrayOrMappingCounter DataFile.BufferHolder tempBh = bh[bank]; if (tempBh != null) { long t1 = 0; if (finerLoggable) { t1 = System.nanoTime(); } if (mode == ReleaseMode.DATA_MUST_STAY_ALIVE) { tempBh.unmap(forcePhysicalWriting); } else { tempBh.dispose(); } bh[bank] = null; // allows garbage collector to finalize this buffer specBufs[bank] = null; // necessary for quick brange in getXxx/setXxx if (finerLoggable) { long t2 = System.nanoTime(); LargeMemoryModel.LOGGER.finer( "**** The bank #" + bank + ": " + tempBh + " is " + (mode == ReleaseMode.DATA_MUST_STAY_ALIVE ? "unmapped (" : "disposed (") + String.format(Locale.US, "%.3f", (t2 - t1) * 1e-6) + " ms)"); } return true; } else { return false; } } private void scheduleFinalizationForMapping(DataFile.BufferHolder bufferHolder) { Object checkedData = bufferHolder.mappingObject(); if (checkedData != null) { boolean loggable = bufferHolder.isLoadedFromCache() ? finestLoggable : finerLoggable; final String bhToString = loggable ? bufferHolder.toString() : null; final int id = System.identityHashCode(checkedData); lockForGc.lock(); try { mappingCounter++; } finally { lockForGc.unlock(); } LargeMemoryModel.globalMappingFinalizer.invokeOnDeallocation(checkedData, () -> { if (bhToString != null) { // i.e. if loggable LargeMemoryModel.LOGGER.finer("~~~~ " + bhToString + " @" + Integer.toHexString(id) + " is deallocated" + " (" + finalizationTasksInfo() + ")"); } decreaseArrayOrMappingCounter(CounterKind.MAPPING_COUNTER); }); } } private void scheduleFinalizationForAllStorage() { if (finalizationHolder == null) { finalizationHolder = new Object(); lockForGc.lock(); try { mappingCounter++; } finally { lockForGc.unlock(); } LargeMemoryModel.LOGGER.fine("FF++ " + this + ": finalization is scheduled" + " (" + finalizationTasksInfo() + ")"); LargeMemoryModel.globalStorageFinalizer.invokeOnDeallocation(finalizationHolder, new Runnable() { public void run() { LargeMemoryModel.LOGGER.fine("FF-- " + this + " is released" + " (" + finalizationTasksInfo() + ")"); decreaseArrayOrMappingCounter(CounterKind.MAPPING_COUNTER); } }); } } private enum LogAllBanksCallPlace { translateFailedIndex, translateFailedIndexSame, translateFailedIndexHole, loadBank } private AssertionError logAndCheckBanks(long index, boolean useSecondBank, LogAllBanksCallPlace whereCalled, Level level) { String msg = null; errorCheck: { if (bankCount < 0 || bankCount > bh.length) { msg = "invalid bankCount = " + bankCount; break errorCheck; } for (int k = 1; k < bankCount; k++) { // the bank #0, but only it, can ne empty when bankCount>0 if (bh[k] == null) { msg = "bh[" + k + "] is null, but " + (bankPos[k] != -1 ? "bankPos[" + k + "] != -1" : "bankCount is " + bankCount); break errorCheck; } if (bankPos[k] == -1) { msg = "bankPos[" + k + "] is -1, but " + (bh[k] != null ? "bh[" + k + "] != null" : "bankCount is " + bankCount); break errorCheck; } } for (int k = bankCount; k < bh.length; k++) { if (bh[k] != null) { msg = "bh[" + k + "] is not null, but bankCount is " + bankCount; break errorCheck; } if (bankPos[k] != -1) { msg = "bankPos[" + k + "] is not -1, but bankCount is " + bankCount; break errorCheck; } } for (int k = 0; k < bankCount; k++) { if (k >= 2 && bankPos[k] != -1) { for (int j = 0; j < k; j++) { if (bankPos[k] == bankPos[j]) { msg = "banks #" + j + " and #" + k + " are duplicates"; break errorCheck; } } } } } AssertionError result = null; if (msg != null) { result = new AssertionError("Internal error found in " + whereCalled + ": " + msg); level = Level.SEVERE; } if (LargeMemoryModel.LOGGER.isLoggable(level)) { StringBuilder sb = new StringBuilder(bankCount + " banks, accessing index " + Long.toHexString(index) + "h"); for (int k = 0; k < (result == null ? bankCount : bh.length); k++) { sb.append(InternalUtils.LF).append(" [").append(whereCalled) .append("] mapping ").append(useSecondBank ? "(at second bank) " : "") .append(k).append(": ").append(bh[k]); } sb.append(InternalUtils.LF).append("The reason:"); StackTraceElement[] se = Thread.currentThread().getStackTrace(); for (int k = 2; k < se.length; k++) { sb.append(InternalUtils.LF).append(" ").append(se[k]); } if (result == null) { LargeMemoryModel.LOGGER.log(level, sb.toString()); } else { LargeMemoryModel.LOGGER.log(level, sb.toString(), result); } } return result; } private void releaseFileAndMapping(ReleaseMode mode, boolean forcePhysicalWriting) { assert lock.isHeldByCurrentThread() : "releaseFileAndMapping is called from non-synchronized code"; // Here mode can be equal to DATA_MAY_BE_LOST only in finalization/cleaner, // that always means that the file is temporary, // or as a result of direct dispose() call, // that does not need to write something into the file, because it will be deleted now long t1 = System.nanoTime(); int count = 0; int bStart = 0; if (!syncNecessary) { validSingleSpecBufForThisThread = null; } if (bankPos[0] == bankPos[1]) { bankPos[0] = -1; bh[0] = null; specBufs[0] = null; bStart++; } for (int k = 0; k < bankIndexes.length; k++) { bankIndexes[k] = k; } ArraySorter.getQuickSorter().sortIndexes(bankIndexes, bStart, bankIndexes.length, bankOrderComparator); // sorting by increasing file offset for (int b = bStart; b < bh.length; b++) { int k = bankIndexes[b]; assert k >= bStart && k < bh.length; bankPos[k] = -1; if (unmapBank(k, mode, forcePhysicalWriting)) { count++; } } finalizationHolder = null; bankCount = 0; long t2 = System.nanoTime(); if (count > 0 && finerLoggable) { LargeMemoryModel.LOGGER.finer("All buffers are released (" + count + " banks, " + String.format(Locale.US, "%.3f", (t2 - t1) * 1e-6) + " ms)"); } DataFile df = this.dataFile; if (df != null) { // can be null, if dispose() was called df.close(); if (finerLoggable) { LargeMemoryModel.LOGGER.finer("Data file " + df + " is closed"); } } } private void checkIsDataFileDisposedOrShutdownInProgress() { if (shutdownInProgress) { throw new IOError(new IllegalStateException( "AlgART array @<" + this + "> is inaccessible: system shutdown in progress")); } if (dataFile == null) // for a case when the file was disposed via dispose() method { throw new IOError(new IllegalStateException( "AlgART array @<" + this + "> is inaccessible: the storage was already disposed and deleted")); } } private enum CounterKind {ARRAY_COUNTER, MAPPING_COUNTER} private void decreaseArrayOrMappingCounter(CounterKind counterKind) { DataFile df = null; Object dfp = null; boolean needToClearBanks = false; boolean needToDeleteFile = false; boolean needToForgetStorage = false; lockForGc.lock(); try { if (counterKind == CounterKind.ARRAY_COUNTER) { arrayCounter--; } if (arrayCounter < 0) { throw new AssertionError("Negative number of attached arrays!"); } if (counterKind == CounterKind.MAPPING_COUNTER) { mappingCounter--; } if (mappingCounter < 0) { throw new AssertionError("Negative number of mappings!"); } if (counterKind == CounterKind.ARRAY_COUNTER && arrayCounter == 0 && mappingCounter > 0) { needToClearBanks = true; } if (arrayCounter == 0 && mappingCounter == 0) { df = this.dataFileForDeletion; dfp = this.dataFilePath; // can be already null, if dispose() was called and successfully performed this.dataFile = null; needToDeleteFile = autoDeleted && df != null; needToForgetStorage = true; } if (finerLoggable) { LargeMemoryModel.LOGGER.finer("~~-- Decreasing " + (counterKind == CounterKind.ARRAY_COUNTER ? "array" : "mapping") + " counter: " + arrayCounter + " arrays, " + mappingCounter + " mappings" + " (" + finalizationTasksInfo() + ")" + (needToDeleteFile ? "; file " + df + " should be deleted" : "")); } } finally { lockForGc.unlock(); } try { if (needToClearBanks) { final ReentrantLock lock = this.lock; lock.lock(); // Synchronization is necessary: in another case, freeResources method will be able // to close the unmapping buffers here, that can lead to error in some data file models try { releaseFileAndMapping(ReleaseMode.DATA_MAY_BE_LOST, false); } finally { lock.unlock(); } } } catch (Throwable ex) { LargeMemoryModel.LOGGER.log(Level.SEVERE, "Cannot release array file/mapping resources", ex); return; } if (needToDeleteFile) { final ReentrantLock lock = this.lock; lock.lock(); try { if (!disposeCalled) { // always set and check it inside the synchronized block final int timeout = LargeMemoryModel.DELETION_TIMEOUT_IN_FINALIZATION; try { if (deleteWithSeveralAttempts(ms.dataFileModel, df, timeout)) { LargeMemoryModel.LOGGER.fine("Finalization: temporary array storage file " + df + " is successfully deleted" + (countOfFailedDeletionsWhileFinalization > 0 ? " (" + countOfFailedDeletionsWhileFinalization + " attempts)" : "")); } else { LargeMemoryModel.LOGGER.finer("Finalization: no temporary array storage file " + df); } } catch (Throwable ex) { String msg = "Finalization: cannot delete temporary array storage file " + df + (timeout > 0 ? " in " + timeout + " ms" : ""); if (ex instanceof IOError) { if (++countOfFailedDeletionsWhileFinalization >= LargeMemoryModel.MAX_NUMBER_OF_DELETIONS_WHILE_FINALIZATION) { warningEvenInHook(msg + " (" + ex + "). It was the attempt #" + countOfFailedDeletionsWhileFinalization + ": deletion of this file by the finalizer is canceled!"); } else { LargeMemoryModel.LOGGER.fine(msg + ". Deletion is scheduled on the next garbage collector execution. " + "(" + ex + ")"); scheduleFinalizationForAllStorage(); // - we shall try to remove it at the next gc finalizationHolder = null; // - immediately allow gc for this storage dfp = null; // - cancel finalization notification in this GC pass } } else { LargeMemoryModel.LOGGER.log(Level.SEVERE, msg + " (Unexpected exception!)", ex); } this.deletionErrorWhileFinalization = true; return; } // - the next operator will not be performed in a case of exception while releasing or file deletion this.dataFileForDeletion = null; } } finally { lock.unlock(); } } if (needToForgetStorage) { // - the next operator will not be performed in a case of exception while releasing or file deletion allNonFinalizedMappedStorages.remove(this); if (dfp != null) { ms.finalizationNotify(dfp, false); } } } } static class MappedBitStorage extends MappedStorage implements DataBitStorage { private final LongBuffer[] lb; private final DataBitBuffer dbuf; MappedBitStorage(MappingSettings ms) { super(ms); this.lb = (LongBuffer[])this.specBufs; this.dbuf = ms.lazyFillingPattern == null ? null : (DataBitBuffer)Arrays.bufferInternal(ms.lazyFillingPattern, DataBuffer.AccessMode.READ); } DataStorage newCompatibleEmptyStorage(boolean unresizable) { return new MappedBitStorage(ms.getCompatibleInstanceForNewTemporaryFile(unresizable)); } int bytesPerBufferElementLog() { return BYTES_PER_LONG_LOG; } @Override final boolean getBit(long index) { final ReentrantLock lock = this.lock; LongBuffer lb0 = lb[0]; final boolean sync = this.syncNecessary || !(lb0 == validSingleSpecBufForThisThread && lb0 != null); // Without synchronization, we may use lb0 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. if (sync) { lock.lock(); } try { long i; if (sync) { if ((index & indexHighBits) == bankPos[0]) { // assert this instanceof MappedBitStorage || index - bankPos[0] <= Integer.MAX_VALUE; i = index - bankPos[0]; } else { i = translateFailedIndex(index, false, LoadingMode.DEFAULT); } lb0 = lb[0]; } else { i = index; } int ii = (int)(i >>> 6), bit = ((int)i) & 63; return (lb0.get(ii) & (1L << bit)) != 0L; } finally { if (sync) { lock.unlock(); } } } @Override final void setBit(long index, boolean value) { final ReentrantLock lock = this.lock; LongBuffer lb1 = lb[1]; final boolean sync = this.syncNecessary || !(lb1 == validSingleSpecBufForThisThread && lb1 != null); // Without synchronization, we may use lb1 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. if (sync) { lock.lock(); } try { long i; if (sync) { if ((index & indexHighBits) == bankPos[1]) { // assert this instanceof MappedBitStorage || index - bankPos[1] <= Integer.MAX_VALUE; i = index - bankPos[1]; } else { i = translateFailedIndex(index, true, LoadingMode.DEFAULT); } lb1 = lb[1]; } else { i = index; } int ii = (int)(i >>> 6), bit = ((int)i) & 63; synchronized(lb1) { if (value) { lb1.put(ii, lb1.get(ii) | 1L << bit); } else { lb1.put(ii, lb1.get(ii) & ~(1L << bit)); } } } finally { if (sync) { lock.unlock(); } } } long getBits64(long arrayPos, int count) { long[] array = new long[1]; getBits(arrayPos, array, 0, count); return array[0]; } void setBits64(long arrayPos, long bits, int count) { long[] array = new long[1]; array[0] = bits; setBits(arrayPos, array, 0, count); } // No special implementation for setBitNoSync and setBits64NoSync: synchronization is needed in any case. @Override final long indexOfBit(long lowIndex, long highIndex, boolean value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final long ofs = offset(lowIndex); assert ofs >= 0; long count = highIndex - lowIndex; final long bse = ms.bankSizeInElements; long len = singleMapping ? count : Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == ofs; buf = lb[0]; } finally { lock.unlock(); } long result = PackedBitBuffers.indexOfBit(buf, ofs, ofs + len, value); if (result != -1) { return result + lowIndex - ofs; } } finally { decrementMappingInUseCounter(); } } lowIndex += len; count -= len; for (; count > 0; lowIndex += bse, count -= bse) { len = Math.min(count, bse); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == 0; buf = lb[0]; } finally { lock.unlock(); } long result = PackedBitBuffers.indexOfBit(buf, 0, len, value); if (result != -1) { return result + lowIndex; } } finally { decrementMappingInUseCounter(); } } } return -1; } @Override final long lastIndexOfBit(long lowIndex, long highIndex, boolean value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final long ofs = offset(highIndex - 1); assert ofs >= 0; long count = highIndex - lowIndex; final long bse = ms.bankSizeInElements; long len = singleMapping ? count : Math.min(count, ofs + 1); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == ofs; buf = lb[0]; } finally { lock.unlock(); } long result = PackedBitBuffers.lastIndexOfBit(buf, ofs - len + 1, ofs + 1, value); if (result != -1) { return result + highIndex - 1 - ofs; } } finally { decrementMappingInUseCounter(); } } highIndex -= len; count -= len; for (; count > 0; highIndex -= bse, count -= bse) { len = Math.min(count, bse); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == bse - 1; buf = lb[0]; } finally { lock.unlock(); } long result = PackedBitBuffers.lastIndexOfBit(buf, bse - len, bse, value); if (result != -1) { return result + highIndex - bse; } } finally { decrementMappingInUseCounter(); } } } return -1; } //[[Repeat.SectionStart mapped_getBits_method_impl]] public void getBits(long pos, long[] destArray, long destArrayOffset, long count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: getBitsFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; long len = singleMapping ? count : Math.min(count, bse - ofs); if (isBankLazyAndNotFilledYet(pos)) { getUninitializedLazyBitsFromBank(pos, destArray, destArrayOffset, len); } else { //EndOfLazy !! this comment is necessary for preprocessing by Repeater !! incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = lb[0]; } finally { lock.unlock(); } getBitsFromFirstBank(buf, ofs, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = Math.min(count, bse); if (isBankLazyAndNotFilledYet(pos)) { getUninitializedLazyBitsFromBank(pos, destArray, destArrayOffset, len); } else { //EndOfLazy !! this comment is necessary for preprocessing by Repeater !! incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = lb[0]; } finally { lock.unlock(); } getBitsFromFirstBank(buf, 0, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.SectionEnd mapped_getBits_method_impl]] public void setBits(long pos, long[] srcArray, long srcArrayOffset, long count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: setBitsInFirstBank access the specBuf, // that can become null at any moment final long bse = ms.bankSizeInElements; long ofs = offset(pos); assert ofs >= 0; long len = singleMapping ? count : Math.min(count, bse - ofs); if (singleMapping || ofs > 0) { // in another case, the loop can perform all tasks incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long o = translateIndex(pos); assert o == ofs; buf = lb[0]; } finally { lock.unlock(); } setBitsInFirstBank(buf, ofs, srcArray, srcArrayOffset, len); } finally { decrementMappingInUseCounter(); } srcArrayOffset += len; pos += len; count -= len; } for (; count > 0; pos += bse, srcArrayOffset += bse, count -= bse) { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { if (count >= bse) { len = (int)bse; ofs = translateIndexWO(pos); } else { len = count; ofs = translateIndex(pos); } assert ofs == 0; buf = lb[0]; } finally { lock.unlock(); } setBitsInFirstBank(buf, 0, srcArray, srcArrayOffset, Math.min(count, bse)); } finally { decrementMappingInUseCounter(); } } } //[[Repeat(INCLUDE_FROM_FILE, THIS_FILE, mapped_getBits_method_impl) // if\s*\(isBankLazyAndNotFilledYet.*?\{\s*\/\/EndOfLazy.*?((?:\r(?!\n)|\n|\r\n)\s*) ==> {$1;; // getBits ==> andBits !! Auto-generated: NOT EDIT !! ]] public void andBits(long pos, long[] destArray, long destArrayOffset, long count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: andBitsFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; long len = singleMapping ? count : Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = lb[0]; } finally { lock.unlock(); } andBitsFromFirstBank(buf, ofs, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = Math.min(count, bse); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = lb[0]; } finally { lock.unlock(); } andBitsFromFirstBank(buf, 0, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.IncludeEnd]] //[[Repeat(INCLUDE_FROM_FILE, THIS_FILE, mapped_getBits_method_impl) // if\s*\(isBankLazyAndNotFilledYet.*?\{\s*\/\/EndOfLazy.*?((?:\r(?!\n)|\n|\r\n)\s*) ==> {$1;; // getBits ==> orBits !! Auto-generated: NOT EDIT !! ]] public void orBits(long pos, long[] destArray, long destArrayOffset, long count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: orBitsFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; long len = singleMapping ? count : Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = lb[0]; } finally { lock.unlock(); } orBitsFromFirstBank(buf, ofs, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = Math.min(count, bse); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = lb[0]; } finally { lock.unlock(); } orBitsFromFirstBank(buf, 0, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.IncludeEnd]] //[[Repeat(INCLUDE_FROM_FILE, THIS_FILE, mapped_getBits_method_impl) // if\s*\(isBankLazyAndNotFilledYet.*?\{\s*\/\/EndOfLazy.*?((?:\r(?!\n)|\n|\r\n)\s*) ==> {$1;; // getBits ==> xorBits !! Auto-generated: NOT EDIT !! ]] public void xorBits(long pos, long[] destArray, long destArrayOffset, long count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: xorBitsFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; long len = singleMapping ? count : Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = lb[0]; } finally { lock.unlock(); } xorBitsFromFirstBank(buf, ofs, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = Math.min(count, bse); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = lb[0]; } finally { lock.unlock(); } xorBitsFromFirstBank(buf, 0, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.IncludeEnd]] //[[Repeat(INCLUDE_FROM_FILE, THIS_FILE, mapped_getBits_method_impl) // if\s*\(isBankLazyAndNotFilledYet.*?\{\s*\/\/EndOfLazy.*?((?:\r(?!\n)|\n|\r\n)\s*) ==> {$1;; // getBits ==> andNotBits !! Auto-generated: NOT EDIT !! ]] public void andNotBits(long pos, long[] destArray, long destArrayOffset, long count) { if (count == 0) // necessary check: our swapping algorithm does not work with zero-length file { return; } final ReentrantLock lock = this.lock; // synchronization is necessary always: andNotBitsFromFirstBank access the specBuf, // that can become null at any moment long ofs = offset(pos); assert ofs >= 0; final long bse = ms.bankSizeInElements; long len = singleMapping ? count : Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == ofs; buf = lb[0]; } finally { lock.unlock(); } andNotBitsFromFirstBank(buf, ofs, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } destArrayOffset += len; pos += len; count -= len; for (; count > 0; pos += bse, destArrayOffset += bse, count -= bse) { len = Math.min(count, bse); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(pos); assert translateIndexResult == 0; buf = lb[0]; } finally { lock.unlock(); } andNotBitsFromFirstBank(buf, 0, destArray, destArrayOffset, len); } finally { decrementMappingInUseCounter(); } } } } //[[Repeat.IncludeEnd]] final void copy(long destIndex, long srcIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { setBit(destIndex, getBit(srcIndex)); } finally { lock.unlock(); } } final void swap(long firstIndex, long secondIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { long i1 = translateIndex(firstIndex); assert i1 >= 0; int ii1 = (int)(i1 >>> 6), bit1 = ((int)i1) & 63; long i2 = translateIndex(secondIndex, true); assert i2 >= 0; int ii2 = (int)(i2 >>> 6), bit2 = ((int)i2) & 63; synchronized(lb) { long l1 = lb[0].get(ii1); long l2 = lb[1].get(ii2); boolean v1 = (l1 & (1L << bit1)) != 0L; boolean v2 = (l2 & (1L << bit2)) != 0L; if (v1 != v2) { if (v2) { lb[0].put(ii1, l1 | 1L << bit1); } else { lb[0].put(ii1, l1 & ~(1L << bit1)); } l2 = lb[1].get(ii2); // for swapping 2 bits in the same long if (v1) { lb[1].put(ii2, l2 | 1L << bit2); } else { lb[1].put(ii2, l2 & ~(1L << bit2)); } } } } finally { lock.unlock(); } } void setSpecificBuffer(int bank) { lb[bank] = bh[bank] == null ? null : bh[bank].data().asLongBuffer(); } void clearData(long pos, long count) { fillData(pos, count, booleanZero); } void getDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { PackedBitBuffers.unpackBits((boolean[])destArray, destArrayOffset, (LongBuffer)buf, firstBankOffset, count); } void setDataInFirstBank(Buffer buf, long firstBankOffset, Object srcArray, int srcArrayOffset, int count) { PackedBitBuffers.packBits((LongBuffer)buf, firstBankOffset, (boolean[])srcArray, srcArrayOffset, count); // for (int k = 0; k < count; k++) { // Java 1.7 bug here! // if (PackedBitBuffers.getBit((LongBuffer)buf, firstBankOffset + k) // != ((boolean[])srcArray)[srcArrayOffset + k]) { // StringBuilder sb1 = new StringBuilder(), sb2 = new StringBuilder(); // for (int j = 0; j < count; j++) { // if (j == k) { // sb1.append(" "); // sb2.append(" "); // } // sb1.append(PackedBitBuffers.getBit((LongBuffer)buf, firstBankOffset + j) ? "1" : "0"); // sb2.append(((boolean[])srcArray)[srcArrayOffset + j] ? "1" : "0"); // } // throw new AssertionError("k=" + k + ", count=" + count + ", srcArrayOffset=" + srcArrayOffset // + ", firstBankOffset=" + firstBankOffset + ", srcArray[" // + ((boolean[])srcArray).length + "]," + (LongBuffer)buf + "\n" + sb1 + "\n" + sb2); // } // } } void fillDataInFirstBank(long destOffset, long count, Object fillerWrapper) { PackedBitBuffers.fillBits(lb[0], destOffset, count, ((Boolean)fillerWrapper).booleanValue()); } void copyFirstBank(MappedStorage src, long srcOffset, long destOffset, long count, boolean reverseOrder, boolean copyToSecondBank) { PackedBitBuffers.copyBits(lb[copyToSecondBank ? 1 : 0], destOffset, ((MappedBitStorage)src).lb[0], srcOffset, count, reverseOrder); } void swapFirstBank(MappedStorage another, long anotherOffset, long thisOffset, long count, boolean swapWithSecondBank) { PackedBitBuffers.swapBits(((MappedBitStorage)another).lb[0], anotherOffset, lb[swapWithSecondBank ? 1 : 0], thisOffset, count); } void minDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { throw new UnsupportedOperationException("minDataFromFirstBank is not supported for bit storages"); } void maxDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { throw new UnsupportedOperationException("maxDataFromFirstBank is not supported for bit storages"); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, int[] destArray, int destArrayOffset, int count) { throw new UnsupportedOperationException("addDataFromFirstBank is not supported for bit storages"); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, double[] destArray, int destArrayOffset, int count, double mult) { throw new UnsupportedOperationException("addDataFromFirstBank is not supported for bit storages"); } void subtractDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { throw new UnsupportedOperationException("subtractDataFromFirstBank is not supported for bit storages"); } void absDiffDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { throw new UnsupportedOperationException("absDiffDataFromFirstBank is not supported for bit storages"); } @Override void actualizeLazyFillingBank(ByteBuffer bankBB, final long elementIndex) { assert lock.isHeldByCurrentThread() : "actualizeLazyFillingBank is called from non-synchronized code"; assert (elementIndex & 63) == 0; // index is specified in elements and is even enough long length; if (ms.lazyFillingPattern == null || (length = ms.lazyFillingPattern.length()) <= elementIndex) { actualizeLazyZeroFillingBank(bankBB, 0, bankBB.limit()); } else { LongBuffer bankSpecBuf = bankBB.asLongBuffer(); long count = ((long)bankSpecBuf.limit()) << 6; if (count > length - elementIndex) { count = length - elementIndex; actualizeLazyZeroFillingBank(bankBB, (int)(count >> 3), bankBB.limit()); // if necessary, here we fill several extra bits, // corresponding to the last byte of ms.lazyFillingPattern } for (long ofs = 0; ofs < count; ) { dbuf.map(elementIndex + ofs, count - ofs); long len = dbuf.count(); assert len > 0; PackedBitBuffers.copyBits(bankSpecBuf, ofs, LongBuffer.wrap(dbuf.data()), dbuf.from(), len); ofs += len; } } } private void getUninitializedLazyBitsFromBank(long pos, long[] destArray, long destArrayOffset, long count) { long length = ms.lazyFillingPattern == null ? 0 : ms.lazyFillingPattern.length(); if (count > length - pos) { long newCount = length < pos ? 0 : length - pos; PackedBitArrays.fillBits(destArray, destArrayOffset + newCount, count - newCount, false); count = newCount; } if (count > 0) { // so, length > pos and ms.lazyFillingPattern != null ((BitArray)ms.lazyFillingPattern).getBits(pos, destArray, destArrayOffset, count); } } private void getBitsFromFirstBank(LongBuffer buf, long firstBankOffset, long[] destArray, long destArrayOffset, long count) { PackedBitBuffers.copyBits(LongBuffer.wrap(destArray), destArrayOffset, buf, firstBankOffset, count); } private void setBitsInFirstBank(LongBuffer buf, long firstBankOffset, long[] srcArray, long srcArrayOffset, long count) { PackedBitBuffers.copyBits(buf, firstBankOffset, LongBuffer.wrap(srcArray), srcArrayOffset, count); } private void andBitsFromFirstBank(LongBuffer buf, long firstBankOffset, long[] destArray, long destArrayOffset, long count) { PackedBitBuffers.andBits(destArray, destArrayOffset, buf, firstBankOffset, count); } private void orBitsFromFirstBank(LongBuffer buf, long firstBankOffset, long[] destArray, long destArrayOffset, long count) { PackedBitBuffers.orBits(destArray, destArrayOffset, buf, firstBankOffset, count); } private void xorBitsFromFirstBank(LongBuffer buf, long firstBankOffset, long[] destArray, long destArrayOffset, long count) { PackedBitBuffers.xorBits(destArray, destArrayOffset, buf, firstBankOffset, count); } private void andNotBitsFromFirstBank(LongBuffer buf, long firstBankOffset, long[] destArray, long destArrayOffset, long count) { PackedBitBuffers.andNotBits(destArray, destArrayOffset, buf, firstBankOffset, count); } } /*Repeat() byte(?!s) ==> char,,short,,int,,long,,float,,double;; \bByte\b ==> Character,,Short,,Integer,,Long,,Float,,Double;; Byte(?!Buffer\s+bankBB) ==> Char,,Short,,Int,,Long,,Float,,Double;; BYTE(?!S) ==> CHAR,,SHORT,,INT,,LONG,,FLOAT,,DOUBLE;; bb ==> cb,,sb,,ib,,lb,,fb,,db;; (bh\[bank\]\.data\(\)|=\s*bankBB)(?:\.duplicate\(\))? ==> $1.asCharBuffer(),,$1.asShortBuffer(),, $1.asIntBuffer(),,$1.asLongBuffer(),, $1.asFloatBuffer(),,$1.asDoubleBuffer();; (count)(,\s*truncateOverflows) ==> $1$2,,$1$2,,$1$2,,$1,,...;; (JBuffers\.absDiffOfIntArrayAndBuffer\(.*?)\); ==> $1, truncateOverflows);,,... */ static class MappedByteStorage extends MappedStorage { private final ByteBuffer[] bb; private final DataByteBuffer dbuf; MappedByteStorage(MappingSettings ms) { super(ms); this.bb = (ByteBuffer[])this.specBufs; this.dbuf = ms.lazyFillingPattern == null ? null : (DataByteBuffer)Arrays.bufferInternal(ms.lazyFillingPattern, DataBuffer.AccessMode.READ); } DataStorage newCompatibleEmptyStorage(boolean unresizable) { return new MappedByteStorage(ms.getCompatibleInstanceForNewTemporaryFile(unresizable)); } int bytesPerBufferElementLog() { return BYTES_PER_BYTE_LOG; } @Override final byte getByte(long index) { final ByteBuffer bb0 = bb[0]; if (this.syncNecessary || !(bb0 == validSingleSpecBufForThisThread && bb0 != null)) { // Without synchronization, we may use bb0 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. return getByteSync(index); } else { // single mapping mode and bank is ready return bb0.get((int)index); } } private byte getByteSync(long index) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[0]) { // assert this instanceof MappedBitStorage || index - bankPos[0] <= Integer.MAX_VALUE; i = index - bankPos[0]; } else { i = translateFailedIndex(index, false, LoadingMode.DEFAULT); } return bb[0].get((int)i); } finally { lock.unlock(); } } @Override final void setByte(long index, byte value) { final ByteBuffer bb1 = bb[1]; if (this.syncNecessary || !(bb1 == validSingleSpecBufForThisThread && bb1 != null)) { // Without synchronization, we may use bb1 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. setByteSync(index, value); } else { // single mapping mode and bank is ready bb1.put((int)index, value); } } private void setByteSync(long index, byte value) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[1]) { // assert this instanceof MappedBitStorage || index - bankPos[1] <= Integer.MAX_VALUE; i = index - bankPos[1]; } else { i = translateFailedIndex(index, true, LoadingMode.DEFAULT); } bb[1].put((int)i, value); } finally { lock.unlock(); } } @Override final long indexOfByte(long lowIndex, long highIndex, byte value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(lowIndex); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { ByteBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == ofs; buf = bb[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfByte(buf, ofs, ofs + len, value); if (result != -1) { return result + lowIndex - ofs; } } finally { decrementMappingInUseCounter(); } } lowIndex += len; count -= len; for (; count > 0; lowIndex += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { ByteBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == 0; buf = bb[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfByte(buf, 0, len, value); if (result != -1) { return result + lowIndex; } } finally { decrementMappingInUseCounter(); } } } return -1; } @Override final long lastIndexOfByte(long lowIndex, long highIndex, byte value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(highIndex - 1); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, ofs + 1); { incrementMappingInUseCounter(); try { ByteBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == ofs; buf = bb[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfByte(buf, ofs - len + 1, ofs + 1, value); if (result != -1) { return result + highIndex - 1 - ofs; } } finally { decrementMappingInUseCounter(); } } highIndex -= len; count -= len; for (; count > 0; highIndex -= bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { ByteBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == bse - 1; buf = bb[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfByte(buf, bse - len, bse, value); if (result != -1) { return result + highIndex - bse; } } finally { decrementMappingInUseCounter(); } } } return -1; } final void copy(long destIndex, long srcIndex) { final ByteBuffer bb0 = bb[0], bb1 = bb[1]; if (this.syncNecessary || !(bb0 == validSingleSpecBufForThisThread && bb1 == bb0 && bb0 != null)) { // Without synchronization, we may use bb0/bb1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. copySync(destIndex, srcIndex); } else { // single mapping mode and banks are ready bb1.put((int)destIndex, bb0.get((int)srcIndex)); } } private void copySync(long destIndex, long srcIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { setByte(destIndex, getByte(srcIndex)); } finally { lock.unlock(); } } final void swap(long firstIndex, long secondIndex) { final ByteBuffer bb0 = bb[0], bb1 = bb[1]; if (this.syncNecessary || !(bb0 == validSingleSpecBufForThisThread && bb1 == bb0 && bb0 != null)) { // Without synchronization, we may use bb0/bb1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. swapSync(firstIndex, secondIndex); } else { // single mapping mode and banks are ready int i1 = (int)firstIndex; int i2 = (int)secondIndex; byte v1 = bb0.get(i1); byte v2 = bb1.get(i2); bb0.put(i1, v2); bb1.put(i2, v1); } } private void swapSync(long firstIndex, long secondIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { int i1 = (int)translateIndex(firstIndex); assert i1 >= 0; int i2 = (int)translateIndex(secondIndex, true); assert i2 >= 0; byte v1 = bb[0].get(i1); byte v2 = bb[1].get(i2); bb[0].put(i1, v2); bb[1].put(i2, v1); } finally { lock.unlock(); } } void setSpecificBuffer(int bank) { bb[bank] = bh[bank] == null ? null : bh[bank].data(); } void clearData(long pos, long count) { fillData(pos, count, byteZero); } void getDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; ByteBuffer dup = ((ByteBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.get((byte[])destArray, destArrayOffset, count); } void setDataInFirstBank(Buffer buf, long firstBankOffset, Object srcArray, int srcArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; ByteBuffer dup = ((ByteBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.put((byte[])srcArray, srcArrayOffset, count); } void fillDataInFirstBank(long destOffset, long count, Object fillerWrapper) { int ofs = (int)destOffset; int cnt = (int)count; assert destOffset == ofs; assert count == cnt; JBuffers.fillByteBuffer(bb[0], ofs, cnt, ((Byte)fillerWrapper).byteValue()); } void copyFirstBank(MappedStorage src, long srcOffset, long destOffset, long count, boolean reverseOrder, boolean copyToSecondBank) { int srcOfs = (int)srcOffset; int destOfs = (int)destOffset; int cnt = (int)count; assert srcOffset == srcOfs; assert destOffset == destOfs; assert count == cnt; JBuffers.copyByteBuffer(bb[copyToSecondBank ? 1 : 0], destOfs, ((MappedByteStorage)src).bb[0], srcOfs, cnt, reverseOrder); } void swapFirstBank(MappedStorage another, long anotherOffset, long thisOffset, long count, boolean swapWithSecondBank) { int anotherOfs = (int)anotherOffset; int thisOfs = (int)thisOffset; int cnt = (int)count; assert anotherOffset == anotherOfs; assert thisOffset == thisOfs; assert count == cnt; JBuffers.swapByteBuffer(((MappedByteStorage)another).bb[0], anotherOfs, bb[swapWithSecondBank ? 1 : 0], thisOfs, cnt); } void minDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.minByteArrayAndBuffer((byte[])destArray, destArrayOffset, (ByteBuffer)buf, (int)firstBankOffset, count); } void maxDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.maxByteArrayAndBuffer((byte[])destArray, destArrayOffset, (ByteBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, int[] destArray, int destArrayOffset, int count) { JBuffers.addByteBufferToArray(destArray, destArrayOffset, (ByteBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, double[] destArray, int destArrayOffset, int count, double mult) { JBuffers.addByteBufferToArray(destArray, destArrayOffset, (ByteBuffer)buf, (int)firstBankOffset, count, mult); } void subtractDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.subtractByteBufferFromArray((byte[])destArray, destArrayOffset, (ByteBuffer)buf, (int)firstBankOffset, count, truncateOverflows); } void absDiffDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.absDiffOfByteArrayAndBuffer((byte[])destArray, destArrayOffset, (ByteBuffer)buf, (int)firstBankOffset, count); } @Override void actualizeLazyFillingBank(ByteBuffer bankBB, final long elementIndex) { assert lock.isHeldByCurrentThread() : "actualizeLazyFillingBank is called from non-synchronized code"; assert (elementIndex & 7) == 0; // index is specified in elements and is even enough long length; if (ms.lazyFillingPattern == null || (length = ms.lazyFillingPattern.length()) <= elementIndex) { actualizeLazyZeroFillingBank(bankBB, 0, bankBB.limit()); } else { ByteBuffer bankSpecBuf = bankBB.duplicate(); int count = bankSpecBuf.limit(); if (count > length - elementIndex) { count = (int)(length - elementIndex); actualizeLazyZeroFillingBank(bankBB, count << BYTES_PER_BYTE_LOG, bankBB.limit()); } for (long ofs = 0; ofs < count; ) { dbuf.map(elementIndex + ofs, count - ofs); int len = dbuf.cnt(); assert len > 0; bankSpecBuf.put(dbuf.data(), dbuf.from(), len); // this call shifts bankSpecBuf.position() ofs += len; } } } } /*Repeat.AutoGeneratedStart !! Auto-generated: NOT EDIT !! */ static class MappedCharStorage extends MappedStorage { private final CharBuffer[] cb; private final DataCharBuffer dbuf; MappedCharStorage(MappingSettings ms) { super(ms); this.cb = (CharBuffer[])this.specBufs; this.dbuf = ms.lazyFillingPattern == null ? null : (DataCharBuffer)Arrays.bufferInternal(ms.lazyFillingPattern, DataBuffer.AccessMode.READ); } DataStorage newCompatibleEmptyStorage(boolean unresizable) { return new MappedCharStorage(ms.getCompatibleInstanceForNewTemporaryFile(unresizable)); } int bytesPerBufferElementLog() { return BYTES_PER_CHAR_LOG; } @Override final char getChar(long index) { final CharBuffer cb0 = cb[0]; if (this.syncNecessary || !(cb0 == validSingleSpecBufForThisThread && cb0 != null)) { // Without synchronization, we may use cb0 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. return getCharSync(index); } else { // single mapping mode and bank is ready return cb0.get((int)index); } } private char getCharSync(long index) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[0]) { // assert this instanceof MappedBitStorage || index - bankPos[0] <= Integer.MAX_VALUE; i = index - bankPos[0]; } else { i = translateFailedIndex(index, false, LoadingMode.DEFAULT); } return cb[0].get((int)i); } finally { lock.unlock(); } } @Override final void setChar(long index, char value) { final CharBuffer cb1 = cb[1]; if (this.syncNecessary || !(cb1 == validSingleSpecBufForThisThread && cb1 != null)) { // Without synchronization, we may use cb1 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. setCharSync(index, value); } else { // single mapping mode and bank is ready cb1.put((int)index, value); } } private void setCharSync(long index, char value) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[1]) { // assert this instanceof MappedBitStorage || index - bankPos[1] <= Integer.MAX_VALUE; i = index - bankPos[1]; } else { i = translateFailedIndex(index, true, LoadingMode.DEFAULT); } cb[1].put((int)i, value); } finally { lock.unlock(); } } @Override final long indexOfChar(long lowIndex, long highIndex, char value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(lowIndex); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { CharBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == ofs; buf = cb[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfChar(buf, ofs, ofs + len, value); if (result != -1) { return result + lowIndex - ofs; } } finally { decrementMappingInUseCounter(); } } lowIndex += len; count -= len; for (; count > 0; lowIndex += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { CharBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == 0; buf = cb[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfChar(buf, 0, len, value); if (result != -1) { return result + lowIndex; } } finally { decrementMappingInUseCounter(); } } } return -1; } @Override final long lastIndexOfChar(long lowIndex, long highIndex, char value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(highIndex - 1); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, ofs + 1); { incrementMappingInUseCounter(); try { CharBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == ofs; buf = cb[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfChar(buf, ofs - len + 1, ofs + 1, value); if (result != -1) { return result + highIndex - 1 - ofs; } } finally { decrementMappingInUseCounter(); } } highIndex -= len; count -= len; for (; count > 0; highIndex -= bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { CharBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == bse - 1; buf = cb[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfChar(buf, bse - len, bse, value); if (result != -1) { return result + highIndex - bse; } } finally { decrementMappingInUseCounter(); } } } return -1; } final void copy(long destIndex, long srcIndex) { final CharBuffer cb0 = cb[0], cb1 = cb[1]; if (this.syncNecessary || !(cb0 == validSingleSpecBufForThisThread && cb1 == cb0 && cb0 != null)) { // Without synchronization, we may use cb0/cb1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. copySync(destIndex, srcIndex); } else { // single mapping mode and banks are ready cb1.put((int)destIndex, cb0.get((int)srcIndex)); } } private void copySync(long destIndex, long srcIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { setChar(destIndex, getChar(srcIndex)); } finally { lock.unlock(); } } final void swap(long firstIndex, long secondIndex) { final CharBuffer cb0 = cb[0], cb1 = cb[1]; if (this.syncNecessary || !(cb0 == validSingleSpecBufForThisThread && cb1 == cb0 && cb0 != null)) { // Without synchronization, we may use cb0/cb1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. swapSync(firstIndex, secondIndex); } else { // single mapping mode and banks are ready int i1 = (int)firstIndex; int i2 = (int)secondIndex; char v1 = cb0.get(i1); char v2 = cb1.get(i2); cb0.put(i1, v2); cb1.put(i2, v1); } } private void swapSync(long firstIndex, long secondIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { int i1 = (int)translateIndex(firstIndex); assert i1 >= 0; int i2 = (int)translateIndex(secondIndex, true); assert i2 >= 0; char v1 = cb[0].get(i1); char v2 = cb[1].get(i2); cb[0].put(i1, v2); cb[1].put(i2, v1); } finally { lock.unlock(); } } void setSpecificBuffer(int bank) { cb[bank] = bh[bank] == null ? null : bh[bank].data().asCharBuffer(); } void clearData(long pos, long count) { fillData(pos, count, charZero); } void getDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; CharBuffer dup = ((CharBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.get((char[])destArray, destArrayOffset, count); } void setDataInFirstBank(Buffer buf, long firstBankOffset, Object srcArray, int srcArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; CharBuffer dup = ((CharBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.put((char[])srcArray, srcArrayOffset, count); } void fillDataInFirstBank(long destOffset, long count, Object fillerWrapper) { int ofs = (int)destOffset; int cnt = (int)count; assert destOffset == ofs; assert count == cnt; JBuffers.fillCharBuffer(cb[0], ofs, cnt, ((Character)fillerWrapper).charValue()); } void copyFirstBank(MappedStorage src, long srcOffset, long destOffset, long count, boolean reverseOrder, boolean copyToSecondBank) { int srcOfs = (int)srcOffset; int destOfs = (int)destOffset; int cnt = (int)count; assert srcOffset == srcOfs; assert destOffset == destOfs; assert count == cnt; JBuffers.copyCharBuffer(cb[copyToSecondBank ? 1 : 0], destOfs, ((MappedCharStorage)src).cb[0], srcOfs, cnt, reverseOrder); } void swapFirstBank(MappedStorage another, long anotherOffset, long thisOffset, long count, boolean swapWithSecondBank) { int anotherOfs = (int)anotherOffset; int thisOfs = (int)thisOffset; int cnt = (int)count; assert anotherOffset == anotherOfs; assert thisOffset == thisOfs; assert count == cnt; JBuffers.swapCharBuffer(((MappedCharStorage)another).cb[0], anotherOfs, cb[swapWithSecondBank ? 1 : 0], thisOfs, cnt); } void minDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.minCharArrayAndBuffer((char[])destArray, destArrayOffset, (CharBuffer)buf, (int)firstBankOffset, count); } void maxDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.maxCharArrayAndBuffer((char[])destArray, destArrayOffset, (CharBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, int[] destArray, int destArrayOffset, int count) { JBuffers.addCharBufferToArray(destArray, destArrayOffset, (CharBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, double[] destArray, int destArrayOffset, int count, double mult) { JBuffers.addCharBufferToArray(destArray, destArrayOffset, (CharBuffer)buf, (int)firstBankOffset, count, mult); } void subtractDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.subtractCharBufferFromArray((char[])destArray, destArrayOffset, (CharBuffer)buf, (int)firstBankOffset, count, truncateOverflows); } void absDiffDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.absDiffOfCharArrayAndBuffer((char[])destArray, destArrayOffset, (CharBuffer)buf, (int)firstBankOffset, count); } @Override void actualizeLazyFillingBank(ByteBuffer bankBB, final long elementIndex) { assert lock.isHeldByCurrentThread() : "actualizeLazyFillingBank is called from non-synchronized code"; assert (elementIndex & 7) == 0; // index is specified in elements and is even enough long length; if (ms.lazyFillingPattern == null || (length = ms.lazyFillingPattern.length()) <= elementIndex) { actualizeLazyZeroFillingBank(bankBB, 0, bankBB.limit()); } else { CharBuffer bankSpecBuf = bankBB.asCharBuffer(); int count = bankSpecBuf.limit(); if (count > length - elementIndex) { count = (int)(length - elementIndex); actualizeLazyZeroFillingBank(bankBB, count << BYTES_PER_CHAR_LOG, bankBB.limit()); } for (long ofs = 0; ofs < count; ) { dbuf.map(elementIndex + ofs, count - ofs); int len = dbuf.cnt(); assert len > 0; bankSpecBuf.put(dbuf.data(), dbuf.from(), len); // this call shifts bankSpecBuf.position() ofs += len; } } } } static class MappedShortStorage extends MappedStorage { private final ShortBuffer[] sb; private final DataShortBuffer dbuf; MappedShortStorage(MappingSettings ms) { super(ms); this.sb = (ShortBuffer[])this.specBufs; this.dbuf = ms.lazyFillingPattern == null ? null : (DataShortBuffer)Arrays.bufferInternal(ms.lazyFillingPattern, DataBuffer.AccessMode.READ); } DataStorage newCompatibleEmptyStorage(boolean unresizable) { return new MappedShortStorage(ms.getCompatibleInstanceForNewTemporaryFile(unresizable)); } int bytesPerBufferElementLog() { return BYTES_PER_SHORT_LOG; } @Override final short getShort(long index) { final ShortBuffer sb0 = sb[0]; if (this.syncNecessary || !(sb0 == validSingleSpecBufForThisThread && sb0 != null)) { // Without synchronization, we may use sb0 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. return getShortSync(index); } else { // single mapping mode and bank is ready return sb0.get((int)index); } } private short getShortSync(long index) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[0]) { // assert this instanceof MappedBitStorage || index - bankPos[0] <= Integer.MAX_VALUE; i = index - bankPos[0]; } else { i = translateFailedIndex(index, false, LoadingMode.DEFAULT); } return sb[0].get((int)i); } finally { lock.unlock(); } } @Override final void setShort(long index, short value) { final ShortBuffer sb1 = sb[1]; if (this.syncNecessary || !(sb1 == validSingleSpecBufForThisThread && sb1 != null)) { // Without synchronization, we may use sb1 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. setShortSync(index, value); } else { // single mapping mode and bank is ready sb1.put((int)index, value); } } private void setShortSync(long index, short value) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[1]) { // assert this instanceof MappedBitStorage || index - bankPos[1] <= Integer.MAX_VALUE; i = index - bankPos[1]; } else { i = translateFailedIndex(index, true, LoadingMode.DEFAULT); } sb[1].put((int)i, value); } finally { lock.unlock(); } } @Override final long indexOfShort(long lowIndex, long highIndex, short value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(lowIndex); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { ShortBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == ofs; buf = sb[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfShort(buf, ofs, ofs + len, value); if (result != -1) { return result + lowIndex - ofs; } } finally { decrementMappingInUseCounter(); } } lowIndex += len; count -= len; for (; count > 0; lowIndex += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { ShortBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == 0; buf = sb[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfShort(buf, 0, len, value); if (result != -1) { return result + lowIndex; } } finally { decrementMappingInUseCounter(); } } } return -1; } @Override final long lastIndexOfShort(long lowIndex, long highIndex, short value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(highIndex - 1); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, ofs + 1); { incrementMappingInUseCounter(); try { ShortBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == ofs; buf = sb[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfShort(buf, ofs - len + 1, ofs + 1, value); if (result != -1) { return result + highIndex - 1 - ofs; } } finally { decrementMappingInUseCounter(); } } highIndex -= len; count -= len; for (; count > 0; highIndex -= bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { ShortBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == bse - 1; buf = sb[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfShort(buf, bse - len, bse, value); if (result != -1) { return result + highIndex - bse; } } finally { decrementMappingInUseCounter(); } } } return -1; } final void copy(long destIndex, long srcIndex) { final ShortBuffer sb0 = sb[0], sb1 = sb[1]; if (this.syncNecessary || !(sb0 == validSingleSpecBufForThisThread && sb1 == sb0 && sb0 != null)) { // Without synchronization, we may use sb0/sb1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. copySync(destIndex, srcIndex); } else { // single mapping mode and banks are ready sb1.put((int)destIndex, sb0.get((int)srcIndex)); } } private void copySync(long destIndex, long srcIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { setShort(destIndex, getShort(srcIndex)); } finally { lock.unlock(); } } final void swap(long firstIndex, long secondIndex) { final ShortBuffer sb0 = sb[0], sb1 = sb[1]; if (this.syncNecessary || !(sb0 == validSingleSpecBufForThisThread && sb1 == sb0 && sb0 != null)) { // Without synchronization, we may use sb0/sb1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. swapSync(firstIndex, secondIndex); } else { // single mapping mode and banks are ready int i1 = (int)firstIndex; int i2 = (int)secondIndex; short v1 = sb0.get(i1); short v2 = sb1.get(i2); sb0.put(i1, v2); sb1.put(i2, v1); } } private void swapSync(long firstIndex, long secondIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { int i1 = (int)translateIndex(firstIndex); assert i1 >= 0; int i2 = (int)translateIndex(secondIndex, true); assert i2 >= 0; short v1 = sb[0].get(i1); short v2 = sb[1].get(i2); sb[0].put(i1, v2); sb[1].put(i2, v1); } finally { lock.unlock(); } } void setSpecificBuffer(int bank) { sb[bank] = bh[bank] == null ? null : bh[bank].data().asShortBuffer(); } void clearData(long pos, long count) { fillData(pos, count, shortZero); } void getDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; ShortBuffer dup = ((ShortBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.get((short[])destArray, destArrayOffset, count); } void setDataInFirstBank(Buffer buf, long firstBankOffset, Object srcArray, int srcArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; ShortBuffer dup = ((ShortBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.put((short[])srcArray, srcArrayOffset, count); } void fillDataInFirstBank(long destOffset, long count, Object fillerWrapper) { int ofs = (int)destOffset; int cnt = (int)count; assert destOffset == ofs; assert count == cnt; JBuffers.fillShortBuffer(sb[0], ofs, cnt, ((Short)fillerWrapper).shortValue()); } void copyFirstBank(MappedStorage src, long srcOffset, long destOffset, long count, boolean reverseOrder, boolean copyToSecondBank) { int srcOfs = (int)srcOffset; int destOfs = (int)destOffset; int cnt = (int)count; assert srcOffset == srcOfs; assert destOffset == destOfs; assert count == cnt; JBuffers.copyShortBuffer(sb[copyToSecondBank ? 1 : 0], destOfs, ((MappedShortStorage)src).sb[0], srcOfs, cnt, reverseOrder); } void swapFirstBank(MappedStorage another, long anotherOffset, long thisOffset, long count, boolean swapWithSecondBank) { int anotherOfs = (int)anotherOffset; int thisOfs = (int)thisOffset; int cnt = (int)count; assert anotherOffset == anotherOfs; assert thisOffset == thisOfs; assert count == cnt; JBuffers.swapShortBuffer(((MappedShortStorage)another).sb[0], anotherOfs, sb[swapWithSecondBank ? 1 : 0], thisOfs, cnt); } void minDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.minShortArrayAndBuffer((short[])destArray, destArrayOffset, (ShortBuffer)buf, (int)firstBankOffset, count); } void maxDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.maxShortArrayAndBuffer((short[])destArray, destArrayOffset, (ShortBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, int[] destArray, int destArrayOffset, int count) { JBuffers.addShortBufferToArray(destArray, destArrayOffset, (ShortBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, double[] destArray, int destArrayOffset, int count, double mult) { JBuffers.addShortBufferToArray(destArray, destArrayOffset, (ShortBuffer)buf, (int)firstBankOffset, count, mult); } void subtractDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.subtractShortBufferFromArray((short[])destArray, destArrayOffset, (ShortBuffer)buf, (int)firstBankOffset, count, truncateOverflows); } void absDiffDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.absDiffOfShortArrayAndBuffer((short[])destArray, destArrayOffset, (ShortBuffer)buf, (int)firstBankOffset, count); } @Override void actualizeLazyFillingBank(ByteBuffer bankBB, final long elementIndex) { assert lock.isHeldByCurrentThread() : "actualizeLazyFillingBank is called from non-synchronized code"; assert (elementIndex & 7) == 0; // index is specified in elements and is even enough long length; if (ms.lazyFillingPattern == null || (length = ms.lazyFillingPattern.length()) <= elementIndex) { actualizeLazyZeroFillingBank(bankBB, 0, bankBB.limit()); } else { ShortBuffer bankSpecBuf = bankBB.asShortBuffer(); int count = bankSpecBuf.limit(); if (count > length - elementIndex) { count = (int)(length - elementIndex); actualizeLazyZeroFillingBank(bankBB, count << BYTES_PER_SHORT_LOG, bankBB.limit()); } for (long ofs = 0; ofs < count; ) { dbuf.map(elementIndex + ofs, count - ofs); int len = dbuf.cnt(); assert len > 0; bankSpecBuf.put(dbuf.data(), dbuf.from(), len); // this call shifts bankSpecBuf.position() ofs += len; } } } } static class MappedIntStorage extends MappedStorage { private final IntBuffer[] ib; private final DataIntBuffer dbuf; MappedIntStorage(MappingSettings ms) { super(ms); this.ib = (IntBuffer[])this.specBufs; this.dbuf = ms.lazyFillingPattern == null ? null : (DataIntBuffer)Arrays.bufferInternal(ms.lazyFillingPattern, DataBuffer.AccessMode.READ); } DataStorage newCompatibleEmptyStorage(boolean unresizable) { return new MappedIntStorage(ms.getCompatibleInstanceForNewTemporaryFile(unresizable)); } int bytesPerBufferElementLog() { return BYTES_PER_INT_LOG; } @Override final int getInt(long index) { final IntBuffer ib0 = ib[0]; if (this.syncNecessary || !(ib0 == validSingleSpecBufForThisThread && ib0 != null)) { // Without synchronization, we may use ib0 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. return getIntSync(index); } else { // single mapping mode and bank is ready return ib0.get((int)index); } } private int getIntSync(long index) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[0]) { // assert this instanceof MappedBitStorage || index - bankPos[0] <= Integer.MAX_VALUE; i = index - bankPos[0]; } else { i = translateFailedIndex(index, false, LoadingMode.DEFAULT); } return ib[0].get((int)i); } finally { lock.unlock(); } } @Override final void setInt(long index, int value) { final IntBuffer ib1 = ib[1]; if (this.syncNecessary || !(ib1 == validSingleSpecBufForThisThread && ib1 != null)) { // Without synchronization, we may use ib1 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. setIntSync(index, value); } else { // single mapping mode and bank is ready ib1.put((int)index, value); } } private void setIntSync(long index, int value) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[1]) { // assert this instanceof MappedBitStorage || index - bankPos[1] <= Integer.MAX_VALUE; i = index - bankPos[1]; } else { i = translateFailedIndex(index, true, LoadingMode.DEFAULT); } ib[1].put((int)i, value); } finally { lock.unlock(); } } @Override final long indexOfInt(long lowIndex, long highIndex, int value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(lowIndex); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { IntBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == ofs; buf = ib[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfInt(buf, ofs, ofs + len, value); if (result != -1) { return result + lowIndex - ofs; } } finally { decrementMappingInUseCounter(); } } lowIndex += len; count -= len; for (; count > 0; lowIndex += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { IntBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == 0; buf = ib[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfInt(buf, 0, len, value); if (result != -1) { return result + lowIndex; } } finally { decrementMappingInUseCounter(); } } } return -1; } @Override final long lastIndexOfInt(long lowIndex, long highIndex, int value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(highIndex - 1); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, ofs + 1); { incrementMappingInUseCounter(); try { IntBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == ofs; buf = ib[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfInt(buf, ofs - len + 1, ofs + 1, value); if (result != -1) { return result + highIndex - 1 - ofs; } } finally { decrementMappingInUseCounter(); } } highIndex -= len; count -= len; for (; count > 0; highIndex -= bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { IntBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == bse - 1; buf = ib[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfInt(buf, bse - len, bse, value); if (result != -1) { return result + highIndex - bse; } } finally { decrementMappingInUseCounter(); } } } return -1; } final void copy(long destIndex, long srcIndex) { final IntBuffer ib0 = ib[0], ib1 = ib[1]; if (this.syncNecessary || !(ib0 == validSingleSpecBufForThisThread && ib1 == ib0 && ib0 != null)) { // Without synchronization, we may use ib0/ib1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. copySync(destIndex, srcIndex); } else { // single mapping mode and banks are ready ib1.put((int)destIndex, ib0.get((int)srcIndex)); } } private void copySync(long destIndex, long srcIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { setInt(destIndex, getInt(srcIndex)); } finally { lock.unlock(); } } final void swap(long firstIndex, long secondIndex) { final IntBuffer ib0 = ib[0], ib1 = ib[1]; if (this.syncNecessary || !(ib0 == validSingleSpecBufForThisThread && ib1 == ib0 && ib0 != null)) { // Without synchronization, we may use ib0/ib1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. swapSync(firstIndex, secondIndex); } else { // single mapping mode and banks are ready int i1 = (int)firstIndex; int i2 = (int)secondIndex; int v1 = ib0.get(i1); int v2 = ib1.get(i2); ib0.put(i1, v2); ib1.put(i2, v1); } } private void swapSync(long firstIndex, long secondIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { int i1 = (int)translateIndex(firstIndex); assert i1 >= 0; int i2 = (int)translateIndex(secondIndex, true); assert i2 >= 0; int v1 = ib[0].get(i1); int v2 = ib[1].get(i2); ib[0].put(i1, v2); ib[1].put(i2, v1); } finally { lock.unlock(); } } void setSpecificBuffer(int bank) { ib[bank] = bh[bank] == null ? null : bh[bank].data().asIntBuffer(); } void clearData(long pos, long count) { fillData(pos, count, intZero); } void getDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; IntBuffer dup = ((IntBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.get((int[])destArray, destArrayOffset, count); } void setDataInFirstBank(Buffer buf, long firstBankOffset, Object srcArray, int srcArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; IntBuffer dup = ((IntBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.put((int[])srcArray, srcArrayOffset, count); } void fillDataInFirstBank(long destOffset, long count, Object fillerWrapper) { int ofs = (int)destOffset; int cnt = (int)count; assert destOffset == ofs; assert count == cnt; JBuffers.fillIntBuffer(ib[0], ofs, cnt, ((Integer)fillerWrapper).intValue()); } void copyFirstBank(MappedStorage src, long srcOffset, long destOffset, long count, boolean reverseOrder, boolean copyToSecondBank) { int srcOfs = (int)srcOffset; int destOfs = (int)destOffset; int cnt = (int)count; assert srcOffset == srcOfs; assert destOffset == destOfs; assert count == cnt; JBuffers.copyIntBuffer(ib[copyToSecondBank ? 1 : 0], destOfs, ((MappedIntStorage)src).ib[0], srcOfs, cnt, reverseOrder); } void swapFirstBank(MappedStorage another, long anotherOffset, long thisOffset, long count, boolean swapWithSecondBank) { int anotherOfs = (int)anotherOffset; int thisOfs = (int)thisOffset; int cnt = (int)count; assert anotherOffset == anotherOfs; assert thisOffset == thisOfs; assert count == cnt; JBuffers.swapIntBuffer(((MappedIntStorage)another).ib[0], anotherOfs, ib[swapWithSecondBank ? 1 : 0], thisOfs, cnt); } void minDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.minIntArrayAndBuffer((int[])destArray, destArrayOffset, (IntBuffer)buf, (int)firstBankOffset, count); } void maxDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.maxIntArrayAndBuffer((int[])destArray, destArrayOffset, (IntBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, int[] destArray, int destArrayOffset, int count) { JBuffers.addIntBufferToArray(destArray, destArrayOffset, (IntBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, double[] destArray, int destArrayOffset, int count, double mult) { JBuffers.addIntBufferToArray(destArray, destArrayOffset, (IntBuffer)buf, (int)firstBankOffset, count, mult); } void subtractDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.subtractIntBufferFromArray((int[])destArray, destArrayOffset, (IntBuffer)buf, (int)firstBankOffset, count, truncateOverflows); } void absDiffDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.absDiffOfIntArrayAndBuffer((int[])destArray, destArrayOffset, (IntBuffer)buf, (int)firstBankOffset, count, truncateOverflows); } @Override void actualizeLazyFillingBank(ByteBuffer bankBB, final long elementIndex) { assert lock.isHeldByCurrentThread() : "actualizeLazyFillingBank is called from non-synchronized code"; assert (elementIndex & 7) == 0; // index is specified in elements and is even enough long length; if (ms.lazyFillingPattern == null || (length = ms.lazyFillingPattern.length()) <= elementIndex) { actualizeLazyZeroFillingBank(bankBB, 0, bankBB.limit()); } else { IntBuffer bankSpecBuf = bankBB.asIntBuffer(); int count = bankSpecBuf.limit(); if (count > length - elementIndex) { count = (int)(length - elementIndex); actualizeLazyZeroFillingBank(bankBB, count << BYTES_PER_INT_LOG, bankBB.limit()); } for (long ofs = 0; ofs < count; ) { dbuf.map(elementIndex + ofs, count - ofs); int len = dbuf.cnt(); assert len > 0; bankSpecBuf.put(dbuf.data(), dbuf.from(), len); // this call shifts bankSpecBuf.position() ofs += len; } } } } static class MappedLongStorage extends MappedStorage { private final LongBuffer[] lb; private final DataLongBuffer dbuf; MappedLongStorage(MappingSettings ms) { super(ms); this.lb = (LongBuffer[])this.specBufs; this.dbuf = ms.lazyFillingPattern == null ? null : (DataLongBuffer)Arrays.bufferInternal(ms.lazyFillingPattern, DataBuffer.AccessMode.READ); } DataStorage newCompatibleEmptyStorage(boolean unresizable) { return new MappedLongStorage(ms.getCompatibleInstanceForNewTemporaryFile(unresizable)); } int bytesPerBufferElementLog() { return BYTES_PER_LONG_LOG; } @Override final long getLong(long index) { final LongBuffer lb0 = lb[0]; if (this.syncNecessary || !(lb0 == validSingleSpecBufForThisThread && lb0 != null)) { // Without synchronization, we may use lb0 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. return getLongSync(index); } else { // single mapping mode and bank is ready return lb0.get((int)index); } } private long getLongSync(long index) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[0]) { // assert this instanceof MappedBitStorage || index - bankPos[0] <= Integer.MAX_VALUE; i = index - bankPos[0]; } else { i = translateFailedIndex(index, false, LoadingMode.DEFAULT); } return lb[0].get((int)i); } finally { lock.unlock(); } } @Override final void setLong(long index, long value) { final LongBuffer lb1 = lb[1]; if (this.syncNecessary || !(lb1 == validSingleSpecBufForThisThread && lb1 != null)) { // Without synchronization, we may use lb1 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. setLongSync(index, value); } else { // single mapping mode and bank is ready lb1.put((int)index, value); } } private void setLongSync(long index, long value) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[1]) { // assert this instanceof MappedBitStorage || index - bankPos[1] <= Integer.MAX_VALUE; i = index - bankPos[1]; } else { i = translateFailedIndex(index, true, LoadingMode.DEFAULT); } lb[1].put((int)i, value); } finally { lock.unlock(); } } @Override final long indexOfLong(long lowIndex, long highIndex, long value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(lowIndex); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == ofs; buf = lb[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfLong(buf, ofs, ofs + len, value); if (result != -1) { return result + lowIndex - ofs; } } finally { decrementMappingInUseCounter(); } } lowIndex += len; count -= len; for (; count > 0; lowIndex += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == 0; buf = lb[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfLong(buf, 0, len, value); if (result != -1) { return result + lowIndex; } } finally { decrementMappingInUseCounter(); } } } return -1; } @Override final long lastIndexOfLong(long lowIndex, long highIndex, long value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(highIndex - 1); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, ofs + 1); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == ofs; buf = lb[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfLong(buf, ofs - len + 1, ofs + 1, value); if (result != -1) { return result + highIndex - 1 - ofs; } } finally { decrementMappingInUseCounter(); } } highIndex -= len; count -= len; for (; count > 0; highIndex -= bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { LongBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == bse - 1; buf = lb[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfLong(buf, bse - len, bse, value); if (result != -1) { return result + highIndex - bse; } } finally { decrementMappingInUseCounter(); } } } return -1; } final void copy(long destIndex, long srcIndex) { final LongBuffer lb0 = lb[0], lb1 = lb[1]; if (this.syncNecessary || !(lb0 == validSingleSpecBufForThisThread && lb1 == lb0 && lb0 != null)) { // Without synchronization, we may use lb0/lb1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. copySync(destIndex, srcIndex); } else { // single mapping mode and banks are ready lb1.put((int)destIndex, lb0.get((int)srcIndex)); } } private void copySync(long destIndex, long srcIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { setLong(destIndex, getLong(srcIndex)); } finally { lock.unlock(); } } final void swap(long firstIndex, long secondIndex) { final LongBuffer lb0 = lb[0], lb1 = lb[1]; if (this.syncNecessary || !(lb0 == validSingleSpecBufForThisThread && lb1 == lb0 && lb0 != null)) { // Without synchronization, we may use lb0/lb1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. swapSync(firstIndex, secondIndex); } else { // single mapping mode and banks are ready int i1 = (int)firstIndex; int i2 = (int)secondIndex; long v1 = lb0.get(i1); long v2 = lb1.get(i2); lb0.put(i1, v2); lb1.put(i2, v1); } } private void swapSync(long firstIndex, long secondIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { int i1 = (int)translateIndex(firstIndex); assert i1 >= 0; int i2 = (int)translateIndex(secondIndex, true); assert i2 >= 0; long v1 = lb[0].get(i1); long v2 = lb[1].get(i2); lb[0].put(i1, v2); lb[1].put(i2, v1); } finally { lock.unlock(); } } void setSpecificBuffer(int bank) { lb[bank] = bh[bank] == null ? null : bh[bank].data().asLongBuffer(); } void clearData(long pos, long count) { fillData(pos, count, longZero); } void getDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; LongBuffer dup = ((LongBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.get((long[])destArray, destArrayOffset, count); } void setDataInFirstBank(Buffer buf, long firstBankOffset, Object srcArray, int srcArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; LongBuffer dup = ((LongBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.put((long[])srcArray, srcArrayOffset, count); } void fillDataInFirstBank(long destOffset, long count, Object fillerWrapper) { int ofs = (int)destOffset; int cnt = (int)count; assert destOffset == ofs; assert count == cnt; JBuffers.fillLongBuffer(lb[0], ofs, cnt, ((Long)fillerWrapper).longValue()); } void copyFirstBank(MappedStorage src, long srcOffset, long destOffset, long count, boolean reverseOrder, boolean copyToSecondBank) { int srcOfs = (int)srcOffset; int destOfs = (int)destOffset; int cnt = (int)count; assert srcOffset == srcOfs; assert destOffset == destOfs; assert count == cnt; JBuffers.copyLongBuffer(lb[copyToSecondBank ? 1 : 0], destOfs, ((MappedLongStorage)src).lb[0], srcOfs, cnt, reverseOrder); } void swapFirstBank(MappedStorage another, long anotherOffset, long thisOffset, long count, boolean swapWithSecondBank) { int anotherOfs = (int)anotherOffset; int thisOfs = (int)thisOffset; int cnt = (int)count; assert anotherOffset == anotherOfs; assert thisOffset == thisOfs; assert count == cnt; JBuffers.swapLongBuffer(((MappedLongStorage)another).lb[0], anotherOfs, lb[swapWithSecondBank ? 1 : 0], thisOfs, cnt); } void minDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.minLongArrayAndBuffer((long[])destArray, destArrayOffset, (LongBuffer)buf, (int)firstBankOffset, count); } void maxDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.maxLongArrayAndBuffer((long[])destArray, destArrayOffset, (LongBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, int[] destArray, int destArrayOffset, int count) { JBuffers.addLongBufferToArray(destArray, destArrayOffset, (LongBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, double[] destArray, int destArrayOffset, int count, double mult) { JBuffers.addLongBufferToArray(destArray, destArrayOffset, (LongBuffer)buf, (int)firstBankOffset, count, mult); } void subtractDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.subtractLongBufferFromArray((long[])destArray, destArrayOffset, (LongBuffer)buf, (int)firstBankOffset, count); } void absDiffDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.absDiffOfLongArrayAndBuffer((long[])destArray, destArrayOffset, (LongBuffer)buf, (int)firstBankOffset, count); } @Override void actualizeLazyFillingBank(ByteBuffer bankBB, final long elementIndex) { assert lock.isHeldByCurrentThread() : "actualizeLazyFillingBank is called from non-synchronized code"; assert (elementIndex & 7) == 0; // index is specified in elements and is even enough long length; if (ms.lazyFillingPattern == null || (length = ms.lazyFillingPattern.length()) <= elementIndex) { actualizeLazyZeroFillingBank(bankBB, 0, bankBB.limit()); } else { LongBuffer bankSpecBuf = bankBB.asLongBuffer(); int count = bankSpecBuf.limit(); if (count > length - elementIndex) { count = (int)(length - elementIndex); actualizeLazyZeroFillingBank(bankBB, count << BYTES_PER_LONG_LOG, bankBB.limit()); } for (long ofs = 0; ofs < count; ) { dbuf.map(elementIndex + ofs, count - ofs); int len = dbuf.cnt(); assert len > 0; bankSpecBuf.put(dbuf.data(), dbuf.from(), len); // this call shifts bankSpecBuf.position() ofs += len; } } } } static class MappedFloatStorage extends MappedStorage { private final FloatBuffer[] fb; private final DataFloatBuffer dbuf; MappedFloatStorage(MappingSettings ms) { super(ms); this.fb = (FloatBuffer[])this.specBufs; this.dbuf = ms.lazyFillingPattern == null ? null : (DataFloatBuffer)Arrays.bufferInternal(ms.lazyFillingPattern, DataBuffer.AccessMode.READ); } DataStorage newCompatibleEmptyStorage(boolean unresizable) { return new MappedFloatStorage(ms.getCompatibleInstanceForNewTemporaryFile(unresizable)); } int bytesPerBufferElementLog() { return BYTES_PER_FLOAT_LOG; } @Override final float getFloat(long index) { final FloatBuffer fb0 = fb[0]; if (this.syncNecessary || !(fb0 == validSingleSpecBufForThisThread && fb0 != null)) { // Without synchronization, we may use fb0 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. return getFloatSync(index); } else { // single mapping mode and bank is ready return fb0.get((int)index); } } private float getFloatSync(long index) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[0]) { // assert this instanceof MappedBitStorage || index - bankPos[0] <= Integer.MAX_VALUE; i = index - bankPos[0]; } else { i = translateFailedIndex(index, false, LoadingMode.DEFAULT); } return fb[0].get((int)i); } finally { lock.unlock(); } } @Override final void setFloat(long index, float value) { final FloatBuffer fb1 = fb[1]; if (this.syncNecessary || !(fb1 == validSingleSpecBufForThisThread && fb1 != null)) { // Without synchronization, we may use fb1 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. setFloatSync(index, value); } else { // single mapping mode and bank is ready fb1.put((int)index, value); } } private void setFloatSync(long index, float value) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[1]) { // assert this instanceof MappedBitStorage || index - bankPos[1] <= Integer.MAX_VALUE; i = index - bankPos[1]; } else { i = translateFailedIndex(index, true, LoadingMode.DEFAULT); } fb[1].put((int)i, value); } finally { lock.unlock(); } } @Override final long indexOfFloat(long lowIndex, long highIndex, float value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(lowIndex); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { FloatBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == ofs; buf = fb[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfFloat(buf, ofs, ofs + len, value); if (result != -1) { return result + lowIndex - ofs; } } finally { decrementMappingInUseCounter(); } } lowIndex += len; count -= len; for (; count > 0; lowIndex += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { FloatBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == 0; buf = fb[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfFloat(buf, 0, len, value); if (result != -1) { return result + lowIndex; } } finally { decrementMappingInUseCounter(); } } } return -1; } @Override final long lastIndexOfFloat(long lowIndex, long highIndex, float value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(highIndex - 1); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, ofs + 1); { incrementMappingInUseCounter(); try { FloatBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == ofs; buf = fb[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfFloat(buf, ofs - len + 1, ofs + 1, value); if (result != -1) { return result + highIndex - 1 - ofs; } } finally { decrementMappingInUseCounter(); } } highIndex -= len; count -= len; for (; count > 0; highIndex -= bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { FloatBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == bse - 1; buf = fb[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfFloat(buf, bse - len, bse, value); if (result != -1) { return result + highIndex - bse; } } finally { decrementMappingInUseCounter(); } } } return -1; } final void copy(long destIndex, long srcIndex) { final FloatBuffer fb0 = fb[0], fb1 = fb[1]; if (this.syncNecessary || !(fb0 == validSingleSpecBufForThisThread && fb1 == fb0 && fb0 != null)) { // Without synchronization, we may use fb0/fb1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. copySync(destIndex, srcIndex); } else { // single mapping mode and banks are ready fb1.put((int)destIndex, fb0.get((int)srcIndex)); } } private void copySync(long destIndex, long srcIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { setFloat(destIndex, getFloat(srcIndex)); } finally { lock.unlock(); } } final void swap(long firstIndex, long secondIndex) { final FloatBuffer fb0 = fb[0], fb1 = fb[1]; if (this.syncNecessary || !(fb0 == validSingleSpecBufForThisThread && fb1 == fb0 && fb0 != null)) { // Without synchronization, we may use fb0/fb1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. swapSync(firstIndex, secondIndex); } else { // single mapping mode and banks are ready int i1 = (int)firstIndex; int i2 = (int)secondIndex; float v1 = fb0.get(i1); float v2 = fb1.get(i2); fb0.put(i1, v2); fb1.put(i2, v1); } } private void swapSync(long firstIndex, long secondIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { int i1 = (int)translateIndex(firstIndex); assert i1 >= 0; int i2 = (int)translateIndex(secondIndex, true); assert i2 >= 0; float v1 = fb[0].get(i1); float v2 = fb[1].get(i2); fb[0].put(i1, v2); fb[1].put(i2, v1); } finally { lock.unlock(); } } void setSpecificBuffer(int bank) { fb[bank] = bh[bank] == null ? null : bh[bank].data().asFloatBuffer(); } void clearData(long pos, long count) { fillData(pos, count, floatZero); } void getDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; FloatBuffer dup = ((FloatBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.get((float[])destArray, destArrayOffset, count); } void setDataInFirstBank(Buffer buf, long firstBankOffset, Object srcArray, int srcArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; FloatBuffer dup = ((FloatBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.put((float[])srcArray, srcArrayOffset, count); } void fillDataInFirstBank(long destOffset, long count, Object fillerWrapper) { int ofs = (int)destOffset; int cnt = (int)count; assert destOffset == ofs; assert count == cnt; JBuffers.fillFloatBuffer(fb[0], ofs, cnt, ((Float)fillerWrapper).floatValue()); } void copyFirstBank(MappedStorage src, long srcOffset, long destOffset, long count, boolean reverseOrder, boolean copyToSecondBank) { int srcOfs = (int)srcOffset; int destOfs = (int)destOffset; int cnt = (int)count; assert srcOffset == srcOfs; assert destOffset == destOfs; assert count == cnt; JBuffers.copyFloatBuffer(fb[copyToSecondBank ? 1 : 0], destOfs, ((MappedFloatStorage)src).fb[0], srcOfs, cnt, reverseOrder); } void swapFirstBank(MappedStorage another, long anotherOffset, long thisOffset, long count, boolean swapWithSecondBank) { int anotherOfs = (int)anotherOffset; int thisOfs = (int)thisOffset; int cnt = (int)count; assert anotherOffset == anotherOfs; assert thisOffset == thisOfs; assert count == cnt; JBuffers.swapFloatBuffer(((MappedFloatStorage)another).fb[0], anotherOfs, fb[swapWithSecondBank ? 1 : 0], thisOfs, cnt); } void minDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.minFloatArrayAndBuffer((float[])destArray, destArrayOffset, (FloatBuffer)buf, (int)firstBankOffset, count); } void maxDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.maxFloatArrayAndBuffer((float[])destArray, destArrayOffset, (FloatBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, int[] destArray, int destArrayOffset, int count) { JBuffers.addFloatBufferToArray(destArray, destArrayOffset, (FloatBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, double[] destArray, int destArrayOffset, int count, double mult) { JBuffers.addFloatBufferToArray(destArray, destArrayOffset, (FloatBuffer)buf, (int)firstBankOffset, count, mult); } void subtractDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.subtractFloatBufferFromArray((float[])destArray, destArrayOffset, (FloatBuffer)buf, (int)firstBankOffset, count); } void absDiffDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.absDiffOfFloatArrayAndBuffer((float[])destArray, destArrayOffset, (FloatBuffer)buf, (int)firstBankOffset, count); } @Override void actualizeLazyFillingBank(ByteBuffer bankBB, final long elementIndex) { assert lock.isHeldByCurrentThread() : "actualizeLazyFillingBank is called from non-synchronized code"; assert (elementIndex & 7) == 0; // index is specified in elements and is even enough long length; if (ms.lazyFillingPattern == null || (length = ms.lazyFillingPattern.length()) <= elementIndex) { actualizeLazyZeroFillingBank(bankBB, 0, bankBB.limit()); } else { FloatBuffer bankSpecBuf = bankBB.asFloatBuffer(); int count = bankSpecBuf.limit(); if (count > length - elementIndex) { count = (int)(length - elementIndex); actualizeLazyZeroFillingBank(bankBB, count << BYTES_PER_FLOAT_LOG, bankBB.limit()); } for (long ofs = 0; ofs < count; ) { dbuf.map(elementIndex + ofs, count - ofs); int len = dbuf.cnt(); assert len > 0; bankSpecBuf.put(dbuf.data(), dbuf.from(), len); // this call shifts bankSpecBuf.position() ofs += len; } } } } static class MappedDoubleStorage extends MappedStorage { private final DoubleBuffer[] db; private final DataDoubleBuffer dbuf; MappedDoubleStorage(MappingSettings ms) { super(ms); this.db = (DoubleBuffer[])this.specBufs; this.dbuf = ms.lazyFillingPattern == null ? null : (DataDoubleBuffer)Arrays.bufferInternal(ms.lazyFillingPattern, DataBuffer.AccessMode.READ); } DataStorage newCompatibleEmptyStorage(boolean unresizable) { return new MappedDoubleStorage(ms.getCompatibleInstanceForNewTemporaryFile(unresizable)); } int bytesPerBufferElementLog() { return BYTES_PER_DOUBLE_LOG; } @Override final double getDouble(long index) { final DoubleBuffer db0 = db[0]; if (this.syncNecessary || !(db0 == validSingleSpecBufForThisThread && db0 != null)) { // Without synchronization, we may use db0 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. return getDoubleSync(index); } else { // single mapping mode and bank is ready return db0.get((int)index); } } private double getDoubleSync(long index) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[0]) { // assert this instanceof MappedBitStorage || index - bankPos[0] <= Integer.MAX_VALUE; i = index - bankPos[0]; } else { i = translateFailedIndex(index, false, LoadingMode.DEFAULT); } return db[0].get((int)i); } finally { lock.unlock(); } } @Override final void setDouble(long index, double value) { final DoubleBuffer db1 = db[1]; if (this.syncNecessary || !(db1 == validSingleSpecBufForThisThread && db1 != null)) { // Without synchronization, we may use db1 only if it is identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. setDoubleSync(index, value); } else { // single mapping mode and bank is ready db1.put((int)index, value); } } private void setDoubleSync(long index, double value) { final ReentrantLock lock = this.lock; lock.lock(); try { long i; if ((index & indexHighBits) == bankPos[1]) { // assert this instanceof MappedBitStorage || index - bankPos[1] <= Integer.MAX_VALUE; i = index - bankPos[1]; } else { i = translateFailedIndex(index, true, LoadingMode.DEFAULT); } db[1].put((int)i, value); } finally { lock.unlock(); } } @Override final long indexOfDouble(long lowIndex, long highIndex, double value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(lowIndex); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, bse - ofs); { incrementMappingInUseCounter(); try { DoubleBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == ofs; buf = db[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfDouble(buf, ofs, ofs + len, value); if (result != -1) { return result + lowIndex - ofs; } } finally { decrementMappingInUseCounter(); } } lowIndex += len; count -= len; for (; count > 0; lowIndex += bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { DoubleBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(lowIndex); assert translateIndexResult == 0; buf = db[0]; } finally { lock.unlock(); } long result = JBuffers.indexOfDouble(buf, 0, len, value); if (result != -1) { return result + lowIndex; } } finally { decrementMappingInUseCounter(); } } } return -1; } @Override final long lastIndexOfDouble(long lowIndex, long highIndex, double value) { if (highIndex <= lowIndex) { // necessary check: our algorithm does not work with zero-length file return -1; } final ReentrantLock lock = this.lock; // synchronization is necessary always: below we access the specBuf, // that can become null at any moment final int ofs = (int)offset(highIndex - 1); assert ofs >= 0; long count = highIndex - lowIndex; final int bse = (int)ms.bankSizeInElements; assert bse == ms.bankSizeInElements; int len = singleMapping ? (int)count : (int)Math.min(count, ofs + 1); { incrementMappingInUseCounter(); try { DoubleBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == ofs; buf = db[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfDouble(buf, ofs - len + 1, ofs + 1, value); if (result != -1) { return result + highIndex - 1 - ofs; } } finally { decrementMappingInUseCounter(); } } highIndex -= len; count -= len; for (; count > 0; highIndex -= bse, count -= bse) { len = (int)Math.min(count, bse); { incrementMappingInUseCounter(); try { DoubleBuffer buf; lock.lock(); try { long translateIndexResult = translateIndex(highIndex - 1); assert translateIndexResult == bse - 1; buf = db[0]; } finally { lock.unlock(); } long result = JBuffers.lastIndexOfDouble(buf, bse - len, bse, value); if (result != -1) { return result + highIndex - bse; } } finally { decrementMappingInUseCounter(); } } } return -1; } final void copy(long destIndex, long srcIndex) { final DoubleBuffer db0 = db[0], db1 = db[1]; if (this.syncNecessary || !(db0 == validSingleSpecBufForThisThread && db1 == db0 && db0 != null)) { // Without synchronization, we may use db0/db1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. copySync(destIndex, srcIndex); } else { // single mapping mode and banks are ready db1.put((int)destIndex, db0.get((int)srcIndex)); } } private void copySync(long destIndex, long srcIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { setDouble(destIndex, getDouble(srcIndex)); } finally { lock.unlock(); } } final void swap(long firstIndex, long secondIndex) { final DoubleBuffer db0 = db[0], db1 = db[1]; if (this.syncNecessary || !(db0 == validSingleSpecBufForThisThread && db1 == db0 && db0 != null)) { // Without synchronization, we may use db0/db1 only if they are identical to // volatile validSingleSpecBufForThisThread: that reference cannot // refer to partially initialized buffer. swapSync(firstIndex, secondIndex); } else { // single mapping mode and banks are ready int i1 = (int)firstIndex; int i2 = (int)secondIndex; double v1 = db0.get(i1); double v2 = db1.get(i2); db0.put(i1, v2); db1.put(i2, v1); } } private void swapSync(long firstIndex, long secondIndex) { final ReentrantLock lock = this.lock; lock.lock(); try { int i1 = (int)translateIndex(firstIndex); assert i1 >= 0; int i2 = (int)translateIndex(secondIndex, true); assert i2 >= 0; double v1 = db[0].get(i1); double v2 = db[1].get(i2); db[0].put(i1, v2); db[1].put(i2, v1); } finally { lock.unlock(); } } void setSpecificBuffer(int bank) { db[bank] = bh[bank] == null ? null : bh[bank].data().asDoubleBuffer(); } void clearData(long pos, long count) { fillData(pos, count, doubleZero); } void getDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; DoubleBuffer dup = ((DoubleBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.get((double[])destArray, destArrayOffset, count); } void setDataInFirstBank(Buffer buf, long firstBankOffset, Object srcArray, int srcArrayOffset, int count) { int ofs = (int)firstBankOffset; assert firstBankOffset == ofs; DoubleBuffer dup = ((DoubleBuffer)buf).duplicate(); // necessary for multithreading if !this.syncNecessary dup.position(ofs); dup.put((double[])srcArray, srcArrayOffset, count); } void fillDataInFirstBank(long destOffset, long count, Object fillerWrapper) { int ofs = (int)destOffset; int cnt = (int)count; assert destOffset == ofs; assert count == cnt; JBuffers.fillDoubleBuffer(db[0], ofs, cnt, ((Double)fillerWrapper).doubleValue()); } void copyFirstBank(MappedStorage src, long srcOffset, long destOffset, long count, boolean reverseOrder, boolean copyToSecondBank) { int srcOfs = (int)srcOffset; int destOfs = (int)destOffset; int cnt = (int)count; assert srcOffset == srcOfs; assert destOffset == destOfs; assert count == cnt; JBuffers.copyDoubleBuffer(db[copyToSecondBank ? 1 : 0], destOfs, ((MappedDoubleStorage)src).db[0], srcOfs, cnt, reverseOrder); } void swapFirstBank(MappedStorage another, long anotherOffset, long thisOffset, long count, boolean swapWithSecondBank) { int anotherOfs = (int)anotherOffset; int thisOfs = (int)thisOffset; int cnt = (int)count; assert anotherOffset == anotherOfs; assert thisOffset == thisOfs; assert count == cnt; JBuffers.swapDoubleBuffer(((MappedDoubleStorage)another).db[0], anotherOfs, db[swapWithSecondBank ? 1 : 0], thisOfs, cnt); } void minDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.minDoubleArrayAndBuffer((double[])destArray, destArrayOffset, (DoubleBuffer)buf, (int)firstBankOffset, count); } void maxDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count) { JBuffers.maxDoubleArrayAndBuffer((double[])destArray, destArrayOffset, (DoubleBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, int[] destArray, int destArrayOffset, int count) { JBuffers.addDoubleBufferToArray(destArray, destArrayOffset, (DoubleBuffer)buf, (int)firstBankOffset, count); } void addDataFromFirstBank(Buffer buf, long firstBankOffset, double[] destArray, int destArrayOffset, int count, double mult) { JBuffers.addDoubleBufferToArray(destArray, destArrayOffset, (DoubleBuffer)buf, (int)firstBankOffset, count, mult); } void subtractDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.subtractDoubleBufferFromArray((double[])destArray, destArrayOffset, (DoubleBuffer)buf, (int)firstBankOffset, count); } void absDiffDataFromFirstBank(Buffer buf, long firstBankOffset, Object destArray, int destArrayOffset, int count, boolean truncateOverflows) { JBuffers.absDiffOfDoubleArrayAndBuffer((double[])destArray, destArrayOffset, (DoubleBuffer)buf, (int)firstBankOffset, count); } @Override void actualizeLazyFillingBank(ByteBuffer bankBB, final long elementIndex) { assert lock.isHeldByCurrentThread() : "actualizeLazyFillingBank is called from non-synchronized code"; assert (elementIndex & 7) == 0; // index is specified in elements and is even enough long length; if (ms.lazyFillingPattern == null || (length = ms.lazyFillingPattern.length()) <= elementIndex) { actualizeLazyZeroFillingBank(bankBB, 0, bankBB.limit()); } else { DoubleBuffer bankSpecBuf = bankBB.asDoubleBuffer(); int count = bankSpecBuf.limit(); if (count > length - elementIndex) { count = (int)(length - elementIndex); actualizeLazyZeroFillingBank(bankBB, count << BYTES_PER_DOUBLE_LOG, bankBB.limit()); } for (long ofs = 0; ofs < count; ) { dbuf.map(elementIndex + ofs, count - ofs); int len = dbuf.cnt(); assert len > 0; bankSpecBuf.put(dbuf.data(), dbuf.from(), len); // this call shifts bankSpecBuf.position() ofs += len; } } } } /*Repeat.AutoGeneratedEnd*/ }