| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.jackrabbit.oak.segment.file.tar.index; |
| |
| import static com.google.common.base.Preconditions.checkElementIndex; |
| import static com.google.common.collect.Sets.newHashSetWithExpectedSize; |
| |
| import java.nio.ByteBuffer; |
| import java.util.Set; |
| import java.util.UUID; |
| |
| class IndexV1 implements Index { |
| |
| static final int FOOTER_SIZE = 16; |
| |
| private final ByteBuffer entries; |
| |
| IndexV1(ByteBuffer entries) { |
| this.entries = entries; |
| } |
| |
| @Override |
| public Set<UUID> getUUIDs() { |
| Set<UUID> uuids = newHashSetWithExpectedSize(entries.remaining() / IndexEntryV1.SIZE); |
| int position = entries.position(); |
| while (position < entries.limit()) { |
| long msb = entries.getLong(position); |
| long lsb = entries.getLong(position + 8); |
| uuids.add(new UUID(msb, lsb)); |
| position += IndexEntryV1.SIZE; |
| } |
| return uuids; |
| } |
| |
| @Override |
| public int findEntry(long msb, long lsb) { |
| // The segment identifiers are randomly generated with uniform |
| // distribution, so we can use interpolation search to find the |
| // matching entry in the index. The average runtime is O(log log n). |
| |
| int lowIndex = 0; |
| int highIndex = entries.remaining() / IndexEntryV1.SIZE - 1; |
| float lowValue = Long.MIN_VALUE; |
| float highValue = Long.MAX_VALUE; |
| float targetValue = msb; |
| |
| while (lowIndex <= highIndex) { |
| int guessIndex = lowIndex + Math.round( |
| (highIndex - lowIndex) |
| * (targetValue - lowValue) |
| / (highValue - lowValue)); |
| int position = entries.position() + guessIndex * IndexEntryV1.SIZE; |
| long m = entries.getLong(position); |
| if (msb < m) { |
| highIndex = guessIndex - 1; |
| highValue = m; |
| } else if (msb > m) { |
| lowIndex = guessIndex + 1; |
| lowValue = m; |
| } else { |
| // getting close... |
| long l = entries.getLong(position + 8); |
| if (lsb < l) { |
| highIndex = guessIndex - 1; |
| highValue = m; |
| } else if (lsb > l) { |
| lowIndex = guessIndex + 1; |
| lowValue = m; |
| } else { |
| return position / IndexEntryV1.SIZE; |
| } |
| } |
| } |
| |
| return -1; |
| } |
| |
| @Override |
| public int size() { |
| return entries.remaining() + FOOTER_SIZE; |
| } |
| |
| @Override |
| public int count() { |
| return entries.remaining() / IndexEntryV1.SIZE; |
| } |
| |
| @Override |
| public IndexEntryV1 entry(int i) { |
| return new IndexEntryV1(entries, checkElementIndex(i, count()) * IndexEntryV1.SIZE); |
| } |
| |
| } |