| using J2N.IO; |
| using Lucene.Net.Diagnostics; |
| using Lucene.Net.Support.IO; |
| using System; |
| using System.IO; |
| using System.Threading; |
| |
| 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> |
| /// An <see cref="FSDirectory"/> implementation that uses <see cref="FileStream"/>'s |
| /// positional read, which allows multiple threads to read from the same file |
| /// without synchronizing. |
| /// <para/> |
| /// This class only uses <see cref="FileStream"/> when reading; writing is achieved with |
| /// <see cref="FSDirectory.FSIndexOutput"/>. |
| /// <para/> |
| /// <b>NOTE</b>: Since the .NET <see cref="NIOFSDirectory"/> uses additional seeking during reads, |
| /// it will generally be slightly less efficient than <see cref="SimpleFSDirectory"/>. |
| /// This class has poor concurrent read performance (multiple threads will |
| /// bottleneck) as it synchronizes when multiple threads |
| /// read from the same file. It's usually better to use |
| /// <see cref="MMapDirectory"/> for reading. |
| /// <para/> |
| /// <font color="red"><b>NOTE:</b> Unlike in Java, it is not recommended to use |
| /// <see cref="System.Threading.Thread.Interrupt()"/> in .NET |
| /// in conjunction with an open <see cref="FSDirectory"/> because it is not guaranteed to exit atomically. |
| /// Any <c>lock</c> statement or <see cref="System.Threading.Monitor.Enter(object)"/> call can throw a |
| /// <see cref="System.Threading.ThreadInterruptedException"/>, which makes shutting down unpredictable. |
| /// To exit parallel tasks safely, we recommend using <see cref="System.Threading.Tasks.Task"/>s |
| /// and "interrupt" them with <see cref="System.Threading.CancellationToken"/>s.</font> |
| /// </summary> |
| public class NIOFSDirectory : FSDirectory |
| { |
| /// <summary> |
| /// Create a new <see cref="NIOFSDirectory"/> for the named location. |
| /// </summary> |
| /// <param name="path"> the path of the directory </param> |
| /// <param name="lockFactory"> the lock factory to use, or null for the default |
| /// (<see cref="NativeFSLockFactory"/>); </param> |
| /// <exception cref="IOException"> if there is a low-level I/O error </exception> |
| public NIOFSDirectory(DirectoryInfo path, LockFactory lockFactory) |
| : base(path, lockFactory) |
| { |
| } |
| |
| /// <summary> |
| /// Create a new <see cref="NIOFSDirectory"/> for the named location and <see cref="NativeFSLockFactory"/>. |
| /// </summary> |
| /// <param name="path"> the path of the directory </param> |
| /// <exception cref="IOException"> if there is a low-level I/O error </exception> |
| public NIOFSDirectory(DirectoryInfo path) |
| : base(path, null) |
| { |
| } |
| |
| /// <summary> |
| /// Create a new <see cref="NIOFSDirectory"/> for the named location. |
| /// <para/> |
| /// LUCENENET specific overload for convenience using string instead of <see cref="DirectoryInfo"/>. |
| /// </summary> |
| /// <param name="path"> the path of the directory </param> |
| /// <param name="lockFactory"> the lock factory to use, or null for the default |
| /// (<see cref="NativeFSLockFactory"/>); </param> |
| /// <exception cref="IOException"> if there is a low-level I/O error </exception> |
| public NIOFSDirectory(string path, LockFactory lockFactory) |
| : this(new DirectoryInfo(path), lockFactory) |
| { |
| } |
| |
| /// <summary> |
| /// Create a new <see cref="NIOFSDirectory"/> for the named location and <see cref="NativeFSLockFactory"/>. |
| /// <para/> |
| /// LUCENENET specific overload for convenience using string instead of <see cref="DirectoryInfo"/>. |
| /// </summary> |
| /// <param name="path"> the path of the directory </param> |
| /// <exception cref="IOException"> if there is a low-level I/O error </exception> |
| public NIOFSDirectory(string path) |
| : this(path, null) |
| { |
| } |
| |
| /// <summary> |
| /// Creates an <see cref="IndexInput"/> for the file with the given name. </summary> |
| public override IndexInput OpenInput(string name, IOContext context) |
| { |
| EnsureOpen(); |
| var path = Path.Combine(Directory.FullName, name); // LUCENENET specific: changed to use string file name instead of allocating a FileInfo (#832) |
| var fc = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete); |
| return new NIOFSIndexInput("NIOFSIndexInput(path=\"" + path + "\")", fc, context); |
| } |
| |
| public override IndexInputSlicer CreateSlicer(string name, IOContext context) |
| { |
| EnsureOpen(); |
| var path = Path.Combine(Directory.FullName, name); // LUCENENET specific: changed to use string file name instead of allocating a FileInfo (#832) |
| var fc = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete); |
| return new IndexInputSlicerAnonymousClass(context, path, fc); |
| } |
| |
| private sealed class IndexInputSlicerAnonymousClass : IndexInputSlicer |
| { |
| private readonly IOContext context; |
| private readonly string path; // LUCENENET specific: changed to use string file name instead of allocating a FileInfo (#832) |
| private readonly FileStream descriptor; |
| private int disposed = 0; // LUCENENET specific - allow double-dispose |
| |
| public IndexInputSlicerAnonymousClass(IOContext context, string path, FileStream descriptor) |
| { |
| this.context = context; |
| this.path = path; |
| this.descriptor = descriptor; |
| } |
| |
| protected override void Dispose(bool disposing) |
| { |
| if (0 != Interlocked.CompareExchange(ref this.disposed, 1, 0)) return; // LUCENENET specific - allow double-dispose |
| |
| if (disposing) |
| { |
| descriptor.Dispose(); |
| } |
| } |
| |
| public override IndexInput OpenSlice(string sliceDescription, long offset, long length) |
| { |
| return new NIOFSIndexInput("NIOFSIndexInput(" + sliceDescription + " in path=\"" + path + "\" slice=" + offset + ":" + (offset + length) + ")", descriptor, offset, length, |
| BufferedIndexInput.GetBufferSize(context)); |
| } |
| |
| [Obsolete("Only for reading CFS files from 3.x indexes.")] |
| public override IndexInput OpenFullSlice() |
| { |
| try |
| { |
| return OpenSlice("full-slice", 0, descriptor.Length); |
| } |
| catch (Exception ex) when (ex.IsIOException()) |
| { |
| throw RuntimeException.Create(ex); |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Reads bytes with the <see cref="StreamExtensions.Read(FileStream, Span{Byte}, long)"/> |
| /// extension method for <see cref="FileStream"/>. |
| /// </summary> |
| protected class NIOFSIndexInput : BufferedIndexInput |
| { |
| /// <summary> |
| /// The maximum chunk size for reads of 16384 bytes. |
| /// </summary> |
| private const int CHUNK_SIZE = 16384; |
| |
| /// <summary> |
| /// the file channel we will read from </summary> |
| protected readonly FileStream m_channel; |
| |
| /// <summary> |
| /// is this instance a clone and hence does not own the file to close it </summary> |
| internal bool isClone = false; |
| |
| /// <summary> |
| /// start offset: non-zero in the slice case </summary> |
| protected readonly long m_off; |
| |
| /// <summary> |
| /// end offset (start+length) </summary> |
| protected readonly long m_end; |
| |
| // LUCENENET: byteBuf not needed because we can use Span<byte> inline |
| |
| private int disposed = 0; // LUCENENET specific - allow double-dispose |
| |
| public NIOFSIndexInput(string resourceDesc, FileStream fc, IOContext context) |
| : base(resourceDesc, context) |
| { |
| this.m_channel = fc; |
| this.m_off = 0L; |
| this.m_end = fc.Length; |
| } |
| |
| public NIOFSIndexInput(string resourceDesc, FileStream fc, long off, long length, int bufferSize) |
| : base(resourceDesc, bufferSize) |
| { |
| this.m_channel = fc; |
| this.m_off = off; |
| this.m_end = off + length; |
| this.isClone = true; |
| } |
| |
| protected override void Dispose(bool disposing) |
| { |
| if (0 != Interlocked.CompareExchange(ref this.disposed, 1, 0)) return; // LUCENENET specific - allow double-dispose |
| |
| if (disposing && !isClone) |
| { |
| m_channel.Dispose(); |
| } |
| } |
| |
| public override object Clone() |
| { |
| NIOFSIndexInput clone = (NIOFSIndexInput)base.Clone(); |
| clone.isClone = true; |
| return clone; |
| } |
| |
| public override sealed long Length => m_end - m_off; |
| |
| protected override void ReadInternal(Span<byte> destination) |
| { |
| int len = destination.Length; |
| |
| int readOffset = 0; |
| int readLength = len; |
| long pos = Position + m_off; // LUCENENET specific: Renamed from getFilePointer() to match FileStream |
| |
| if (pos + len > m_end) |
| { |
| throw EOFException.Create("read past EOF: " + this); |
| } |
| |
| try |
| { |
| while (readLength > 0) |
| { |
| int toRead = Math.Min(CHUNK_SIZE, readLength); |
| Span<byte> slice = destination.Slice(readOffset, toRead); |
| int i = m_channel.Read(slice, pos); |
| if (i <= 0) // be defensive here, even though we checked before hand, something could have changed |
| { |
| throw EOFException.Create("read past EOF: " + this + " off: " + readOffset + " len: " + len + " pos: " + pos + " chunkLen: " + readLength + " end: " + m_end); |
| } |
| pos += i; |
| readOffset += i; |
| readLength -= i; |
| } |
| if (Debugging.AssertsEnabled) Debugging.Assert(readLength == 0); |
| } |
| catch (Exception ioe) when (ioe.IsIOException()) |
| { |
| throw new IOException(ioe.ToString() + ": " + this, ioe); |
| } |
| } |
| |
| protected override void SeekInternal(long pos) |
| { |
| } |
| } |
| } |
| } |