You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by ni...@apache.org on 2025/10/01 06:13:57 UTC

(lucenenet) 04/09: BREAKING: Lucene.Net.Store.DataOutput: Added abstract overload of WriteBytes(ReadOnlySpan) and made the WriteBytes(byte[], int, int) overload virtual.

This is an automated email from the ASF dual-hosted git repository.

nightowl888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucenenet.git

commit eef93fb7bb1a87ae3a28307b57532944d3c6e385
Author: Shad Storhaug <sh...@shadstorhaug.com>
AuthorDate: Sun Sep 28 23:15:42 2025 +0700

    BREAKING: Lucene.Net.Store.DataOutput: Added abstract overload of WriteBytes(ReadOnlySpan<byte>) and made the WriteBytes(byte[], int, int) overload virtual.
---
 .../Store/MockDirectoryWrapper.cs                  |  5 +-
 .../Store/MockIndexOutputWrapper.cs                | 81 +++++++++++++++++++++-
 .../Util/ThrottledIndexOutput.cs                   |  6 +-
 src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs     |  6 +-
 src/Lucene.Net/Index/ByteSliceWriter.cs            |  9 ++-
 src/Lucene.Net/Store/BufferedChecksum.cs           | 18 ++---
 src/Lucene.Net/Store/BufferedIndexOutput.cs        | 27 ++++++--
 src/Lucene.Net/Store/ByteArrayDataOutput.cs        |  6 +-
 src/Lucene.Net/Store/CompoundFileWriter.cs         |  6 +-
 src/Lucene.Net/Store/DataOutput.cs                 | 19 +++--
 src/Lucene.Net/Store/FSDirectory.cs                | 14 ++--
 src/Lucene.Net/Store/OutputStreamDataOutput.cs     |  6 +-
 src/Lucene.Net/Store/RAMOutputStream.cs            | 11 +--
 src/Lucene.Net/Store/RateLimitedIndexOutput.cs     |  9 ++-
 src/Lucene.Net/Support/CRC32.cs                    | 15 ++--
 src/Lucene.Net/Support/IChecksum.cs                |  6 +-
 src/Lucene.Net/Util/Fst/BytesStore.cs              | 10 ++-
 src/Lucene.Net/Util/GrowableByteArrayDataOutput.cs |  7 +-
 src/Lucene.Net/Util/PagedBytes.cs                  | 11 +--
 19 files changed, 209 insertions(+), 63 deletions(-)

diff --git a/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs b/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs
index aa34fc178..5b17f170e 100644
--- a/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs
+++ b/src/Lucene.Net.TestFramework/Store/MockDirectoryWrapper.cs
@@ -1444,9 +1444,10 @@ public BufferedIndexOutputWrapper(int bufferSize, IndexOutput io)
 
             public override long Length => io.Length;
 
-            protected internal override void FlushBuffer(byte[] b, int offset, int len)
+            // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+            protected internal override void FlushBuffer(ReadOnlySpan<byte> bytes)
             {
-                io.WriteBytes(b, offset, len);
+                io.WriteBytes(bytes);
             }
 
             [Obsolete("(4.1) this method will be removed in Lucene 5.0")]
diff --git a/src/Lucene.Net.TestFramework/Store/MockIndexOutputWrapper.cs b/src/Lucene.Net.TestFramework/Store/MockIndexOutputWrapper.cs
index 96f92c28b..ee3a11747 100644
--- a/src/Lucene.Net.TestFramework/Store/MockIndexOutputWrapper.cs
+++ b/src/Lucene.Net.TestFramework/Store/MockIndexOutputWrapper.cs
@@ -3,6 +3,7 @@
 using System;
 using System.IO;
 using System.Threading;
+#nullable enable
 
 namespace Lucene.Net.Store
 {
@@ -56,7 +57,7 @@ private void CheckCrashed()
             }
         }
 
-        private void CheckDiskFull(byte[] b, int offset, DataInput @in, long len)
+        private void CheckDiskFull(byte[]? b, int offset, DataInput? @in, long len)
         {
             long freeSpace = dir.maxSize == 0 ? 0 : dir.maxSize - dir.GetSizeInBytes();
             long realUsage = 0;
@@ -103,6 +104,48 @@ private void CheckDiskFull(byte[] b, int offset, DataInput @in, long len)
             }
         }
 
+        // LUCENENET specific overload
+        private void CheckDiskFull(ReadOnlySpan<byte> source)
+        {
+            long len = source.Length;
+            long freeSpace = dir.maxSize == 0 ? 0 : dir.maxSize - dir.GetSizeInBytes();
+            long realUsage = 0;
+
+            // Enforce disk full:
+            if (dir.maxSize != 0 && freeSpace <= len)
+            {
+                // Compute the real disk free.  this will greatly slow
+                // down our test but makes it more accurate:
+                realUsage = dir.GetRecomputedActualSizeInBytes();
+                freeSpace = dir.maxSize - realUsage;
+            }
+
+            if (dir.maxSize != 0 && freeSpace <= len)
+            {
+                if (freeSpace > 0)
+                {
+                    realUsage += freeSpace;
+                    @delegate.WriteBytes(source.Slice(/*offset*/ 0, (int)freeSpace));
+                }
+                if (realUsage > dir.maxUsedSize)
+                {
+                    dir.maxUsedSize = realUsage;
+                }
+                string message = "fake disk full at " + dir.GetRecomputedActualSizeInBytes() + " bytes when writing " + name + " (file length=" + @delegate.Length;
+                if (freeSpace > 0)
+                {
+                    message += "; wrote " + freeSpace + " of " + len + " bytes";
+                }
+                message += ")";
+                if (LuceneTestCase.Verbose)
+                {
+                    Console.WriteLine(Thread.CurrentThread.Name + ": MDW: now throw fake disk full");
+                    StackTraceHelper.PrintCurrentStackTrace(Console.Out);
+                }
+                throw new IOException(message);
+            }
+        }
+
         protected override void Dispose(bool disposing)
         {
             if (disposing)
@@ -141,6 +184,9 @@ public override void WriteByte(byte b)
             WriteBytes(singleByte, 0, 1);
         }
 
+        // LUCENENET: For performance reasons, it is better to keep this duplication, for now.
+        // At least until we have migrated many of the callers to call the ReadOnlySpan<char> overload
+        // directly.
         public override void WriteBytes(byte[] b, int offset, int len)
         {
             CheckCrashed();
@@ -169,6 +215,39 @@ public override void WriteBytes(byte[] b, int offset, int len)
             }
         }
 
+        // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+        public override void WriteBytes(ReadOnlySpan<byte> source)
+        {
+            int len = source.Length;
+            CheckCrashed();
+            CheckDiskFull(source);
+
+            if (dir.randomState.Next(200) == 0)
+            {
+                int half = len / 2;
+                //@delegate.WriteBytes(b, offset, half);
+                @delegate.WriteBytes(source.Slice(/*offset*/ 0, half));
+                Thread.Yield();
+                //@delegate.WriteBytes(b, offset + half, len - half);
+                @delegate.WriteBytes(source.Slice(/*offset +*/ half, len - half));
+            }
+            else
+            {
+                //@delegate.WriteBytes(b, offset, len);
+                @delegate.WriteBytes(source);
+            }
+
+            dir.MaybeThrowDeterministicException();
+
+            if (first)
+            {
+                // Maybe throw random exception; only do this on first
+                // write to a new file:
+                first = false;
+                dir.MaybeThrowIOException(name);
+            }
+        }
+
         public override long Position => @delegate.Position; // LUCENENET specific: Renamed from getFilePointer() to match FileStream
 
         [Obsolete("(4.1) this method will be removed in Lucene 5.0")]
diff --git a/src/Lucene.Net.TestFramework/Util/ThrottledIndexOutput.cs b/src/Lucene.Net.TestFramework/Util/ThrottledIndexOutput.cs
index 815eda7e7..3956b67e5 100644
--- a/src/Lucene.Net.TestFramework/Util/ThrottledIndexOutput.cs
+++ b/src/Lucene.Net.TestFramework/Util/ThrottledIndexOutput.cs
@@ -107,13 +107,15 @@ public override void WriteByte(byte b)
             WriteBytes(bytes, 0, 1);
         }
 
-        public override void WriteBytes(byte[] b, int offset, int length)
+        // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+        public override void WriteBytes(ReadOnlySpan<byte> source)
         {
+            int length = source.Length;
             long before = J2N.Time.NanoTime();
             // TODO: sometimes, write only half the bytes, then
             // sleep, then 2nd half, then sleep, so we sometimes
             // interrupt having only written not all bytes
-            @delegate.WriteBytes(b, offset, length);
+            @delegate.WriteBytes(source);
             timeElapsed += J2N.Time.NanoTime() - before;
             pendingBytes += length;
             Sleep(GetDelay(false));
diff --git a/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs b/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs
index b9e19606a..c3a31f44a 100644
--- a/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs
+++ b/src/Lucene.Net.Tests.Facet/SlowRAMDirectory.cs
@@ -220,15 +220,17 @@ public override void WriteByte(byte b)
                 io.WriteByte(b);
             }
 
-            public override void WriteBytes(byte[] b, int offset, int length)
+            // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+            public override void WriteBytes(ReadOnlySpan<byte> source)
             {
+                int length = source.Length;
                 if (numWrote >= IO_SLEEP_THRESHOLD)
                 {
                     outerInstance.DoSleep(rand, length);
                     numWrote = 0;
                 }
                 numWrote += length;
-                io.WriteBytes(b, offset, length);
+                io.WriteBytes(source);
             }
 
             [Obsolete]
diff --git a/src/Lucene.Net/Index/ByteSliceWriter.cs b/src/Lucene.Net/Index/ByteSliceWriter.cs
index 27bc4a7bd..75bb24fa7 100644
--- a/src/Lucene.Net/Index/ByteSliceWriter.cs
+++ b/src/Lucene.Net/Index/ByteSliceWriter.cs
@@ -1,5 +1,5 @@
 using Lucene.Net.Diagnostics;
-using System.Diagnostics;
+using System;
 
 namespace Lucene.Net.Index
 {
@@ -69,8 +69,11 @@ public override void WriteByte(byte b)
             if (Debugging.AssertsEnabled) Debugging.Assert(upto != slice.Length);
         }
 
-        public override void WriteBytes(byte[] b, int offset, int len)
+        // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+        public override void WriteBytes(ReadOnlySpan<byte> source)
         {
+            int offset = 0;
+            int len = source.Length;
             int offsetEnd = offset + len;
             while (offset < offsetEnd)
             {
@@ -82,7 +85,7 @@ public override void WriteBytes(byte[] b, int offset, int len)
                     offset0 = pool.ByteOffset;
                 }
 
-                slice[upto++] = (byte)b[offset++];
+                slice[upto++] = source[offset++];
                 if (Debugging.AssertsEnabled) Debugging.Assert(upto != slice.Length);
             }
         }
diff --git a/src/Lucene.Net/Store/BufferedChecksum.cs b/src/Lucene.Net/Store/BufferedChecksum.cs
index c4c769eea..4166bfafe 100644
--- a/src/Lucene.Net/Store/BufferedChecksum.cs
+++ b/src/Lucene.Net/Store/BufferedChecksum.cs
@@ -1,4 +1,5 @@
 using Lucene.Net.Support;
+using System;
 
 namespace Lucene.Net.Store
 {
@@ -72,17 +73,13 @@ public virtual void Update(int b)
         }
 
         // LUCENENET specific overload for updating a whole byte[] array
-        public virtual void Update(byte[] b)
-        {
-            Update(b, 0, b.Length);
-        }
-
-        public virtual void Update(byte[] b, int off, int len)
+        public virtual void Update(ReadOnlySpan<byte> bytes)
         {
+            int len = bytes.Length;
             if (len >= buffer.Length)
             {
                 Flush();
-                @in.Update(b, off, len);
+                @in.Update(bytes);
             }
             else
             {
@@ -90,11 +87,16 @@ public virtual void Update(byte[] b, int off, int len)
                 {
                     Flush();
                 }
-                Arrays.Copy(b, off, buffer, upto, len);
+                Arrays.Copy(bytes, /*off*/ 0, buffer, upto, len);
                 upto += len;
             }
         }
 
+        public virtual void Update(byte[] b, int off, int len)
+        {
+            Update(b.AsSpan(off, len));
+        }
+
         public virtual long Value
         {
             get
diff --git a/src/Lucene.Net/Store/BufferedIndexOutput.cs b/src/Lucene.Net/Store/BufferedIndexOutput.cs
index ba2f36c38..48893dc91 100644
--- a/src/Lucene.Net/Store/BufferedIndexOutput.cs
+++ b/src/Lucene.Net/Store/BufferedIndexOutput.cs
@@ -78,13 +78,20 @@ public override void WriteByte(byte b)
 
         public override void WriteBytes(byte[] b, int offset, int length)
         {
+            WriteBytes(b.AsSpan(offset, length));
+        }
+
+        // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+        public override void WriteBytes(ReadOnlySpan<byte> source)
+        {
+            int length = source.Length;
             if (buffer is null) buffer = new byte[bufferSize]; // LUCENENET: Lazy-load the buffer, so we don't force all subclasses to allocate it
             int bytesLeft = bufferSize - bufferPosition;
             // is there enough space in the buffer?
             if (bytesLeft >= length)
             {
                 // we add the data to the end of the buffer
-                Arrays.Copy(b, offset, buffer, bufferPosition, length);
+                Arrays.Copy(source, /*offset*/ 0, buffer, bufferPosition, length);
                 bufferPosition += length;
                 // if the buffer is full, flush it
                 if (bufferSize - bufferPosition == 0)
@@ -103,8 +110,8 @@ public override void WriteBytes(byte[] b, int offset, int length)
                         Flush();
                     }
                     // and write data at once
-                    crc.Update(b, offset, length);
-                    FlushBuffer(b, offset, length);
+                    crc.Update(source);
+                    FlushBuffer(source);
                     bufferStart += length;
                 }
                 else
@@ -115,7 +122,7 @@ public override void WriteBytes(byte[] b, int offset, int length)
                     while (pos < length)
                     {
                         pieceLength = (length - pos < bytesLeft) ? length - pos : bytesLeft;
-                        Arrays.Copy(b, pos + offset, buffer, bufferPosition, pieceLength);
+                        Arrays.Copy(source, pos /*+ offset*/, buffer, bufferPosition, pieceLength);
                         pos += pieceLength;
                         bufferPosition += pieceLength;
                         // if the buffer is full, flush it
@@ -156,7 +163,17 @@ private void FlushBuffer(byte[] b, int len)
         /// <param name="b"> the bytes to write </param>
         /// <param name="offset"> the offset in the byte array </param>
         /// <param name="len"> the number of bytes to write </param>
-        protected internal abstract void FlushBuffer(byte[] b, int offset, int len);
+        protected internal virtual void FlushBuffer(byte[] b, int offset, int len)
+        {
+            FlushBuffer(b.AsSpan(offset, len));
+        }
+
+        /// <summary>
+        /// Expert: implements buffer write. Writes bytes at the current position in
+        /// the output.
+        /// </summary>
+        /// <param name="source">The bytes to write.</param>
+        protected internal abstract void FlushBuffer(ReadOnlySpan<byte> source);
 
         /// <inheritdoc/>
         protected override void Dispose(bool disposing)
diff --git a/src/Lucene.Net/Store/ByteArrayDataOutput.cs b/src/Lucene.Net/Store/ByteArrayDataOutput.cs
index bffe43d1c..6a5e3d57e 100644
--- a/src/Lucene.Net/Store/ByteArrayDataOutput.cs
+++ b/src/Lucene.Net/Store/ByteArrayDataOutput.cs
@@ -103,10 +103,12 @@ public override void WriteByte(byte b)
             bytes[pos++] = b;
         }
 
-        public override void WriteBytes(byte[] b, int offset, int length)
+        // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+        public override void WriteBytes(ReadOnlySpan<byte> source)
         {
+            int length = source.Length;
             if (Debugging.AssertsEnabled) Debugging.Assert(pos + length <= limit);
-            Arrays.Copy(b, offset, bytes, pos, length);
+            Arrays.Copy(source, /*offset*/ 0, bytes, pos, length);
             pos += length;
         }
     }
diff --git a/src/Lucene.Net/Store/CompoundFileWriter.cs b/src/Lucene.Net/Store/CompoundFileWriter.cs
index 95be8e2a5..5b9d67746 100644
--- a/src/Lucene.Net/Store/CompoundFileWriter.cs
+++ b/src/Lucene.Net/Store/CompoundFileWriter.cs
@@ -420,11 +420,13 @@ public override void WriteByte(byte b)
                 @delegate.WriteByte(b);
             }
 
-            public override void WriteBytes(byte[] b, int offset, int length)
+            // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+            public override void WriteBytes(ReadOnlySpan<byte> source)
             {
+                int length = source.Length;
                 if (Debugging.AssertsEnabled) Debugging.Assert(!closed);
                 writtenBytes += length;
-                @delegate.WriteBytes(b, offset, length);
+                @delegate.WriteBytes(source);
             }
 
             public override long Checksum => @delegate.Checksum;
diff --git a/src/Lucene.Net/Store/DataOutput.cs b/src/Lucene.Net/Store/DataOutput.cs
index 39b5c135a..012ec63cc 100644
--- a/src/Lucene.Net/Store/DataOutput.cs
+++ b/src/Lucene.Net/Store/DataOutput.cs
@@ -1,5 +1,10 @@
 using Lucene.Net.Diagnostics;
+using Lucene.Net.Support.Buffers;
+using Lucene.Net.Util;
+using System;
+using System.Buffers;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
 
 namespace Lucene.Net.Store
@@ -21,9 +26,6 @@ namespace Lucene.Net.Store
      * limitations under the License.
      */
 
-    using BytesRef = Lucene.Net.Util.BytesRef;
-    using UnicodeUtil = Lucene.Net.Util.UnicodeUtil;
-
     /// <summary>
     /// Abstract base class for performing write operations of Lucene's low-level
     /// data types.
@@ -60,7 +62,16 @@ public virtual void WriteBytes(byte[] b, int length)
         /// <param name="offset"> the offset in the byte array </param>
         /// <param name="length"> the number of bytes to write </param>
         /// <seealso cref="DataInput.ReadBytes(byte[], int, int)"/>
-        public abstract void WriteBytes(byte[] b, int offset, int length);
+        public virtual void WriteBytes(byte[] b, int offset, int length)
+        {
+            WriteBytes(b.AsSpan(offset, length));
+        }
+
+        /// <summary>
+        /// Writes a sequence of bytes. </summary>
+        /// <param name="source"> the bytes to write </param>
+        /// <seealso cref="DataInput.ReadBytes(Span{Byte})"/>
+        public abstract void WriteBytes(ReadOnlySpan<byte> source);
 
         /// <summary>
         /// Writes an <see cref="int"/> as four bytes.
diff --git a/src/Lucene.Net/Store/FSDirectory.cs b/src/Lucene.Net/Store/FSDirectory.cs
index 637184cee..32f2abe57 100644
--- a/src/Lucene.Net/Store/FSDirectory.cs
+++ b/src/Lucene.Net/Store/FSDirectory.cs
@@ -538,25 +538,27 @@ public override void WriteByte(byte b)
             }
 
             /// <inheritdoc/>
-            public override void WriteBytes(byte[] b, int offset, int length)
+            // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+            public override void WriteBytes(ReadOnlySpan<byte> source)
             {
                 // LUCENENET specific: Guard to ensure we aren't disposed.
                 if (!isOpen)
                     throw AlreadyClosedException.Create(this.GetType().FullName, "This FSIndexOutput is disposed.");
 
-                crc.Update(b, offset, length);
-                file.Write(b, offset, length);
+                crc.Update(source);
+                file.Write(source);
             }
 
             /// <inheritdoc/>
-            protected internal override void FlushBuffer(byte[] b, int offset, int size)
+            // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+            protected internal override void FlushBuffer(ReadOnlySpan<byte> source)
             {
                 // LUCENENET specific: Guard to ensure we aren't disposed.
                 if (!isOpen)
                     throw AlreadyClosedException.Create(this.GetType().FullName, "This FSIndexOutput is disposed.");
 
-                crc.Update(b, offset, size);
-                file.Write(b, offset, size);
+                crc.Update(source);
+                file.Write(source);
             }
 
             /// <inheritdoc/>
diff --git a/src/Lucene.Net/Store/OutputStreamDataOutput.cs b/src/Lucene.Net/Store/OutputStreamDataOutput.cs
index 0c9abfb1e..e5d13e7bb 100644
--- a/src/Lucene.Net/Store/OutputStreamDataOutput.cs
+++ b/src/Lucene.Net/Store/OutputStreamDataOutput.cs
@@ -1,3 +1,4 @@
+using Lucene.Net.Support.IO;
 using System;
 using System.IO;
 using System.Threading;
@@ -59,9 +60,10 @@ public override void WriteByte(byte b)
             _os.WriteByte(b);
         }
 
-        public override void WriteBytes(byte[] b, int offset, int length)
+        // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+        public override void WriteBytes(ReadOnlySpan<byte> source)
         {
-            _os.Write(b, offset, length);
+            _os.Write(source);
         }
 
         /// <summary>
diff --git a/src/Lucene.Net/Store/RAMOutputStream.cs b/src/Lucene.Net/Store/RAMOutputStream.cs
index 4027b3010..e46d08fc8 100644
--- a/src/Lucene.Net/Store/RAMOutputStream.cs
+++ b/src/Lucene.Net/Store/RAMOutputStream.cs
@@ -160,10 +160,13 @@ public override void WriteByte(byte b)
             currentBuffer[bufferPosition++] = b;
         }
 
-        public override void WriteBytes(byte[] b, int offset, int len)
+        // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+        public override void WriteBytes(ReadOnlySpan<byte> source)
         {
-            if (Debugging.AssertsEnabled) Debugging.Assert(b != null);
-            crc.Update(b, offset, len);
+            int offset = 0;
+            int len = source.Length;
+            //if (Debugging.AssertsEnabled) Debugging.Assert(b != null); // LUCENENET: Not needed
+            crc.Update(source);
             while (len > 0)
             {
                 if (bufferPosition == bufferLength)
@@ -174,7 +177,7 @@ public override void WriteBytes(byte[] b, int offset, int len)
 
                 int remainInBuffer = currentBuffer.Length - bufferPosition;
                 int bytesToCopy = len < remainInBuffer ? len : remainInBuffer;
-                Arrays.Copy(b, offset, currentBuffer, bufferPosition, bytesToCopy);
+                Arrays.Copy(source, offset, currentBuffer, bufferPosition, bytesToCopy);
                 offset += bytesToCopy;
                 len -= bytesToCopy;
                 bufferPosition += bytesToCopy;
diff --git a/src/Lucene.Net/Store/RateLimitedIndexOutput.cs b/src/Lucene.Net/Store/RateLimitedIndexOutput.cs
index e8411fda5..7adbb5aa3 100644
--- a/src/Lucene.Net/Store/RateLimitedIndexOutput.cs
+++ b/src/Lucene.Net/Store/RateLimitedIndexOutput.cs
@@ -48,16 +48,19 @@ internal RateLimitedIndexOutput(RateLimiter rateLimiter, IndexOutput @delegate)
             this.rateLimiter = rateLimiter;
         }
 
-        protected internal override void FlushBuffer(byte[] b, int offset, int len)
+        /// <inheritdoc/>
+        // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+        protected internal override void FlushBuffer(ReadOnlySpan<byte> source)
         {
+            int len = source.Length;
             rateLimiter.Pause(len);
             if (bufferedDelegate != null)
             {
-                bufferedDelegate.FlushBuffer(b, offset, len);
+                bufferedDelegate.FlushBuffer(source);
             }
             else
             {
-                @delegate.WriteBytes(b, offset, len);
+                @delegate.WriteBytes(source);
             }
         }
 
diff --git a/src/Lucene.Net/Support/CRC32.cs b/src/Lucene.Net/Support/CRC32.cs
index 5046d94ce..339ab7c7c 100644
--- a/src/Lucene.Net/Support/CRC32.cs
+++ b/src/Lucene.Net/Support/CRC32.cs
@@ -19,6 +19,8 @@
  *
 */
 
+using System;
+
 namespace Lucene.Net.Support
 {
     internal class CRC32 : IChecksum
@@ -62,15 +64,16 @@ public void Update(int bval)
 
         public void Update(byte[] buf, int off, int len)
         {
-            uint c = ~crc;
-            while (--len >= 0)
-                c = crcTable[(c ^ buf[off++]) & 0xff] ^ (c >> 8);
-            crc = ~c;
+            Update(buf.AsSpan(off, len));
         }
 
-        public void Update(byte[] buf)
+        public void Update(ReadOnlySpan<byte> bytes)
         {
-            Update(buf, 0, buf.Length);
+            int off = 0; int len = bytes.Length;
+            uint c = ~crc;
+            while (--len >= 0)
+                c = crcTable[(c ^ bytes[off++]) & 0xff] ^ (c >> 8);
+            crc = ~c;
         }
     }
 }
diff --git a/src/Lucene.Net/Support/IChecksum.cs b/src/Lucene.Net/Support/IChecksum.cs
index 82c02ab65..ed3f0a682 100644
--- a/src/Lucene.Net/Support/IChecksum.cs
+++ b/src/Lucene.Net/Support/IChecksum.cs
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
+using System;
+
 namespace Lucene.Net.Support
 {
     /// <summary>
@@ -26,10 +28,10 @@ internal interface IChecksum
 
         void Update(int b);
 
-        void Update(byte[] b);
-
         void Update(byte[] b, int offset, int length);
 
+        void Update(ReadOnlySpan<byte> bytes);
+
         long Value { get; }
     }
 }
diff --git a/src/Lucene.Net/Util/Fst/BytesStore.cs b/src/Lucene.Net/Util/Fst/BytesStore.cs
index aabb980e3..77e93fc64 100644
--- a/src/Lucene.Net/Util/Fst/BytesStore.cs
+++ b/src/Lucene.Net/Util/Fst/BytesStore.cs
@@ -97,14 +97,18 @@ public override void WriteByte(byte b)
             current[nextWrite++] = b;
         }
 
-        public override void WriteBytes(byte[] b, int offset, int len)
+        /// <inheritdoc/>
+        // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+        public override void WriteBytes(ReadOnlySpan<byte> source)
         {
+            int offset = 0;
+            int len = source.Length;
             while (len > 0)
             {
                 int chunk = blockSize - nextWrite;
                 if (len <= chunk)
                 {
-                    Arrays.Copy(b, offset, current, nextWrite, len);
+                    Arrays.Copy(source, offset, current, nextWrite, len);
                     nextWrite += len;
                     break;
                 }
@@ -112,7 +116,7 @@ public override void WriteBytes(byte[] b, int offset, int len)
                 {
                     if (chunk > 0)
                     {
-                        Arrays.Copy(b, offset, current, nextWrite, chunk);
+                        Arrays.Copy(source, offset, current, nextWrite, chunk);
                         offset += chunk;
                         len -= chunk;
                     }
diff --git a/src/Lucene.Net/Util/GrowableByteArrayDataOutput.cs b/src/Lucene.Net/Util/GrowableByteArrayDataOutput.cs
index ac5706c07..f2894e14d 100644
--- a/src/Lucene.Net/Util/GrowableByteArrayDataOutput.cs
+++ b/src/Lucene.Net/Util/GrowableByteArrayDataOutput.cs
@@ -1,4 +1,5 @@
 using Lucene.Net.Support;
+using System;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
 
@@ -63,12 +64,14 @@ public override void WriteByte(byte b)
             bytes[Length++] = b;
         }
 
+        // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public override void WriteBytes(byte[] b, int off, int len)
+        public override void WriteBytes(ReadOnlySpan<byte> source)
         {
+            int len = source.Length;
             int newLength = Length + len;
             bytes = ArrayUtil.Grow(bytes, newLength);
-            Arrays.Copy(b, off, bytes, Length, len);
+            Arrays.Copy(source, /*off*/ 0, bytes, Length, len);
             Length = newLength;
         }
     }
diff --git a/src/Lucene.Net/Util/PagedBytes.cs b/src/Lucene.Net/Util/PagedBytes.cs
index 4d9782e7b..3ea815063 100644
--- a/src/Lucene.Net/Util/PagedBytes.cs
+++ b/src/Lucene.Net/Util/PagedBytes.cs
@@ -442,9 +442,12 @@ public override void WriteByte(byte b)
                 outerInstance.currentBlock[outerInstance.upto++] = (byte)b;
             }
 
-            public override void WriteBytes(byte[] b, int offset, int length)
+            // LUCENENET: Use ReadOnlySpan<byte> instead of byte[] for better compatibility.
+            public override void WriteBytes(ReadOnlySpan<byte> source)
             {
-                if (Debugging.AssertsEnabled) Debugging.Assert(b.Length >= offset + length);
+                int offset = 0;
+                int length = source.Length;
+                //if (Debugging.AssertsEnabled) Debugging.Assert(bytes.Length >= offset + length); // LUCENENET: Not needed
                 if (length == 0)
                 {
                     return;
@@ -468,7 +471,7 @@ public override void WriteBytes(byte[] b, int offset, int length)
                     int blockLeft = outerInstance.blockSize - outerInstance.upto;
                     if (blockLeft < left)
                     {
-                        Arrays.Copy(b, offset, outerInstance.currentBlock, outerInstance.upto, blockLeft);
+                        Arrays.Copy(source, offset, outerInstance.currentBlock, outerInstance.upto, blockLeft);
                         outerInstance.blocks.Add(outerInstance.currentBlock);
                         outerInstance.blockEnd.Add(outerInstance.blockSize);
                         outerInstance.currentBlock = new byte[outerInstance.blockSize];
@@ -478,7 +481,7 @@ public override void WriteBytes(byte[] b, int offset, int length)
                     else
                     {
                         // Last block
-                        Arrays.Copy(b, offset, outerInstance.currentBlock, outerInstance.upto, left);
+                        Arrays.Copy(source, offset, outerInstance.currentBlock, outerInstance.upto, left);
                         outerInstance.upto += left;
                         break;
                     }