/// <summary>
		/// Create a new table builder.
		/// - storageOptions define the options for the table buildup.
		/// - dataStream is where the data for the table will be written to.
		///		REQUIRES: Being able to read dataStream.Position 
		/// - tempStream is where temporary data is written to avoid holding too much in memory
		///     REQUIRES: Being able to read tempStream.Position AND change tempStream.Position
		/// </summary>
		public TableBuilder(StorageState storageState,
			Stream dataStream,
			TemporaryFiles temporaryFiles)
		{
			_temporaryFiles = temporaryFiles;
			try
			{
				_storageState = storageState;
				_dataStream = dataStream;
				_indexStream = temporaryFiles.Create();
				_originalIndexStreamPosition = _indexStream.Position;

				_lastKeyBuffer = _storageState.Options.BufferPool.Take(storageState.Options.MaximumExpectedKeySize);
				_scratchBuffer = _storageState.Options.BufferPool.Take(storageState.Options.MaximumExpectedKeySize);

				if (storageState.Options.FilterPolicy != null)
				{
					var filterBuilder = storageState.Options.FilterPolicy.CreateBuilder();
					_filterBlockStream = temporaryFiles.Create();
					_filterBuilder = new FilterBlockBuilder(_filterBlockStream, filterBuilder);
					_filterBuilder.StartBlock(0);
				}

				_indexBlock = new BlockBuilder(_indexStream, storageState, _storageState.InternalKeyComparator,
				                               blockRestartInterval: 1);
				_dataBlock = new BlockBuilder(_dataStream, storageState, _storageState.InternalKeyComparator, _storageState.Options.BlockRestartInterval);
			}
			catch (Exception)
			{
				Dispose();
				throw;
			}
		}
		public void Finish()
		{
			Flush();
			_closed = true;

			BlockHandle filterBlockHandle = null;

			//write filter block
			if (_filterBuilder != null)
				filterBlockHandle = _filterBuilder.Finish(_dataStream);

			// write metadata block

			var metaIndexBlock = new BlockBuilder(_dataStream, _storageState, _storageState.InternalKeyComparator, _storageState.Options.BlockRestartInterval);
			if (filterBlockHandle != null)
			{
				metaIndexBlock.Add(("filter." + _storageState.Options.FilterPolicy.Name), filterBlockHandle.AsStream());
			}
			var metadIndexBlockHandle = WriteBlock(metaIndexBlock);

			// write index block

			if (_pendingIndexEntry)
			{
				var newKey = _storageState.InternalKeyComparator.FindShortestSuccessor(_lastKey, ref _scratchBuffer);
				_indexBlock.Add(newKey, _pendingHandle.AsStream());
				_pendingIndexEntry = false;
			}

			var indexBlockSize = _indexBlock.Finish();
			_indexBlock.Stream.WriteByte(0);//write type, uncompressed
			_indexBlock.Stream.WriteInt32(Crc.Mask(_indexBlock.Stream.WriteCrc));
			_indexBlock.Stream.Position = _originalIndexStreamPosition;

			var indexBlockHandler = new BlockHandle
				{
					Position = _dataStream.Position,
					Count = indexBlockSize
				};

			_indexBlock.Stream.Stream.CopyTo(_dataStream);

			// write footer
			var footer = new Footer
				{
					IndexHandle = indexBlockHandler,
					MetaIndexHandle = metadIndexBlockHandle
				};

			footer.EncodeTo(_dataStream);
		}
		private BlockHandle WriteBlock(BlockBuilder block)
		{
			// File format contains a sequence of blocks where each block has:
			//    block_data: uint8[n]
			//    type: uint8 - right now always uncompressed
			//    crc: uint32
			var size = block.Finish();

			var originalPosition = block.OriginalPosition;

			var handle = new BlockHandle
				{
					Count = size,
					Position = originalPosition
				};

			// write trailer
			block.Stream.WriteByte(0); // type - uncompressed
			_dataStream.WriteInt32(Crc.Mask(block.Stream.WriteCrc));

			
			block.Dispose();

			return handle;
		}
		/// <summary>
		/// Advance: Forces the creation of a new block. 
		/// Client code should probably not call this method
		/// </summary>
		public void Flush()
		{
			if (_dataBlock.IsEmpty)
				return;
			if (_closed)
				throw new InvalidOperationException("Cannot add after the table builder was closed");
			if (_pendingIndexEntry)
				throw new InvalidOperationException("Cannot call Flush when pending for an index entry");

			_pendingHandle = WriteBlock(_dataBlock);
			_dataBlock = new BlockBuilder(_dataStream, _storageState, _storageState.InternalKeyComparator, _storageState.Options.BlockRestartInterval);

			_pendingIndexEntry = true;
			_dataStream.Flush();

			if (_filterBuilder != null)
				_filterBuilder.StartBlock(_dataStream.Position);
		}