public override bool Equals(object obj)
        {
            if (this == obj)
            {
                return(true);
            }
            else if (!(obj is IndexCacheEntryKey))
            {
                return(false);
            }
            IndexCacheEntryKey other = (IndexCacheEntryKey)obj;

            if (this.subFileParameter == null && other.subFileParameter != null)
            {
                return(false);
            }
            else if (this.subFileParameter != null && !this.subFileParameter.Equals(other.subFileParameter))
            {
                return(false);
            }
            else if (this.indexBlockNumber != other.indexBlockNumber)
            {
                return(false);
            }
            return(true);
        }
        /// <summary>
        /// Returns the index entry of a block in the given map file. If the required index entry is not cached, it will be
        /// read from the map file index and put in the cache.
        /// </summary>
        /// <param name="subFileParameter">
        ///            the parameters of the map file for which the index entry is needed. </param>
        /// <param name="blockNumber">
        ///            the number of the block in the map file. </param>
        /// <returns> the index entry. </returns>
        /// <exception cref="IOException">
        ///             if an I/O error occurs during reading. </exception>
        internal virtual long GetIndexEntry(SubFileParameter subFileParameter, long blockNumber)
        {
            // check if the block number is out of bounds
            if (blockNumber >= subFileParameter.NumberOfBlocks)
            {
                throw new IOException("invalid block number: " + blockNumber);
            }

            // calculate the index block number
            long indexBlockNumber = blockNumber / INDEX_ENTRIES_PER_BLOCK;

            // create the cache entry key for this request
            IndexCacheEntryKey indexCacheEntryKey = new IndexCacheEntryKey(subFileParameter, indexBlockNumber);

            // check for cached index block
            byte[] indexBlock = this.map.Get(indexCacheEntryKey);
            if (indexBlock == null)
            {
                // cache miss, seek to the correct index block in the file and read it
                long indexBlockPosition = subFileParameter.IndexStartAddress + indexBlockNumber * SIZE_OF_INDEX_BLOCK;

                int remainingIndexSize = (int)(subFileParameter.IndexEndAddress - indexBlockPosition);
                int indexBlockSize     = Math.Min(SIZE_OF_INDEX_BLOCK, remainingIndexSize);
                indexBlock = new byte[indexBlockSize];

                this.randomAccessFile.Seek(indexBlockPosition, SeekOrigin.Begin);
                if (this.randomAccessFile.Read(indexBlock, 0, indexBlockSize) != indexBlockSize)
                {
                    throw new IOException("could not read index block with size: " + indexBlockSize);
                }

                // put the index block in the map
                this.map.Add(indexCacheEntryKey, indexBlock);
            }

            // calculate the address of the index entry inside the index block
            long indexEntryInBlock   = blockNumber % INDEX_ENTRIES_PER_BLOCK;
            int  addressInIndexBlock = (int)(indexEntryInBlock * SubFileParameter.BYTES_PER_INDEX_ENTRY);

            // return the real index entry
            return(Deserializer.GetFiveBytesLong((sbyte[])(Array)indexBlock, addressInIndexBlock));
        }