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

io.datarouter.filesystem.snapshot.block.leaf.LeafBlock Maven / Gradle / Ivy

There is a newer version: 0.0.126
Show newest version
/*
 * Copyright © 2009 HotPads ([email protected])
 *
 * 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 io.datarouter.filesystem.snapshot.block.leaf;

import java.util.Optional;

import io.datarouter.bytes.Bytes;
import io.datarouter.filesystem.snapshot.block.Block;
import io.datarouter.filesystem.snapshot.block.BlockKey;
import io.datarouter.filesystem.snapshot.key.SnapshotKey;
import io.datarouter.filesystem.snapshot.reader.record.SnapshotLeafRecord;
import io.datarouter.filesystem.snapshot.reader.record.SnapshotLeafSearchResult;
import io.datarouter.scanner.Scanner;

public interface LeafBlock extends Block{

	long recordId(int index);

	/**
	 * @return  Offset from first key in snapshot
	 */
	default long firstRecordId(){
		return recordId(0);
	}

	/**
	 * @return  Number of keys in the block
	 */
	int numRecords();

	/*----------------search ------------------*/

	default Optional findRecordId(byte[] searchKey){
		return findRecordIndex(searchKey)
				.map(this::recordId);
	}

	default Optional findRecordIndex(byte[] searchKey){
		int index = insertionIndex(searchKey);
		return index < 0 ? Optional.empty() : Optional.of(index);
	}

	default SnapshotLeafSearchResult search(byte[] searchKey){
		int index = insertionIndex(searchKey);
		boolean exactMatch = true;
		if(index < 0){
			index = -index - 1;
			exactMatch = false;
		}
		long recordId = recordId(index);
		return new SnapshotLeafSearchResult(recordId, exactMatch);
	}

	/**
	 * @return index of first match if present, otherwise -(insertionPoint + 1)
	 */
	default int insertionIndex(byte[] searchKey){
		var searchKeyRange = new Bytes(searchKey);
		int low = 0;
		int mid = 0;
		int high = numRecords() - 1;
		int diff = 0;
		int lastMatch = -1;
		while(low <= high){
			mid = (low + high) >>> 1;
			Bytes midVal = blockKey(mid);
			diff = midVal.compareTo(searchKeyRange);
			if(diff < 0){
				low = mid + 1;
			}else if(diff == 0){
				lastMatch = mid;
				high = mid - 1;
			}else{
				high = mid - 1;
			}
		}
		if(lastMatch >= 0){
			return lastMatch;
		}else if(diff < 0){
			int insertionPoint = mid + 1;
			return -(insertionPoint + 1);
		}else{
			int insertionPoint = mid;
			return -(insertionPoint + 1);
		}
	}

	/*--------------- keys --------------------*/

	/**
	 * @return  Nth key in the block
	 */
	Bytes blockKey(int index);

	/**
	 * @return  Nth key in the snapshot
	 */
	default Bytes snapshotKey(long recordId){
		long blockRecordIndex = recordId - firstRecordId();
		return blockKey((int)blockRecordIndex);
	}

	/**
	 * The Scanner may reuse the same ByteRange for all keys, meaning the caller should clone the ByteRanges if they
	 * need to be long-lived.
	 *
	 * @return  Scanner of key transient ByteRanges
	 */
	default Scanner keys(){
		return Scanner.iterate(0, i -> i + 1)
				.limit(numRecords())
				.map(this::blockKey);
	}

	default Scanner keyCopies(){
		return keys()
				.map(Bytes::toArray);
	}

	/*------------------- values --------------------*/

	/**
	 * @return  Nth value in the block
	 */
	Bytes blockValue(int index);

	/**
	 * @return  Nth key in the snapshot
	 */
	default Bytes snapshotValue(long recordId){
		int index = (int)(recordId - firstRecordId());
		return blockValue(index);
	}

	default Scanner values(){
		return Scanner.iterate(0, i -> i + 1)
				.limit(numRecords())
				.map(this::blockValue);
	}

	default Scanner valueCopies(){
		return values()
				.map(Bytes::toArray);
	}

	/*------------------- key + value --------------------*/

	default SnapshotLeafRecord snapshotLeafRecord(long recordId){
		byte[] key = snapshotKey(recordId).toArray();
		byte[] value = snapshotValue(recordId).toArray();
		return new SnapshotLeafRecord(recordId, key, value);
	}

	default Scanner leafRecords(){
		return Scanner.iterate(firstRecordId(), i -> i + 1)
				.limit(numRecords())
				.map(this::snapshotLeafRecord);
	}

	/*------------------- column values --------------------*/

	public static class ValueLocation{

		public final int valueBlockId;
		public final int valueIndex;

		public ValueLocation(int valueBlockId, int valueIndex){
			this.valueBlockId = valueBlockId;
			this.valueIndex = valueIndex;
		}

	}

	default ValueLocation getValueBlock(int column, long recordId){
		int index = (int)(recordId - firstRecordId());
		int valueBlockOffset = valueBlockOffsetForKey(column, index);
		int valueBlockId = firstValueBlockId(column) + valueBlockOffset;
		int valueIndex = valueIndex(column, valueBlockOffset, index);
		return new ValueLocation(valueBlockId, valueIndex);
	}

	default Optional findValueBlock(int column, byte[] searchKey){
		return findRecordIndex(searchKey)
				.map(index -> {
					int valueBlockOffset = valueBlockOffsetForKey(column, index);
					int valueBlockId = firstValueBlockId(column) + valueBlockOffset;
					int valueIndex = valueIndex(column, valueBlockOffset, index);
					return new ValueLocation(valueBlockId, valueIndex);
				});
	}

	/**
	 * @return  First ValueBlock known to this LeafBlock
	 */
	int firstValueBlockId(int column);

	/**
	 * @return  Number of ValueBlocks referenced by this LeafBlock
	 */
	int numValueBlocks(int column);

	default Scanner valueBlockIds(int column){
		return Scanner.iterate(firstValueBlockId(column), i -> i + 1)
				.limit(numValueBlocks(column));
	}

	int valueBlockEnding(int column, int valueBlockId);

	BlockKey valueBlockKey(SnapshotKey snapshotKey, int column, int valueBlockId);

	/**
	 * @return  First index into first ValueBlock known to this LeafBlock
	 */
	int firstValueIndex(int column);

	/**
	 * @param valueBlockOffsetIndex offset into the list of valueBlockOffsets.  Passing 0 should always return 0.
	 * @return recordIndex at the requested offset
	 */
	int valueBlockOffset(int column, int valueBlockOffsetIndex);

	/**
	 * @param index  Record index
	 * @return  Offset of ValueBlock from firstValueBlock
	 */
	default int valueBlockOffsetForKey(int column, int index){
		int valueBlockOffsetIndex = 0;
		for(int i = 1; i <= numValueBlocks(column) - 1; ++i){
			if(valueBlockOffset(column, i) <= index){
				++valueBlockOffsetIndex;
			}
		}
		return valueBlockOffsetIndex;
	}

	/**
	 * @param index  Key index
	 * @return  The index of the value in the value block (not the byte offset)
	 */
	default int valueIndex(int column, int valueBlockOffsetForKey, int index){
		int valueBlockOffset = valueBlockOffsetForKey;
		if(valueBlockOffset == 0){
			return firstValueIndex(column) + index;
		}
		int previousValueBlockOffset = valueBlockOffset(column, valueBlockOffset);
		return index - previousValueBlockOffset;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy