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

jetbrains.exodus.entitystore.iterate.EntityIdArrayCachedInstanceIterableFactory Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2010 - 2018 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jetbrains.exodus.entitystore.iterate;

import jetbrains.exodus.core.dataStructures.IntArrayList;
import jetbrains.exodus.core.dataStructures.LongArrayList;
import jetbrains.exodus.entitystore.EntityId;
import jetbrains.exodus.entitystore.PersistentStoreTransaction;
import jetbrains.exodus.entitystore.iterate.cached.*;
import jetbrains.exodus.entitystore.util.ImmutableSingleTypeEntityIdBitSet;
import jetbrains.exodus.entitystore.util.ImmutableSingleTypeEntityIdCollection;
import jetbrains.exodus.entitystore.util.IntArrayListSpinAllocator;
import jetbrains.exodus.entitystore.util.LongArrayListSpinAllocator;
import org.jetbrains.annotations.NotNull;

import static jetbrains.exodus.entitystore.iterate.EntityIterableBase.NULL_TYPE_ID;

public class EntityIdArrayCachedInstanceIterableFactory {
    public static final int MAX_COMPRESSED_SET_LOAD_FACTOR = 64;

    public static CachedInstanceIterable createInstance(@NotNull final PersistentStoreTransaction txn,
                                                        @NotNull final EntityIterableBase source) {
        return createInstance(txn, source, (EntityIteratorBase) source.getIteratorImpl(txn));
    }

    public static CachedInstanceIterable createInstance(@NotNull final PersistentStoreTransaction txn,
                                                        @NotNull final EntityIterableBase source,
                                                        @NotNull final EntityIteratorBase it) {
        try {
            if (!it.hasNext()) {
                return new EmptyCachedInstanceIterable(txn, source);
            } else {
                final IntArrayList typeIds = IntArrayListSpinAllocator.alloc();
                final LongArrayList localIds = LongArrayListSpinAllocator.alloc();
                long min;
                long max;
                try {
                    boolean onlyOneTypeId = true;
                    boolean localSorted = true;
                    if (source.isSortedById()) {
                        int lastTypeId = -1;
                        EntityId id = it.nextId();
                        while (true) {
                            final int nextTypeId;
                            if (id == null) {
                                nextTypeId = NULL_TYPE_ID;
                                localIds.add(0);
                            } else {
                                nextTypeId = id.getTypeId();
                                localIds.add(id.getLocalId());
                            }
                            if (nextTypeId != lastTypeId) {
                                if (lastTypeId != -1) {
                                    onlyOneTypeId = false;
                                    typeIds.add(localIds.size() - 1); // add upper boundary for previous
                                }
                                typeIds.add(nextTypeId);
                                lastTypeId = nextTypeId;
                            }
                            if (!it.hasNext()) {
                                if (!onlyOneTypeId) {
                                    typeIds.add(localIds.size()); // add boundary for last
                                }
                                break;
                            }
                            id = it.nextId();
                        }
                        min = localIds.get(0);
                        max = localIds.get(localIds.size() - 1);
                    } else {
                        int lastTypeId = -1;
                        long lastLocalId = -1;
                        min = Long.MAX_VALUE;
                        max = Long.MIN_VALUE;
                        boolean compact = true;
                        EntityId id = it.nextId();
                        while (true) {
                            final int nextTypeId;
                            final long nextLocalId;
                            if (id == null) {
                                nextTypeId = NULL_TYPE_ID;
                                nextLocalId = 0;
                            } else {
                                nextTypeId = id.getTypeId();
                                nextLocalId = id.getLocalId();
                            }
                            if (localSorted) {
                                if (lastTypeId > nextTypeId || lastTypeId == nextTypeId && lastLocalId > nextLocalId) {
                                    final int length;
                                    if (nextTypeId == NULL_TYPE_ID && (length = localIds.size()) <= 1) {
                                        if (length == 1) { // direct conversion
                                            onlyOneTypeId = false;
                                            localSorted = false;
                                            compact = false;
                                        } else {
                                            typeIds.add(NULL_TYPE_ID);
                                        }
                                        lastLocalId = nextLocalId;
                                    } else {
                                        localSorted = false;
                                    }
                                } else {
                                    lastLocalId = nextLocalId;
                                }
                            }
                            localIds.add(nextLocalId);
                            if (nextLocalId > max) {
                                max = nextLocalId;
                            }
                            if (nextLocalId < min) {
                                min = nextLocalId;
                            }
                            if (compact) {
                                if (localSorted) {
                                    if (nextTypeId > lastTypeId) {
                                        if (lastTypeId != -1) {
                                            onlyOneTypeId = false;
                                            typeIds.add(localIds.size() - 1); // add upper boundary for previous
                                        }
                                        typeIds.add(nextTypeId);
                                    }
                                    lastTypeId = nextTypeId;
                                } else {
                                    if (typeIds.size() > 1 || nextTypeId != lastTypeId) {
                                        onlyOneTypeId = false;
                                        compact = false;
                                        addNextTypeId(nextTypeId, typeIds, localIds);
                                    }
                                }
                            } else {
                                typeIds.add(nextTypeId);
                            }
                            if (!it.hasNext()) {
                                if (compact && !onlyOneTypeId) {
                                    typeIds.add(localIds.size()); // add boundary for last
                                }
                                break;
                            }
                            id = it.nextId();
                        }
                    }
                    if (localSorted) {
                        if (onlyOneTypeId) {
                            return makeSingleTypeSortedIterable(txn, source, it, typeIds, localIds, min, max);
                        } else {
                            return new MultiTypeSortedEntityIdArrayCachedInstanceIterable(
                                txn, source, typeIds.toArray(), localIds.toArray(), it.toSet()
                            );
                        }
                    } else {
                        if (onlyOneTypeId) {
                            return makeSingleTypeUnsortedIterable(txn, source, it, typeIds, localIds, min, max);
                        } else {
                            return new MultiTypeUnsortedEntityIdArrayCachedInstanceIterable(
                                txn, source, typeIds.toArray(), localIds.toArray(), it.toSet()
                            );
                        }
                    }
                } finally {
                    LongArrayListSpinAllocator.dispose(localIds);
                    IntArrayListSpinAllocator.dispose(typeIds);
                }
            }
        } finally {
            it.disposeIfShouldBe();
        }
    }

    @NotNull
    private static CachedInstanceIterable makeSingleTypeSortedIterable(
        @NotNull PersistentStoreTransaction txn, @NotNull EntityIterableBase source, @NotNull EntityIteratorBase it,
        IntArrayList typeIds, LongArrayList localIds, long min, long max
    ) {
        final int typeId = typeIds.get(0);
        if (typeId != NULL_TYPE_ID) {
            final int length = localIds.size();
            if (length > 1) {
                if (min >= 0) {
                    final long range = max - min + 1;
                    if (range < Integer.MAX_VALUE
                        && range <= ((long) MAX_COMPRESSED_SET_LOAD_FACTOR * length)) {
                        final SortedEntityIdSet set = new ImmutableSingleTypeEntityIdBitSet(
                            typeId, localIds.getInstantArray(), length
                        );
                        // if there are no duplicates in localIds
                        if (set.count() == length) {
                            return new SingleTypeSortedSetEntityIdCachedInstanceIterable(txn, source, typeId, set);
                        }
                    }
                }
            }
        }
        return new SingleTypeSortedEntityIdArrayCachedInstanceIterable(txn, source, typeId, localIds.toArray(), it.toSet());
    }

    @NotNull
    private static CachedInstanceIterable makeSingleTypeUnsortedIterable(
        @NotNull PersistentStoreTransaction txn, @NotNull EntityIterableBase source, @NotNull EntityIteratorBase it,
        IntArrayList typeIds, LongArrayList localIds, long min, long max
    ) {
        return new SingleTypeUnsortedEntityIdArrayCachedInstanceIterable(
            txn, source, typeIds.get(0), localIds.toArray(), it.toSet(), min, max
        );
    }

    @NotNull
    public static OrderedEntityIdCollection makeIdCollection(int typeId, long[] localIds) {
        final int length = localIds.length;
        if (length > 1) {
            final long min = localIds[0];
            if (min >= 0) {
                final long range = localIds[length - 1] - min + 1;
                if (range < Integer.MAX_VALUE
                    && range <= ((long) MAX_COMPRESSED_SET_LOAD_FACTOR * length)) {
                    return new ImmutableSingleTypeEntityIdBitSet(typeId, localIds, length);
                }
            }
        }
        return new ImmutableSingleTypeEntityIdCollection(typeId, localIds);
    }

    private static void addNextTypeId(final int nextTypeId, IntArrayList typeIds, LongArrayList localIds) {
        final int[] typeIdsCopy = typeIds.toArray();
        int length = typeIds.size();
        final int maxBound = localIds.size() - 1;
        typeIds.ensureCapacity(maxBound);
        typeIds.clear();
        int i = 0;
        int j = 0;
        int currentBound = 0;
        int typeId = 0;
        while (j < maxBound) {
            ++j;
            if (j > currentBound) {
                typeId = typeIdsCopy[i];
                ++i;
                currentBound = i < length ? typeIdsCopy[i] : maxBound;
                ++i;
            }
            typeIds.add(typeId);
        }
        typeIds.add(nextTypeId);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy