1+ using Lucene . Net . Util ;
2+ using Microsoft . Win32 . SafeHandles ;
13using System ;
4+ using System . ComponentModel ;
5+ using System . IO ;
6+ using System . Runtime . InteropServices ;
7+ #if FEATURE_SUPPORTEDOSPLATFORMATTRIBUTE
8+ using System . Runtime . Versioning ;
9+ #endif
210
3- namespace org . apache . lucene . store
11+ namespace Lucene . Net . Store
412{
5-
613 /*
714 * Licensed to the Apache Software Foundation (ASF) under one or more
815 * contributor license agreements. See the NOTICE file distributed with
@@ -20,45 +27,205 @@ namespace org.apache.lucene.store
2027 * limitations under the License.
2128 */
2229
23- ignore
24-
2530 /// <summary>
26- /// Provides JNI access to native methods such as madvise() for
27- /// <seealso cref="NativeUnixDirectory"/>
31+ /// Provides access to native POSIX methods such as <c>madvise()</c> for
32+ /// <see cref="NativeUnixDirectory"/>.
33+ /// <para/>
34+ /// LUCENENET specific: the original Lucene implementation called these through a JNI
35+ /// shim compiled from <c>NativePosixUtil.cpp</c>. This port replaces that native build
36+ /// step with direct P/Invoke into <c>libc</c>, so it can only be used on Unix-like
37+ /// platforms (Linux and macOS); it is not supported on Microsoft Windows.
2838 /// </summary>
29- public final class NativePosixUtil
39+ #if FEATURE_SUPPORTEDOSPLATFORMATTRIBUTE
40+ [ UnsupportedOSPlatform ( "windows" ) ]
41+ #endif
42+ public static class NativePosixUtil
3043 {
31- public final static int NORMAL = 0 ;
32- public final static int SEQUENTIAL = 1 ;
33- public final static int RANDOM = 2 ;
34- public final static int WILLNEED = 3 ;
35- public final static int DONTNEED = 4 ;
36- public final static int NOREUSE = 5 ;
37-
38- //JAVA TO C# CONVERTER NOTE: This static initializer block is converted to a static constructor, but there is no current class:
39- static ImpliedClass ( )
40- {
41- //JAVA TO C# CONVERTER TODO TASK: The library is specified in the 'DllImport' attribute for .NET:
42- // System.loadLibrary("NativePosixUtil");
43- }
44-
45- private static native int posix_fadvise( FileDescriptor fd , long offset , long len , int advise ) throws IOException;
46- public static native int posix_madvise( ByteBuffer buf , int advise ) throws IOException;
47- public static native int madvise( ByteBuffer buf , int advise ) throws IOException;
48- public static native FileDescriptor open_direct( string filename , bool read ) throws IOException;
49- public static native long pread( FileDescriptor fd , long pos , ByteBuffer byteBuf ) throws IOException;
50-
51- public static void advise ( FileDescriptor fd , long offset , long len , int advise ) throws IOException
52- {
53- //JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
54- //ORIGINAL LINE: final int code = posix_fadvise(fd, offset, len, advise);
55- int code = posix_fadvise ( fd , offset , len , advise ) ;
56- if ( code != 0 )
44+ // These constants mirror the Java NativePosixUtil ordering. Note this is NOT the same
45+ // ordering as the OS POSIX_FADV_*/POSIX_MADV_* values (SEQUENTIAL/RANDOM are swapped);
46+ // MapAdvice() translates to the OS values.
47+ public const int NORMAL = 0 ;
48+ public const int SEQUENTIAL = 1 ;
49+ public const int RANDOM = 2 ;
50+ public const int WILLNEED = 3 ;
51+ public const int DONTNEED = 4 ;
52+ public const int NOREUSE = 5 ;
53+
54+ /// <summary>
55+ /// Opens a file for direct (un-cached) I/O.
56+ /// <para/>
57+ /// On Linux this uses <c>O_DIRECT | O_NOATIME</c>; on macOS it opens normally and then
58+ /// applies <c>fcntl(F_NOCACHE)</c>, matching the original native implementation.
59+ /// </summary>
60+ /// <param name="filename"> the file to open </param>
61+ /// <param name="read"> <c>true</c> to open read-only; <c>false</c> to open read-write (creating if needed) </param>
62+ /// <returns> a <see cref="SafeFileHandle"/> wrapping the open file descriptor </returns>
63+ /// <exception cref="IOException"> If the file could not be opened </exception>
64+ /// <exception cref="PlatformNotSupportedException"> If running on Microsoft Windows </exception>
65+ public static SafeFileHandle OpenDirect ( string filename , bool read )
5766 {
58- throw new Exception ( "posix_fadvise failed code=" + code ) ;
67+ EnsureUnix ( ) ;
68+
69+ int fd ;
70+ if ( Constants . MAC_OS_X )
71+ {
72+ fd = read
73+ ? NativeMethods . open ( filename , NativeMethods . O_RDONLY )
74+ : NativeMethods . open ( filename , NativeMethods . O_RDWR | NativeMethods . O_CREAT , NativeMethods . S_RW ) ;
75+ if ( fd < 0 )
76+ {
77+ throw NewIOException ( "open" , filename ) ;
78+ }
79+
80+ // macOS has no O_DIRECT; disable the page cache for this descriptor instead.
81+ if ( NativeMethods . fcntl ( fd , NativeMethods . F_NOCACHE , 1 ) < 0 )
82+ {
83+ int err = Marshal . GetLastWin32Error ( ) ;
84+ NativeMethods . close ( fd ) ;
85+ throw NewIOException ( "fcntl(F_NOCACHE)" , filename , err ) ;
86+ }
87+ }
88+ else // Linux (and other Unixes, best-effort)
89+ {
90+ fd = read
91+ ? NativeMethods . open ( filename , NativeMethods . O_RDONLY | NativeMethods . O_DIRECT | NativeMethods . O_NOATIME )
92+ : NativeMethods . open ( filename , NativeMethods . O_RDWR | NativeMethods . O_CREAT | NativeMethods . O_DIRECT | NativeMethods . O_NOATIME , NativeMethods . S_RW ) ;
93+ if ( fd < 0 )
94+ {
95+ throw NewIOException ( "open" , filename ) ;
96+ }
97+ }
98+
99+ return new SafeFileHandle ( ( IntPtr ) fd , ownsHandle : true ) ;
59100 }
60- }
61- }
62101
102+ /// <summary>
103+ /// Positioned read of up to <paramref name="length"/> bytes from <paramref name="fd"/> at
104+ /// absolute offset <paramref name="pos"/> into the native buffer at <paramref name="buffer"/>,
105+ /// without changing the file's current offset.
106+ /// </summary>
107+ /// <returns> the number of bytes read (may be less than <paramref name="length"/> near EOF) </returns>
108+ /// <exception cref="IOException"> If the read failed </exception>
109+ public static long Pread ( SafeFileHandle fd , long pos , IntPtr buffer , int length )
110+ {
111+ EnsureUnix ( ) ;
112+ long n = ( long ) NativeMethods . pread ( Fd ( fd ) , buffer , ( nuint ) length , pos ) ;
113+ if ( n < 0 )
114+ {
115+ throw NewIOException ( "pread" , null ) ;
116+ }
117+ return n ;
118+ }
119+
120+ /// <summary>
121+ /// Issues a <c>posix_madvise()</c> hint over the native buffer at <paramref name="buffer"/>. </summary>
122+ public static int PosixMAdvise ( IntPtr buffer , int length , int advise )
123+ {
124+ EnsureUnix ( ) ;
125+ return NativeMethods . posix_madvise ( buffer , ( nuint ) length , MapAdvice ( advise ) ) ;
126+ }
127+
128+ /// <summary>
129+ /// Issues a <c>madvise()</c> hint over the native buffer at <paramref name="buffer"/>. </summary>
130+ public static int MAdvise ( IntPtr buffer , int length , int advise )
131+ {
132+ EnsureUnix ( ) ;
133+ return NativeMethods . madvise ( buffer , ( nuint ) length , MapAdvice ( advise ) ) ;
134+ }
135+
136+ /// <summary>
137+ /// Issues a <c>posix_fadvise()</c> hint for the given range of <paramref name="fd"/>,
138+ /// throwing if the call reports a non-zero error code.
139+ /// </summary>
140+ /// <exception cref="IOException"> If <c>posix_fadvise</c> returned a non-zero code </exception>
141+ public static void Advise ( SafeFileHandle fd , long offset , long len , int advise )
142+ {
143+ EnsureUnix ( ) ;
144+ int code = NativeMethods . posix_fadvise ( Fd ( fd ) , offset , len , MapAdvice ( advise ) ) ;
145+ if ( code != 0 )
146+ {
147+ // LUCENENET: upstream throws RuntimeException; we use IOException as this is an I/O failure.
148+ throw new IOException ( "posix_fadvise failed code=" + code ) ;
149+ }
150+ }
151+
152+ /// <summary>
153+ /// Translates the Java-style advice ordinal (see the public constants) to the OS
154+ /// <c>POSIX_FADV_*</c>/<c>POSIX_MADV_*</c> value. Only <c>SEQUENTIAL</c> and <c>RANDOM</c>
155+ /// differ in ordering between the two.
156+ /// </summary>
157+ private static int MapAdvice ( int advise )
158+ {
159+ switch ( advise )
160+ {
161+ case SEQUENTIAL : return 2 ; // POSIX_*_SEQUENTIAL
162+ case RANDOM : return 1 ; // POSIX_*_RANDOM
163+ default : return advise ; // NORMAL=0, WILLNEED=3, DONTNEED=4, NOREUSE=5 are identical
164+ }
165+ }
63166
167+ private static int Fd ( SafeFileHandle handle ) => ( int ) handle . DangerousGetHandle ( ) ;
168+
169+ private static IOException NewIOException ( string operation , string filename )
170+ => NewIOException ( operation , filename , Marshal . GetLastWin32Error ( ) ) ;
171+
172+ private static IOException NewIOException ( string operation , string filename , int errno )
173+ {
174+ string where = filename is null ? operation : $ "{ operation } { filename } ";
175+ // On Unix with SetLastError, GetLastWin32Error() returns errno. Win32Exception's message
176+ // is not meaningful on Unix, so include the raw errno for diagnosis.
177+ return new IOException ( $ "{ where } failed (errno { errno } )") ;
178+ }
179+
180+ internal static void EnsureUnix ( )
181+ {
182+ if ( Constants . WINDOWS )
183+ {
184+ throw new PlatformNotSupportedException (
185+ $ "{ nameof ( NativePosixUtil ) } requires Linux or macOS direct I/O and is not supported on Microsoft Windows.") ;
186+ }
187+ }
188+
189+ /// <summary>
190+ /// P/Invoke declarations for the <c>libc</c> functions used here. These replace the
191+ /// JNI/C++ native methods of the original implementation.
192+ /// </summary>
193+ private static class NativeMethods
194+ {
195+ private const string LIBC = "libc" ;
196+
197+ // open() flags. O_CREAT differs between Linux and macOS; O_DIRECT/O_NOATIME are Linux-only.
198+ internal const int O_RDONLY = 0x0 ;
199+ internal const int O_RDWR = 0x2 ;
200+ internal static readonly int O_CREAT = Constants . MAC_OS_X ? 0x0200 : 0x40 ;
201+ internal const int O_DIRECT = 0x4000 ; // Linux
202+ internal const int O_NOATIME = 0x40000 ; // Linux
203+ internal const int F_NOCACHE = 48 ; // macOS
204+ internal const int S_RW = 0x1B6 ; // 0666
205+
206+ [ DllImport ( LIBC , SetLastError = true , EntryPoint = "open" ) ]
207+ internal static extern int open ( [ MarshalAs ( UnmanagedType . LPStr ) ] string pathname , int flags ) ;
208+
209+ [ DllImport ( LIBC , SetLastError = true , EntryPoint = "open" ) ]
210+ internal static extern int open ( [ MarshalAs ( UnmanagedType . LPStr ) ] string pathname , int flags , int mode ) ;
211+
212+ [ DllImport ( LIBC , SetLastError = true ) ]
213+ internal static extern int close ( int fd ) ;
214+
215+ [ DllImport ( LIBC , SetLastError = true ) ]
216+ internal static extern int fcntl ( int fd , int cmd , int arg ) ;
217+
218+ [ DllImport ( LIBC , SetLastError = true ) ]
219+ internal static extern nint pread ( int fd , IntPtr buf , nuint count , long offset ) ;
220+
221+ [ DllImport ( LIBC , SetLastError = true ) ]
222+ internal static extern int posix_fadvise ( int fd , long offset , long len , int advice ) ;
223+
224+ [ DllImport ( LIBC , SetLastError = true ) ]
225+ internal static extern int posix_madvise ( IntPtr addr , nuint length , int advice ) ;
226+
227+ [ DllImport ( LIBC , SetLastError = true ) ]
228+ internal static extern int madvise ( IntPtr addr , nuint length , int advice ) ;
229+ }
230+ }
64231}
0 commit comments