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

de.huxhorn.sulky.codec.filebuffer.SparseDataStrategy Maven / Gradle / Ivy

/*
 * sulky-modules - several general-purpose modules.
 * Copyright (C) 2007-2011 Joern Huxhorn
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */

/*
 * Copyright 2007-2011 Joern Huxhorn
 *
 * 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 de.huxhorn.sulky.codec.filebuffer;

import de.huxhorn.sulky.codec.Codec;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.List;

public class SparseDataStrategy
	implements DataStrategy
{
	/**
	 * The size of the data size, i.e. an int.
	 */
	public static final long DATA_LENGTH_SIZE = 4;

	/**
	 * The size of index, i.e. a long.
	 */
	public static final long INDEX_SIZE = 8;

	private boolean supportingOverwrite;

	public SparseDataStrategy()
	{
		this(true);
	}

	public SparseDataStrategy(boolean supportingOverwrite)
	{
		this.supportingOverwrite = supportingOverwrite;
	}

	public boolean isSupportingOverwrite()
	{
		return supportingOverwrite;
	}

	public void setSupportingOverwrite(boolean supportingOverwrite)
	{
		this.supportingOverwrite = supportingOverwrite;
	}

	@Override
	public void add(E element,
	                RandomAccessFile indexFile,
	                RandomAccessFile dataFile,
	                Codec codec,
	                IndexStrategy indexStrategy)
		throws IOException
	{
		long elementsCount = indexStrategy.getSize(indexFile);

		long offset = dataFile.length();

		internalWriteElement(dataFile, offset, elementsCount, element, codec);

		indexStrategy.setOffset(indexFile, elementsCount, offset);
	}

	@Override
	public void addAll(List elements,
	                   RandomAccessFile indexFile,
	                   RandomAccessFile dataFile,
	                   Codec codec,
	                   IndexStrategy indexStrategy)
		throws IOException
	{
		if(elements != null)
		{
			int newElementCount = elements.size();
			if(newElementCount > 0)
			{
				long elementsCount = indexStrategy.getSize(indexFile);

				long offset = dataFile.length();

				long[] offsets = new long[newElementCount];
				int index = 0;
				for(E element : elements)
				{
					offsets[index] = offset;
					offset = offset + internalWriteElement(dataFile, offset, elementsCount + index, element, codec) + DATA_LENGTH_SIZE + INDEX_SIZE;
					index++;
				}

				index = 0;
				for(long curOffset : offsets)
				{
					indexStrategy.setOffset(indexFile, elementsCount + index, curOffset);
					index++;
				}
			}

		}
	}

	@Override
	public boolean set(long index, E element, RandomAccessFile indexFile, RandomAccessFile dataFile, Codec codec, IndexStrategy indexStrategy)
		throws IOException, UnsupportedOperationException
	{
		long offset = indexStrategy.getOffset(indexFile, index);
		if(!supportingOverwrite && offset >= 0)
		{
			return false;
		}
		if(element != null)
		{
			offset = dataFile.length();
			internalWriteElement(dataFile, offset, index, element, codec);

			indexStrategy.setOffset(indexFile, index, offset);
			return true;
		}
		else
		{
			// set offset to -1 to signal a null value.
			indexStrategy.setOffset(indexFile, index, -1);
			return true;
		}
	}

	@Override
	public boolean isSetSupported()
	{
		return true;
	}

	@Override
	public E get(long index,
	             RandomAccessFile indexFile,
	             RandomAccessFile dataFile,
	             Codec codec,
	             IndexStrategy indexStrategy)
		throws IOException, ClassNotFoundException
	{
		long elementsCount = indexStrategy.getSize(indexFile);
		if(index >= 0 && index < elementsCount)
		{
			long offset = indexStrategy.getOffset(indexFile, index);
			if(offset < 0)
			{
				return null;
			}

			return internalReadElement(dataFile, offset, codec);
		}
		return null;
	}


	private int internalWriteElement(RandomAccessFile dataFile, long offset, long index, E element, Codec codec)
		throws IOException
	{
		if(codec == null)
		{
			throw new IllegalStateException("Codec has not been initialized!");
		}
		byte[] buffer = codec.encode(element);

		int bufferSize = buffer.length;

		dataFile.seek(offset);
		dataFile.writeInt(bufferSize);
		dataFile.writeLong(index);
		dataFile.write(buffer);
		return bufferSize;
	}

	private E internalReadElement(RandomAccessFile dataFile, long offset, Codec codec)
		throws IOException, ClassNotFoundException, ClassCastException
	{
		if(codec == null)
		{
			throw new IllegalStateException("Codec has not been initialized!");
		}

		if(dataFile.length() < offset + DATA_LENGTH_SIZE + INDEX_SIZE)
		{
			throw new IndexOutOfBoundsException("Invalid offset: " + offset + "! Couldn't read length of data!");
		}
		dataFile.seek(offset);
		int bufferSize = dataFile.readInt();
		long startOfData = offset + DATA_LENGTH_SIZE + INDEX_SIZE;
		if(dataFile.length() < startOfData + bufferSize)
		{
			throw new IndexOutOfBoundsException("Invalid length (" + bufferSize + ") at offset: " + offset + "!");
		}
		// ignore stored index in case of read
		dataFile.seek(startOfData);
		byte[] buffer = new byte[bufferSize];
		dataFile.readFully(buffer);
		return codec.decode(buffer);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy