blob: 99fee9039eea02588d06f9689f485d91471812ec [file] [log] [blame]
using Lucene.Net.Diagnostics;
using System;
using System.IO;
namespace Lucene.Net.Store
{
/*
* 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.
*/
/// <summary>
/// Base implementation class for buffered <see cref="IndexInput"/>. </summary>
public abstract class BufferedIndexInput : IndexInput
{
/// <summary>
/// Default buffer size set to <see cref="BUFFER_SIZE"/>. </summary>
public const int BUFFER_SIZE = 1024;
// The normal read buffer size defaults to 1024, but
// increasing this during merging seems to yield
// performance gains. However we don't want to increase
// it too much because there are quite a few
// BufferedIndexInputs created during merging. See
// LUCENE-888 for details.
/// <summary>
/// A buffer size for merges set to <see cref="MERGE_BUFFER_SIZE"/>.
/// </summary>
public const int MERGE_BUFFER_SIZE = 4096;
private int bufferSize = BUFFER_SIZE;
protected byte[] m_buffer;
private long bufferStart = 0; // position in file of buffer
private int bufferLength = 0; // end of valid bytes
private int bufferPosition = 0; // next byte to read
public override sealed byte ReadByte()
{
if (bufferPosition >= bufferLength)
{
Refill();
}
return m_buffer[bufferPosition++];
}
public BufferedIndexInput(string resourceDesc)
: this(resourceDesc, BUFFER_SIZE)
{
}
public BufferedIndexInput(string resourceDesc, IOContext context)
: this(resourceDesc, GetBufferSize(context))
{
}
/// <summary>
/// Inits <see cref="BufferedIndexInput"/> with a specific <paramref name="bufferSize"/> </summary>
public BufferedIndexInput(string resourceDesc, int bufferSize)
: base(resourceDesc)
{
CheckBufferSize(bufferSize);
this.bufferSize = bufferSize;
}
/// <summary>
/// Change the buffer size used by this <see cref="IndexInput"/> </summary>
public void SetBufferSize(int newSize)
{
if (Debugging.AssertsEnabled) Debugging.Assert(m_buffer == null || bufferSize == m_buffer.Length, () => "buffer=" + m_buffer + " bufferSize=" + bufferSize + " buffer.length=" + (m_buffer != null ? m_buffer.Length : 0));
if (newSize != bufferSize)
{
CheckBufferSize(newSize);
bufferSize = newSize;
if (m_buffer != null)
{
// Resize the existing buffer and carefully save as
// many bytes as possible starting from the current
// bufferPosition
byte[] newBuffer = new byte[newSize];
int leftInBuffer = bufferLength - bufferPosition;
int numToCopy;
if (leftInBuffer > newSize)
{
numToCopy = newSize;
}
else
{
numToCopy = leftInBuffer;
}
Array.Copy(m_buffer, bufferPosition, newBuffer, 0, numToCopy);
bufferStart += bufferPosition;
bufferPosition = 0;
bufferLength = numToCopy;
NewBuffer(newBuffer);
}
}
}
protected virtual void NewBuffer(byte[] newBuffer)
{
// Subclasses can do something here
m_buffer = newBuffer;
}
/// <summary>
/// Returns buffer size.
/// </summary>
/// <seealso cref="SetBufferSize(int)"/>
public int BufferSize => bufferSize;
private void CheckBufferSize(int bufferSize)
{
if (bufferSize <= 0)
{
throw new ArgumentException("bufferSize must be greater than 0 (got " + bufferSize + ")");
}
}
public override sealed void ReadBytes(byte[] b, int offset, int len)
{
ReadBytes(b, offset, len, true);
}
public override sealed void ReadBytes(byte[] b, int offset, int len, bool useBuffer)
{
int available = bufferLength - bufferPosition;
if (len <= available)
{
// the buffer contains enough data to satisfy this request
if (len > 0) // to allow b to be null if len is 0...
{
Buffer.BlockCopy(m_buffer, bufferPosition, b, offset, len);
}
bufferPosition += len;
}
else
{
// the buffer does not have enough data. First serve all we've got.
if (available > 0)
{
Buffer.BlockCopy(m_buffer, bufferPosition, b, offset, available);
offset += available;
len -= available;
bufferPosition += available;
}
// and now, read the remaining 'len' bytes:
if (useBuffer && len < bufferSize)
{
// If the amount left to read is small enough, and
// we are allowed to use our buffer, do it in the usual
// buffered way: fill the buffer and copy from it:
Refill();
if (bufferLength < len)
{
// Throw an exception when refill() could not read len bytes:
Buffer.BlockCopy(m_buffer, 0, b, offset, bufferLength);
throw new EndOfStreamException("read past EOF: " + this);
}
else
{
Buffer.BlockCopy(m_buffer, 0, b, offset, len);
bufferPosition = len;
}
}
else
{
// The amount left to read is larger than the buffer
// or we've been asked to not use our buffer -
// there's no performance reason not to read it all
// at once. Note that unlike the previous code of
// this function, there is no need to do a seek
// here, because there's no need to reread what we
// had in the buffer.
long after = bufferStart + bufferPosition + len;
if (after > Length)
{
throw new EndOfStreamException("read past EOF: " + this);
}
ReadInternal(b, offset, len);
bufferStart = after;
bufferPosition = 0;
bufferLength = 0; // trigger refill() on read
}
}
}
/// <summary>
/// NOTE: this was readShort() in Lucene
/// </summary>
public override sealed short ReadInt16()
{
if (2 <= (bufferLength - bufferPosition))
{
return (short)(((m_buffer[bufferPosition++] & 0xFF) << 8) | (m_buffer[bufferPosition++] & 0xFF));
}
else
{
return base.ReadInt16();
}
}
/// <summary>
/// NOTE: this was readInt() in Lucene
/// </summary>
public override sealed int ReadInt32()
{
if (4 <= (bufferLength - bufferPosition))
{
return ((m_buffer[bufferPosition++] & 0xFF) << 24) | ((m_buffer[bufferPosition++] & 0xFF) << 16)
| ((m_buffer[bufferPosition++] & 0xFF) << 8) | (m_buffer[bufferPosition++] & 0xFF);
}
else
{
return base.ReadInt32();
}
}
/// <summary>
/// NOTE: this was readLong() in Lucene
/// </summary>
public override sealed long ReadInt64()
{
if (8 <= (bufferLength - bufferPosition))
{
int i1 = ((m_buffer[bufferPosition++] & 0xff) << 24) | ((m_buffer[bufferPosition++] & 0xff) << 16)
| ((m_buffer[bufferPosition++] & 0xff) << 8) | (m_buffer[bufferPosition++] & 0xff);
int i2 = ((m_buffer[bufferPosition++] & 0xff) << 24) | ((m_buffer[bufferPosition++] & 0xff) << 16)
| ((m_buffer[bufferPosition++] & 0xff) << 8) | (m_buffer[bufferPosition++] & 0xff);
return (((long)i1) << 32) | (i2 & 0xFFFFFFFFL);
}
else
{
return base.ReadInt64();
}
}
/// <summary>
/// NOTE: this was readVInt() in Lucene
/// </summary>
public override sealed int ReadVInt32()
{
if (5 <= (bufferLength - bufferPosition))
{
byte b = m_buffer[bufferPosition++];
if ((sbyte)b >= 0)
{
return b;
}
int i = b & 0x7F;
b = m_buffer[bufferPosition++];
i |= (b & 0x7F) << 7;
if ((sbyte)b >= 0)
{
return i;
}
b = m_buffer[bufferPosition++];
i |= (b & 0x7F) << 14;
if ((sbyte)b >= 0)
{
return i;
}
b = m_buffer[bufferPosition++];
i |= (b & 0x7F) << 21;
if ((sbyte)b >= 0)
{
return i;
}
b = m_buffer[bufferPosition++];
// Warning: the next ands use 0x0F / 0xF0 - beware copy/paste errors:
i |= (b & 0x0F) << 28;
if ((b & 0xF0) == 0)
{
return i;
}
throw new IOException("Invalid VInt32 detected (too many bits)");
}
else
{
return base.ReadVInt32();
}
}
/// <summary>
/// NOTE: this was readVLong() in Lucene
/// </summary>
public override sealed long ReadVInt64()
{
if (9 <= bufferLength - bufferPosition)
{
byte b = m_buffer[bufferPosition++];
if ((sbyte)b >= 0)
{
return b;
}
long i = b & 0x7FL;
b = m_buffer[bufferPosition++];
i |= (b & 0x7FL) << 7;
if ((sbyte)b >= 0)
{
return i;
}
b = m_buffer[bufferPosition++];
i |= (b & 0x7FL) << 14;
if ((sbyte)b >= 0)
{
return i;
}
b = m_buffer[bufferPosition++];
i |= (b & 0x7FL) << 21;
if ((sbyte)b >= 0)
{
return i;
}
b = m_buffer[bufferPosition++];
i |= (b & 0x7FL) << 28;
if ((sbyte)b >= 0)
{
return i;
}
b = m_buffer[bufferPosition++];
i |= (b & 0x7FL) << 35;
if ((sbyte)b >= 0)
{
return i;
}
b = m_buffer[bufferPosition++];
i |= (b & 0x7FL) << 42;
if ((sbyte)b >= 0)
{
return i;
}
b = m_buffer[bufferPosition++];
i |= (b & 0x7FL) << 49;
if ((sbyte)b >= 0)
{
return i;
}
b = m_buffer[bufferPosition++];
i |= (b & 0x7FL) << 56;
if ((sbyte)b >= 0)
{
return i;
}
throw new IOException("Invalid VInt64 detected (negative values disallowed)");
}
else
{
return base.ReadVInt64();
}
}
private void Refill()
{
long start = bufferStart + bufferPosition;
long end = start + bufferSize;
if (end > Length) // don't read past EOF
{
end = Length;
}
int newLength = (int)(end - start);
if (newLength <= 0)
{
throw new EndOfStreamException("read past EOF: " + this);
}
if (m_buffer == null)
{
NewBuffer(new byte[bufferSize]); // allocate buffer lazily
SeekInternal(bufferStart);
}
ReadInternal(m_buffer, 0, newLength);
bufferLength = newLength;
bufferStart = start;
bufferPosition = 0;
}
/// <summary>
/// Expert: implements buffer refill. Reads bytes from the current position
/// in the input. </summary>
/// <param name="b"> the array to read bytes into </param>
/// <param name="offset"> the offset in the array to start storing bytes </param>
/// <param name="length"> the number of bytes to read </param>
protected abstract void ReadInternal(byte[] b, int offset, int length);
public override sealed long GetFilePointer()
{
return bufferStart + bufferPosition;
}
public override sealed void Seek(long pos)
{
if (pos >= bufferStart && pos < (bufferStart + bufferLength))
{
bufferPosition = (int)(pos - bufferStart); // seek within buffer
}
else
{
bufferStart = pos;
bufferPosition = 0;
bufferLength = 0; // trigger refill() on read()
SeekInternal(pos);
}
}
/// <summary>
/// Expert: implements seek. Sets current position in this file, where the
/// next <see cref="ReadInternal(byte[], int, int)"/> will occur. </summary>
/// <seealso cref="ReadInternal(byte[], int, int)"/>
protected abstract void SeekInternal(long pos);
public override object Clone()
{
BufferedIndexInput clone = (BufferedIndexInput)base.Clone();
clone.m_buffer = null;
clone.bufferLength = 0;
clone.bufferPosition = 0;
clone.bufferStart = GetFilePointer();
return clone;
}
/// <summary>
/// Flushes the in-memory buffer to the given output, copying at most
/// <paramref name="numBytes"/>.
/// <para/>
/// <b>NOTE:</b> this method does not refill the buffer, however it does
/// advance the buffer position.
/// </summary>
/// <returns> the number of bytes actually flushed from the in-memory buffer. </returns>
protected int FlushBuffer(IndexOutput @out, long numBytes)
{
int toCopy = bufferLength - bufferPosition;
if (toCopy > numBytes)
{
toCopy = (int)numBytes;
}
if (toCopy > 0)
{
@out.WriteBytes(m_buffer, bufferPosition, toCopy);
bufferPosition += toCopy;
}
return toCopy;
}
/// <summary>
/// Returns default buffer sizes for the given <see cref="IOContext"/>
/// </summary>
public static int GetBufferSize(IOContext context) // LUCENENET NOTE: Renamed from BufferSize to prevent naming conflict
{
switch (context.Context)
{
case IOContext.UsageContext.MERGE:
return MERGE_BUFFER_SIZE;
default:
return BUFFER_SIZE;
}
}
}
}