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

io.datarouter.filesystem.snapshot.block.branch.BranchBlockV1 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.branch;


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.bytes.codec.longcodec.RawLongCodec;
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:
 * - level
 * - num keys
 * - key endings
 * - keys
 * - child block ids
 */
public class BranchBlockV1 implements BranchBlock{

	public static final String FORMAT = "branchV1";

	private static final RawIntCodec RAW_INT_CODEC = RawIntCodec.INSTANCE;
	private static final RawLongCodec RAW_LONG_CODEC = RawLongCodec.INSTANCE;

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

	private final byte[] bytes;
	private final int level;
	private final int firstChildBlockId;
	private final int numRecords;
	private final int recordIdsSectionOffset;
	private final int endingSectionOffset;
	private final int keySectionOffset;
	private final int childFileIdsSectionOffset;
	private final int childEndingsSectionOffset;

	public BranchBlockV1(byte[] bytes){
		this.bytes = bytes;
		var reader = new ByteReader(bytes);
		level = reader.varInt();
		firstChildBlockId = reader.varInt();
		numRecords = reader.varInt();
		recordIdsSectionOffset = reader.position();
		reader.skipLongs(numRecords);
		endingSectionOffset = reader.position();
		reader.skipInts(numRecords);
		keySectionOffset = reader.position();
		reader.skip(keyEnding(numRecords - 1));
		int numEndings = numRecords + 1;// because it includes the previous block's final ending
		childFileIdsSectionOffset = reader.position();
		reader.skipInts(numEndings);
		childEndingsSectionOffset = reader.position();
		reader.skipInts(numEndings);
		reader.assertFinished();
	}

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

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

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

	@Override
	public long recordId(int index){
		int bytesOffset = recordIdsSectionOffset + index * 8;
		return RAW_LONG_CODEC.decode(bytes, bytesOffset);
	}

	@Override
	public Bytes key(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 Scanner keys(){
		return Scanner.iterate(0, i -> i + 1)
				.limit(numRecords)
				.map(this::key);
	}

	@Override
	public int childBlock(int index){
		return firstChildBlockId + index;
	}

	@Override
	public Scanner childBlockIds(){
		return Scanner.iterate(firstChildBlockId, i -> i + 1)
				.limit(numRecords);
	}

	@Override
	public BlockKey childBranchBlockKey(SnapshotKey snapshotKey, int childBlockId){
		int childLevel = level - 1;
		int fileId = childBlockFileId(childBlockId);
		int start = isFirstBlockInFile(childBlockId)
				? 0
				: childBlockEnding(childBlockId - 1);
		int end = childBlockEnding(childBlockId);
		int length = end - start;
		return BlockKey.branch(snapshotKey, childLevel, childBlockId, fileId, start, length);
	}

	@Override
	public BlockKey leafBlockKey(SnapshotKey snapshotKey, int leafBlockId){
		int fileId = childBlockFileId(leafBlockId);
		int start = isFirstBlockInFile(leafBlockId)
				? 0
				: childBlockEnding(leafBlockId - 1);
		int end = childBlockEnding(leafBlockId);
		int length = end - start;
		return BlockKey.leaf(snapshotKey, leafBlockId, fileId, start, length);
	}

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

	private boolean isFirstBlockInFile(int childBlockId){
		if(childBlockId == 0){
			return true;
		}
		int previousFileId = childBlockFileId(childBlockId - 1);
		int fileId = childBlockFileId(childBlockId);
		return previousFileId != fileId;
	}

	private int childBlockFileId(int childBlockId){
		int childBlockIndex = childBlockId - firstChildBlockId;
		int indexExcludingPreviousEndings = childBlockIndex + 1;
		int endingOffset = 4 * indexExcludingPreviousEndings;
		return RAW_INT_CODEC.decode(bytes, childFileIdsSectionOffset + endingOffset);
	}

	private int childBlockEnding(int childBlockId){
		int childBlockIndex = childBlockId - firstChildBlockId;
		int indexExcludingPreviousEndings = childBlockIndex + 1;
		int endingOffset = 4 * indexExcludingPreviousEndings;
		return RAW_INT_CODEC.decode(bytes, childEndingsSectionOffset + endingOffset);
	}

	@Override
	public String toDetailedString(){
		List lines = new ArrayList<>();
		lines.add("                   level " + level);
		lines.add("previousBlockFinalEnding " + childBlockEnding(-1));
		lines.add("       firstChildBlockId " + firstChildBlockId);
		lines.add("              numRecords " + numRecords());
		lines.add("               recordIds " + Scanner.iterate(0, i -> i + 1)
				.limit(numRecords)
				.map(this::recordId)
				.map(Object::toString)
				.collect(Collectors.joining(",", "[", "]")));
		lines.add("              keyEndings " + Scanner.iterate(0, i -> i + 1)
				.limit(numRecords)
				.map(this::keyEnding)
				.map(Object::toString)
				.collect(Collectors.joining(",", "[", "]")));
		lines.add("                childIds " + Scanner.iterate(0, i -> i + 1)
				.limit(numRecords)
				.map(this::childBlock)
				.map(Object::toString)
				.collect(Collectors.joining(",", "[", "]")));
		lines.add("            childEndings " + Scanner.iterate(0, i -> i + 1)
				.limit(numRecords)
				.map(this::childBlockEnding)
				.map(Object::toString)
				.collect(Collectors.joining(",", "[", "]")));
		return "\n" + lines.stream()
				.map(Object::toString)
				.collect(Collectors.joining("\n"));
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy