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

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

The 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.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import io.datarouter.bytes.ByteReader;
import io.datarouter.bytes.Bytes;
import io.datarouter.bytes.codec.intcodec.RawIntCodec;
import io.datarouter.filesystem.snapshot.block.BlockKey;
import io.datarouter.filesystem.snapshot.block.BlockSizeCalculator;
import io.datarouter.filesystem.snapshot.key.SnapshotKey;
import io.datarouter.scanner.Scanner;
/**
 * Sections:
 * - num keys
 * - key endings: relative to the start of the section
 * - keys
 * - value block ids
 * - value block indexes
 */
public class LeafBlockV1 implements LeafBlock{

	public static final String FORMAT = "keyV1";

	private static final RawIntCodec RAW_INT_CODEC = RawIntCodec.INSTANCE;

	private static final int HEAP_SIZE_OVERHEAD = new BlockSizeCalculator()
			.addObjectHeaders(1)
			.addArrays(1)
			.addLongs(1)
			.addInts(7)
			.calculate();

	private final byte[] bytes;

	private final long firstRecordId;
	private final int numRecords;
	private final int numColumns;

	//keys
	private final int keyEndingSectionOffset;
	private final int keySectionOffset;

	//values
	private final int valueEndingSectionOffset;
	private final int valueSectionOffset;

	//value block references
	private final int[] firstValueBlockId;
	private final int[] valueFileIdsSectionOffset;
	private final int[] valueBlockEndingsSectionOffset;
	private final int[] firstValueIndex;
	private final int[] numValueBlockOffsets;
	private final int[] valueBlockOffsetSectionOffset;


	public LeafBlockV1(byte[] bytes){
		this.bytes = bytes;
		var reader = new ByteReader(bytes);

		firstRecordId = reader.varLong();
		numRecords = reader.varInt();
		numColumns = reader.varInt();

		//keys
		keyEndingSectionOffset = reader.position();
		reader.skipInts(numRecords);
		keySectionOffset = reader.position();
		reader.skip(keyEnding(numRecords - 1));

		//values
		valueEndingSectionOffset = reader.position();
		reader.skipInts(numRecords);
		valueSectionOffset = reader.position();
		reader.skip(valueEnding(numRecords - 1));

		//value block references
		firstValueBlockId = new int[numColumns];
		valueFileIdsSectionOffset = new int[numColumns];
		valueBlockEndingsSectionOffset = new int[numColumns];
		firstValueIndex = new int[numColumns];
		numValueBlockOffsets = new int[numColumns];
		valueBlockOffsetSectionOffset = new int[numColumns];
		for(int column = 0; column < numColumns; ++column){
			firstValueBlockId[column] = reader.varInt();

			//fileIds
			int numFileIds = reader.varInt();
			valueFileIdsSectionOffset[column] = reader.position();
			reader.skipInts(numFileIds);

			//endings
			int numValueBlockEndings = reader.varInt();
			valueBlockEndingsSectionOffset[column] = reader.position();
			reader.skipInts(numValueBlockEndings);

			//offsets
			firstValueIndex[column] = reader.varInt();
			numValueBlockOffsets[column] = reader.varInt();
			valueBlockOffsetSectionOffset[column] = reader.position();
			reader.skipInts(numValueBlockOffsets[column]);
		}

		reader.assertFinished();
	}

	@Override
	public String toString(){
		return "LeafBlockV1 [numKeys=" + numRecords + ", keyOffsetSectionOffset=" + keyEndingSectionOffset
				+ ", keySectionOffset=" + keySectionOffset + "]";
	}

	@Override
	public int heapSize(){
		return HEAP_SIZE_OVERHEAD + BlockSizeCalculator.pad(bytes.length);
	}

	@Override
	public long recordId(int index){
		return firstRecordId + index;
	}

	@Override
	public int numRecords(){
		return numRecords;
	}

	@Override
	public Bytes blockKey(int index){
		int start = index == 0 ? 0 : keyEnding(index - 1);
		int end = keyEnding(index);
		int length = end - start;
		return new Bytes(bytes, keySectionOffset + start, length);
	}

	@Override
	public Bytes blockValue(int index){
		int start = index == 0 ? 0 : valueEnding(index - 1);
		int end = valueEnding(index);
		int length = end - start;
		return new Bytes(bytes, valueSectionOffset + start, length);
	}

	@Override
	public int firstValueBlockId(int column){
		return firstValueBlockId[column];
	}

	@Override
	public int numValueBlocks(int column){
		return numValueBlockOffsets[column];
	}

	private int fileId(int column, int valueBlockId){
		int valueBlockIndex = valueBlockId - firstValueBlockId[column] + 1;
		int cursor = valueFileIdsSectionOffset[column] + 4 * valueBlockIndex;
		return RAW_INT_CODEC.decode(bytes, cursor);
	}

	@Override
	public int valueBlockEnding(int column, int valueBlockId){
		int valueBlockIndex = valueBlockId - firstValueBlockId[column] + 1;
		int cursor = valueBlockEndingsSectionOffset[column] + 4 * valueBlockIndex;
		return RAW_INT_CODEC.decode(bytes, cursor);
	}

	@Override
	public BlockKey valueBlockKey(SnapshotKey snapshotKey, int column, int valueBlockId){
		int fileId = fileId(column, valueBlockId);
		int start = isFirstBlockInFile(column, valueBlockId)
				? 0
				: valueBlockEnding(column, valueBlockId - 1);
		int end = valueBlockEnding(column, valueBlockId);
		int length = end - start;
		return BlockKey.value(snapshotKey, column, valueBlockId, fileId, start, length);
	}

	private boolean isFirstBlockInFile(int column, int valueBlockId){
		if(valueBlockId == 0){
			return true;
		}
		int previousFileId = fileId(column, valueBlockId - 1);
		int fileId = fileId(column, valueBlockId);
		return previousFileId != fileId;
	}

	@Override
	public int firstValueIndex(int column){
		return firstValueIndex[column];
	}

	@Override
	public int valueBlockOffset(int column, int valueBlockOffsetIndex){
		int bytesOffset = valueBlockOffsetSectionOffset[column] + (4 * valueBlockOffsetIndex);
		return RAW_INT_CODEC.decode(bytes, bytesOffset);
	}

	private int keyEnding(int index){
		int endingOffset = 4 * index;
		return RAW_INT_CODEC.decode(bytes, keyEndingSectionOffset + endingOffset);
	}

	private int valueEnding(int index){
		int endingOffset = 4 * index;
		return RAW_INT_CODEC.decode(bytes, valueEndingSectionOffset + endingOffset);
	}

	public String toDetailedString(){
		List lines = new ArrayList<>();
		lines.add("  numRecords" + numRecords);
		lines.add("  keyEndings" + Scanner.iterate(0, i -> i + 1)
				.limit(numRecords)
				.map(this::keyEnding)
				.map(Object::toString)
				.collect(Collectors.joining(",", "[", "]")));
		lines.add("valueEndings" + Scanner.iterate(0, i -> i + 1)
				.limit(numRecords)
				.map(this::valueEnding)
				.map(Object::toString)
				.collect(Collectors.joining(",", "[", "]")));
		return "\n" + lines.stream()
				.map(Object::toString)
				.collect(Collectors.joining("\n"));
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy