blob: 1bc8a78cc83dfaf76428db47dc5ab818aad2d9d6 [file] [log] [blame]
using Lucene.Net.Codecs.Sep;
using Lucene.Net.Diagnostics;
using Lucene.Net.Store;
using System.Diagnostics;
namespace Lucene.Net.Codecs.IntBlock
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
// TODO: much of this can be shared code w/ the fixed case
/// <summary>
/// Abstract base class that writes variable-size blocks of ints
/// to an <see cref="IndexOutput"/>. While this is a simple approach, a
/// more performant approach would directly create an impl
/// of <see cref="Int32IndexOutput"/> inside <see cref="Directory"/>. Wrapping a generic
/// <see cref="IndexOutput"/> will likely cost performance.
/// <para/>
/// NOTE: This was VariableIntBlockIndexOutput in Lucene
/// <para/>
/// @lucene.experimental
/// </summary>
/// <remarks>
/// Naive int block API that writes vInts. This is
/// expected to give poor performance; it's really only for
/// testing the pluggability. One should typically use pfor instead.
/// </remarks>
public abstract class VariableInt32BlockIndexOutput : Int32IndexOutput
{
protected readonly IndexOutput m_output;
private int upto;
private bool hitExcDuringWrite;
// TODO what Var-Var codecs exist in practice... and what are there blocksizes like?
// if its less than 128 we should set that as max and use byte?
/// <summary>
/// NOTE: <paramref name="maxBlockSize"/> must be the maximum block size
/// plus the max non-causal lookahead of your codec. EG Simple9
/// requires lookahead=1 because on seeing the Nth value
/// it knows it must now encode the N-1 values before it.
/// </summary>
protected VariableInt32BlockIndexOutput(IndexOutput output, int maxBlockSize)
{
this.m_output = output;
this.m_output.WriteInt32(maxBlockSize);
}
/// <summary>
/// Called one value at a time. Return the number of
/// buffered input values that have been written to out.
/// </summary>
protected abstract int Add(int value);
public override Int32IndexOutput.Index GetIndex()
{
return new Index(this);
}
new private class Index : Int32IndexOutput.Index
{
private readonly VariableInt32BlockIndexOutput outerInstance;
public Index(VariableInt32BlockIndexOutput outerInstance)
{
this.outerInstance = outerInstance;
}
private long fp;
private int upto;
private long lastFP;
private int lastUpto;
public override void Mark()
{
fp = outerInstance.m_output.GetFilePointer();
upto = outerInstance.upto;
}
public override void CopyFrom(Int32IndexOutput.Index other, bool copyLast)
{
Index idx = (Index)other;
fp = idx.fp;
upto = idx.upto;
if (copyLast)
{
lastFP = fp;
lastUpto = upto;
}
}
public override void Write(DataOutput indexOut, bool absolute)
{
if (Debugging.AssertsEnabled) Debugging.Assert(upto >= 0);
if (absolute)
{
indexOut.WriteVInt32(upto);
indexOut.WriteVInt64(fp);
}
else if (fp == lastFP)
{
// same block
if (Debugging.AssertsEnabled) Debugging.Assert(upto >= lastUpto);
int uptoDelta = upto - lastUpto;
indexOut.WriteVInt32(uptoDelta << 1 | 1);
}
else
{
// new block
indexOut.WriteVInt32(upto << 1);
indexOut.WriteVInt64(fp - lastFP);
}
lastUpto = upto;
lastFP = fp;
}
}
public override void Write(int v)
{
hitExcDuringWrite = true;
upto -= Add(v) - 1;
hitExcDuringWrite = false;
if (Debugging.AssertsEnabled) Debugging.Assert(upto >= 0);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
try
{
if (hitExcDuringWrite) return;
// stuff 0s in until the "real" data is flushed:
var stuffed = 0;
while (upto > stuffed)
{
upto -= Add(0) - 1;
if (Debugging.AssertsEnabled) Debugging.Assert(upto >= 0);
stuffed += 1;
}
}
finally
{
m_output.Dispose();
}
}
}
}
}