diff --git a/src/J2N/IO/Buffer.cs b/src/J2N/IO/Buffer.cs index 6b8455e8..090a1a2c 100644 --- a/src/J2N/IO/Buffer.cs +++ b/src/J2N/IO/Buffer.cs @@ -68,6 +68,8 @@ namespace J2N.IO /// public abstract class Buffer { + internal const int ByteStackBufferSize = 128; + /// /// UnsetMark means the mark has not been set. /// diff --git a/src/J2N/IO/ByteBuffer.cs b/src/J2N/IO/ByteBuffer.cs index 117bb943..8fbdb342 100644 --- a/src/J2N/IO/ByteBuffer.cs +++ b/src/J2N/IO/ByteBuffer.cs @@ -16,7 +16,9 @@ */ #endregion +using J2N.Buffers; using System; +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -301,6 +303,8 @@ public int ArrayOffset /// equals to other; a positive value if this is greater /// than other. /// + /// Note to inheritors: This implementation reads bytes one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public virtual int CompareTo(ByteBuffer? other) { if (other is null) return 1; // Using 1 if other is null as specified here: https://stackoverflow.com/a/4852537 @@ -354,6 +358,8 @@ public virtual int CompareTo(ByteBuffer? other) /// true if this byte buffer is equal to , /// false otherwise. /// + /// Note to inheritors: This implementation reads bytes one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override bool Equals(object? other) { if (other is null || !(other is ByteBuffer otherBuffer)) @@ -416,6 +422,8 @@ public virtual ByteBuffer Get(byte[] destination) /// greater than destination.Length - offset /// /// This buffer. + /// Note to inheritors: This implementation reads bytes one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// /// plus indicates a position not within this instance. /// @@ -447,9 +455,35 @@ public virtual ByteBuffer Get(byte[] destination, int offset, int length) // J2N return this; } + /// + /// Reads bytes from the current position into the specified span, + /// and increases the position by the number of bytes read. + /// + /// The property is used to determine + /// how many bytes to read. + /// + /// The target span, sliced to the proper position, if necessary. + /// This buffer. + /// Note to inheritors: This implementation reads bytes one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is greater than + /// . + public virtual ByteBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + { + throw new BufferUnderflowException(); + } + for (int i = 0; i < length; i++) + { + destination[i] = Get(); + } + return this; + } + /// /// Returns the byte at the specified index and does not change the position. - /// /// /// The index, must not be negative and less than limit. /// The byte at the specified index. @@ -617,6 +651,8 @@ public virtual ByteBuffer Get(byte[] destination, int offset, int length) // J2N /// position, limit, capacity and mark don't affect the hash code. /// /// The hash code calculated from the remaining bytes. + /// Note to inheritors: This implementation reads bytes one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override int GetHashCode() { int myPosition = position; @@ -696,7 +732,7 @@ public ByteBuffer SetOrder(ByteOrder byteOrder) /// /// The source byte array. /// This buffer. - /// If is less than src.Length. + /// If is less than source.Length. /// If no changes may be made to the contents of this buffer. /// If is null. public ByteBuffer Put(byte[] source) @@ -721,6 +757,8 @@ public ByteBuffer Put(byte[] source) /// greater than source.Length - offset. /// /// This buffer. + /// Note to inheritors: This implementation writes bytes one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// If is less than . /// /// plus indicates a position not within this instance. @@ -751,6 +789,32 @@ public virtual ByteBuffer Put(byte[] source, int offset, int length) // J2N TODO return this; } + /// + /// Writes bytes in the given span to the current position and increases the + /// position by the number of bytes written. + /// + /// Calling this method has a similar effect as + /// Put(source, 0, source.Length). + /// + /// The source span. + /// This buffer. + /// Note to inheritors: This implementation writes bytes one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is less than source.Length. + /// If no changes may be made to the contents of this buffer. + public virtual ByteBuffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + for (int i = 0; i < length; i++) + { + Put(source[i]); + } + return this; + } + /// /// Writes all the remaining bytes of the byte buffer to this /// buffer's current position, and increases both buffers' position by the @@ -773,10 +837,21 @@ public virtual ByteBuffer Put(ByteBuffer source) if (IsReadOnly) throw new ReadOnlyBufferException(); // J2N: Harmony has a bug - it shouldn't read the source and change its position unless this buffer is writable - byte[] contents = new byte[source.Remaining]; - source.Get(contents); - Put(contents); - return this; + int length = source.Remaining; + byte[]? arrayToReturnToPool = null; + Span contents = length > ByteStackBufferSize + ? (arrayToReturnToPool = ArrayPool.Shared.Rent(length)).AsSpan(0, length) + : stackalloc byte[length]; + try + { + source.Get(contents); + Put(contents); + return this; + } + finally + { + ArrayPool.Shared.ReturnIfNotNull(arrayToReturnToPool); + } } /// @@ -989,5 +1064,4 @@ public override string ToString() return buf.ToString(); } } - } diff --git a/src/J2N/IO/CharArrayBuffer.cs b/src/J2N/IO/CharArrayBuffer.cs index 49567f2e..144171ab 100644 --- a/src/J2N/IO/CharArrayBuffer.cs +++ b/src/J2N/IO/CharArrayBuffer.cs @@ -88,6 +88,17 @@ public override sealed CharBuffer Get(char[] destination, int offset, int length return this; } + public override CharBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + throw new BufferUnderflowException(); + + backingArray.AsSpan(offset + position, length).CopyTo(destination); + position += length; + return this; + } + //public override sealed bool IsDirect => false; public override sealed ByteOrder Order => ByteOrder.NativeOrder; diff --git a/src/J2N/IO/CharBuffer.cs b/src/J2N/IO/CharBuffer.cs index 2a685f39..37547b87 100644 --- a/src/J2N/IO/CharBuffer.cs +++ b/src/J2N/IO/CharBuffer.cs @@ -16,8 +16,10 @@ */ #endregion +using J2N.Buffers; using J2N.Text; using System; +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -121,7 +123,7 @@ public static CharBuffer Wrap(string characterSequence) { if (characterSequence is null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.characterSequence); - return new CharSequenceAdapter(characterSequence.AsCharSequence()); // J2N TODO: Create StringAdapter? + return new CharReadOnlyMemoryAdapter(characterSequence.AsMemory()); } /// @@ -159,7 +161,7 @@ public static CharBuffer Wrap(string characterSequence, int startIndex, int leng if (startIndex > characterSequence.Length - length) // Checks for int overflow ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(startIndex, length); - return new CharSequenceAdapter(characterSequence.AsCharSequence()) // J2N TODO: Create StringAdapter? + return new CharReadOnlyMemoryAdapter(characterSequence.AsMemory()) { position = startIndex, limit = startIndex + length @@ -174,12 +176,19 @@ public static CharBuffer Wrap(string characterSequence, int startIndex, int leng /// /// The which the new buffer will be based on. /// The created char buffer. + /// 's property is known to perform + /// poorly and some operations on a raw will suffer as a result. It is generally + /// preferable to use to allocate a temporary array and then use the + /// method to transfer the chars to the temporary array, + /// then use the array to call the overload for better performance. + /// Be sure to call to return the array to the pool when you + /// are finished with the instance. /// If is null. public static CharBuffer Wrap(StringBuilder characterSequence) { if (characterSequence is null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.characterSequence); - return new CharSequenceAdapter(characterSequence.AsCharSequence()); // J2N TODO: Create StringBuilderAdapter? + return new StringBuilderAdapter(characterSequence); } /// @@ -198,6 +207,13 @@ public static CharBuffer Wrap(StringBuilder characterSequence) /// The end index, must be no less than and no /// greater than characterSequence.Length. /// The created char buffer. + /// 's property is known to perform + /// poorly and some operations on a raw will suffer as a result. It is generally + /// preferable to use to allocate a temporary array and then use the + /// method to transfer the chars to the temporary array, + /// then use the array to call the overload for better performance. + /// Be sure to call to return the array to the pool when you + /// are finished with the instance. /// /// plus indicates a position not within this instance. /// @@ -217,7 +233,7 @@ public static CharBuffer Wrap(StringBuilder characterSequence, int startIndex, i if (startIndex > characterSequence.Length - length) // Checks for int overflow ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(startIndex, length); - return new CharSequenceAdapter(characterSequence.AsCharSequence()) // J2N TODO: Create StringAdapter? + return new StringBuilderAdapter(characterSequence) { position = startIndex, limit = startIndex + length @@ -237,8 +253,16 @@ public static CharBuffer Wrap(StringBuilder characterSequence, int startIndex, i /// If is null. public static CharBuffer Wrap(ICharSequence characterSequence) { - if (characterSequence is null) + if (characterSequence is null || !characterSequence.HasValue) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.characterSequence); + + if (characterSequence is StringCharSequence str) + return new CharReadOnlyMemoryAdapter(str.Value!.AsMemory()); + if (characterSequence is CharArrayCharSequence ca) + return new ReadOnlyCharArrayBuffer(ca.Length, ca.Value!, arrayOffset: 0); + if (characterSequence is StringBuilderCharSequence sb) + return new StringBuilderAdapter(sb.Value!); + return new CharSequenceAdapter(characterSequence); } @@ -268,7 +292,7 @@ public static CharBuffer Wrap(ICharSequence characterSequence) /// If is null. public static CharBuffer Wrap(ICharSequence characterSequence, int startIndex, int length) { - if (characterSequence is null) + if (characterSequence is null || !characterSequence.HasValue) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.characterSequence); if (startIndex < 0) ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(startIndex, ExceptionArgument.startIndex); @@ -277,6 +301,31 @@ public static CharBuffer Wrap(ICharSequence characterSequence, int startIndex, i if (startIndex > characterSequence.Length - length) // Checks for int overflow ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(startIndex, length); + if (characterSequence is StringCharSequence str) + { + return new CharReadOnlyMemoryAdapter(str.Value!.AsMemory()) + { + position = startIndex, + limit = startIndex + length + }; + } + if (characterSequence is CharArrayCharSequence ca) + { + return new ReadOnlyCharArrayBuffer(ca.Length, ca.Value!, arrayOffset: 0) + { + position = startIndex, + limit = startIndex + length + }; + } + if (characterSequence is StringBuilderCharSequence sb) + { + return new StringBuilderAdapter(sb.Value!) + { + position = startIndex, + limit = startIndex + length + }; + } + return new CharSequenceAdapter(characterSequence) { position = startIndex, @@ -364,6 +413,8 @@ public char this[int index] /// A negative value if this is less than ; 0 if /// this equals to ; a positive valie if this is /// greater than . + /// Note to inheritors: This implementation reads chars one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public virtual int CompareTo(CharBuffer? other) { if (other is null) return 1; // Using 1 if other is null as specified here: https://stackoverflow.com/a/4852537 @@ -414,6 +465,8 @@ public virtual int CompareTo(CharBuffer? other) /// /// The object to compare with this char buffer. /// true if this char buffer is equal to , false otherwise. + /// Note to inheritors: This implementation reads chars one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override bool Equals(object? other) { if (other is null || !(other is CharBuffer otherBuffer)) @@ -472,6 +525,8 @@ public virtual CharBuffer Get(char[] destination) /// The number of chars to read, must be no less than zero and no /// greater than destination.Length - offset. /// This buffer. + /// Note to inheritors: This implementation reads chars one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// /// plus indicates a position not within this instance. /// @@ -503,6 +558,33 @@ public virtual CharBuffer Get(char[] destination, int offset, int length) // J2N return this; } + /// + /// Reads chars from the current position into the specified span, + /// and increases the position by the number of chars read. + /// + /// The property is used to determine + /// how many chars to read. + /// + /// The target span, sliced to the proper position, if necessary. + /// This buffer. + /// Note to inheritors: This implementation reads chars one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is greater than + /// . + public virtual CharBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + { + throw new BufferUnderflowException(); + } + for (int i = 0; i < length; i++) + { + destination[i] = Get(); + } + return this; + } + /// /// Returns a char at the specified ; the position is not changed. /// @@ -530,6 +612,8 @@ public virtual CharBuffer Get(char[] destination, int offset, int length) // J2N /// position, limit, capacity and mark don't affect the hash code. /// /// The hash code calculated from the remaining chars. + /// Note to inheritors: This implementation reads chars one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override int GetHashCode() { int myPosition = position; @@ -624,6 +708,8 @@ public CharBuffer Put(char[] source) /// The number of chars to write, must be no less than zero and no /// greater than source.Length - offset. /// This buffer. + /// Note to inheritors: This implementation writes chars one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// If is less than . /// /// plus indicates a position not within this instance. @@ -656,6 +742,32 @@ public virtual CharBuffer Put(char[] source, int offset, int length) // J2N TODO return this; } + /// + /// Writes chars in the given span to the current position and increases the + /// position by the number of chars written. + /// + /// Calling this method has a similar effect as + /// Put(source, 0, source.Length). + /// + /// The source span. + /// This buffer. + /// Note to inheritors: This implementation writes chars one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is less than source.Length. + /// If no changes may be made to the contents of this buffer. + public virtual CharBuffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + for (int i = 0; i < length; i++) + { + Put(source[i]); + } + return this; + } + /// /// Writes all the remaining chars of the char buffer to this /// buffer's current position, and increases both buffers' position by the @@ -678,10 +790,21 @@ public virtual CharBuffer Put(CharBuffer source) if (IsReadOnly) throw new ReadOnlyBufferException(); // J2N: Harmony has a bug - it shouldn't read the source and change its position unless this buffer is writable - char[] contents = new char[source.Remaining]; - source.Get(contents); - Put(contents); - return this; + int length = source.Remaining; + char[]? arrayToReturnToPool = null; + Span contents = length * sizeof(char) > ByteStackBufferSize + ? (arrayToReturnToPool = ArrayPool.Shared.Rent(length)).AsSpan(0, length) + : stackalloc char[length]; + try + { + source.Get(contents); + Put(contents); + return this; + } + finally + { + ArrayPool.Shared.ReturnIfNotNull(arrayToReturnToPool); + } } /// diff --git a/src/J2N/IO/CharReadOnlyMemoryAdapter.cs b/src/J2N/IO/CharReadOnlyMemoryAdapter.cs new file mode 100644 index 00000000..a9691d13 --- /dev/null +++ b/src/J2N/IO/CharReadOnlyMemoryAdapter.cs @@ -0,0 +1,193 @@ +#region Copyright 2010 by Apache Harmony, Licensed under the Apache License, Version 2.0 +/* 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. + */ +#endregion + +using System; + +namespace J2N.IO +{ + /// + /// This class wraps a char sequence to be a buffer. + /// + /// Implementation notice: + /// + /// based buffer is always readonly. + /// + /// + // J2N: Note that this does the same job as ReadOnlyCharArrayBuffer and could effectively replace it, + // but also supports string without any allocations. We are only keeping ReadOnlyCharArrayBuffer around + // because it is paired and we cannot simply replace ReadWriteCharArrayBuffer's char[] backing field + // with Memory because the char[] is exposed through the Array/ProtectedArray properties in those cases. + internal sealed class CharReadOnlyMemoryAdapter : CharBuffer + { + internal static CharReadOnlyMemoryAdapter Copy(CharReadOnlyMemoryAdapter other) + { + return new CharReadOnlyMemoryAdapter(other.sequence) + { + limit = other.limit, + position = other.position, + mark = other.mark + }; + } + + internal readonly object? backingInstance; // Keeps the memory behind sequence in scope + internal readonly ReadOnlyMemory sequence; + + internal CharReadOnlyMemoryAdapter(ReadOnlyMemory chseq) + : base(chseq.Length) + { + sequence = chseq; + sequence.TryGetReference(ref backingInstance); + } + + public override CharBuffer AsReadOnlyBuffer() => Duplicate(); + + public override CharBuffer Compact() + { + throw new ReadOnlyBufferException(); + } + + public override CharBuffer Duplicate() => Copy(this); + + public override char Get() + { + if (position == limit) + { + throw new BufferUnderflowException(); + } + return sequence.Span[position++]; + } + + public override char Get(int index) + { + if ((uint)index >= (uint)limit) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return sequence.Span[index]; + } + + public override sealed CharBuffer Get(char[] destination, int offset, int length) // J2N TODO: API - rename startIndex instead of offset + { + if (destination is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destination); + if (offset < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(offset, ExceptionArgument.offset); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(length, ExceptionArgument.length); + if (offset > destination.Length - length) // Checks for int overflow + ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(offset, length); + if (length > Remaining) + throw new BufferUnderflowException(); + + int newPosition = position + length; + sequence.Slice(position, length).CopyTo(destination.AsMemory(offset, length)); + position = newPosition; + return this; + } + + public override CharBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + throw new BufferUnderflowException(); + + int newPosition = position + length; + sequence.Span.Slice(position, length).CopyTo(destination); + position = newPosition; + return this; + } + + //public override bool IsDirect => false; + + public override bool IsReadOnly => true; + + public override ByteOrder Order => ByteOrder.NativeOrder; + + protected override char[] ProtectedArray + { + get { throw new NotSupportedException(); } + } + + protected override int ProtectedArrayOffset + { + get { throw new NotSupportedException(); } + } + + protected override bool ProtectedHasArray => false; + + public override CharBuffer Put(char value) + { + throw new ReadOnlyBufferException(); + } + + public override CharBuffer Put(int index, char value) + { + throw new ReadOnlyBufferException(); + } + + public override sealed CharBuffer Put(char[] source, int offset, int length) // J2N TODO: API - rename startIndex instead of offset + { + if (source is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); + if (offset < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(offset, ExceptionArgument.offset); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(length, ExceptionArgument.length); + if (offset > source.Length - length) // Checks for int overflow + ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(offset, length); + if (length > Remaining) + throw new BufferOverflowException(); + + throw new ReadOnlyBufferException(); + } + + public override CharBuffer Put(string source, int startIndex, int length) + { + if (source is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); + if (startIndex < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(startIndex, ExceptionArgument.startIndex); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(length, ExceptionArgument.length); + if (startIndex > source.Length - length) // Checks for int overflow + ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(startIndex, length); + + throw new ReadOnlyBufferException(); + } + + public override CharBuffer Slice() + { + return new CharReadOnlyMemoryAdapter(sequence.Slice(position, limit - position)); // J2N: Corrected 2nd parameter + } + + public override CharBuffer Subsequence(int startIndex, int length) + { + if (startIndex < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(startIndex, ExceptionArgument.startIndex); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(length, ExceptionArgument.length); + if (startIndex > Remaining - length) // Checks for int overflow + ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(startIndex, length); + + CharReadOnlyMemoryAdapter result = Copy(this); + result.position = position + startIndex; + result.limit = position + startIndex + length; + return result; + } + } +} diff --git a/src/J2N/IO/CharSequenceAdapter.cs b/src/J2N/IO/CharSequenceAdapter.cs index 6fd62654..4c68f373 100644 --- a/src/J2N/IO/CharSequenceAdapter.cs +++ b/src/J2N/IO/CharSequenceAdapter.cs @@ -18,7 +18,6 @@ using J2N.Text; using System; -using System.Diagnostics.CodeAnalysis; namespace J2N.IO @@ -92,7 +91,19 @@ public override sealed CharBuffer Get(char[] destination, int offset, int length throw new BufferUnderflowException(); int newPosition = position + length; - sequence.ToString().CopyTo(position, destination, offset, length); + sequence.ToString().CopyTo(position, destination, offset, length); // J2N TODO: Create specialized adapter for StringBuilder as a separate class and then loop through the indexer here + position = newPosition; + return this; + } + + public override CharBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + throw new BufferUnderflowException(); + + int newPosition = position + length; + sequence.ToString().AsSpan(position, length).CopyTo(destination); // J2N TODO: Create specialized adapter for StringBuilder as a separate class and then loop through the indexer here position = newPosition; return this; } diff --git a/src/J2N/IO/CharToByteBufferAdapter.cs b/src/J2N/IO/CharToByteBufferAdapter.cs index 8b08a816..71ed7a3e 100644 --- a/src/J2N/IO/CharToByteBufferAdapter.cs +++ b/src/J2N/IO/CharToByteBufferAdapter.cs @@ -161,6 +161,8 @@ public override char Get(int index) return byteBuffer.GetChar(index << 1); } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) to chars and override Get(Span) + //public override bool IsDirect => byteBuffer.IsDirect; public override bool IsReadOnly => byteBuffer.IsReadOnly; @@ -199,6 +201,8 @@ public override CharBuffer Put(int index, char value) return this; } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) from chars and override Put(ReadOnlySpan) + public override CharBuffer Slice() { byteBuffer.Limit = limit << 1; diff --git a/src/J2N/IO/DoubleArrayBuffer.cs b/src/J2N/IO/DoubleArrayBuffer.cs index a59c27da..6da4a8b9 100644 --- a/src/J2N/IO/DoubleArrayBuffer.cs +++ b/src/J2N/IO/DoubleArrayBuffer.cs @@ -87,6 +87,16 @@ public override sealed DoubleBuffer Get(double[] destination, int offset, int le return this; } + public override DoubleBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + throw new BufferUnderflowException(); + + backingArray.AsSpan(offset + position, length).CopyTo(destination); + position += length; + return this; + } //public override sealed bool IsDirect => false; diff --git a/src/J2N/IO/DoubleBuffer.cs b/src/J2N/IO/DoubleBuffer.cs index 7d0fabe0..ea7d82bb 100644 --- a/src/J2N/IO/DoubleBuffer.cs +++ b/src/J2N/IO/DoubleBuffer.cs @@ -16,7 +16,9 @@ */ #endregion +using J2N.Buffers; using System; +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -164,6 +166,8 @@ internal DoubleBuffer(int capacity) /// A negative value if this is less than ; 0 if this /// equals to ; a positive value if this is greater /// than . + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public virtual int CompareTo(DoubleBuffer? other) { if (other is null) return 1; // Using 1 if other is null as specified here: https://stackoverflow.com/a/4852537 @@ -206,6 +210,8 @@ public virtual int CompareTo(DoubleBuffer? other) /// The object to compare with this . /// true if this is equal to , /// false otherwise. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override bool Equals(object? other) { if (other is null || !(other is DoubleBuffer otherBuffer)) @@ -268,6 +274,8 @@ public virtual DoubleBuffer Get(double[] destination) /// The number of s to read, must be no less than zero and not /// greater than destination.Length - offset. /// This buffer. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// /// plus indicates a position not within this instance. /// @@ -297,6 +305,33 @@ public virtual DoubleBuffer Get(double[] destination, int offset, int length) // return this; } + /// + /// Reads s from the current position into the specified span, + /// and increases the position by the number of s read. + /// + /// The property is used to determine + /// how many s to read. + /// + /// The target span, sliced to the proper position, if necessary. + /// This buffer. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is greater than + /// . + public virtual DoubleBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + { + throw new BufferUnderflowException(); + } + for (int i = 0; i < length; i++) + { + destination[i] = Get(); + } + return this; + } + /// /// Returns an at the specified ; the position is not changed. /// @@ -318,6 +353,8 @@ public virtual DoubleBuffer Get(double[] destination, int offset, int length) // /// position, limit, capacity and mark don't affect the hash code. /// /// The hash code calculated from the remaining s. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override int GetHashCode() { int myPosition = position; @@ -410,6 +447,8 @@ public DoubleBuffer Put(double[] source) /// The number of s to write, must be no less than zero and not /// greater than source.Length - offset. /// This buffer. + /// Note to inheritors: This implementation writes s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// If is less than . /// /// plus indicates a position not within this instance. @@ -440,6 +479,32 @@ public virtual DoubleBuffer Put(double[] source, int offset, int length) // J2N return this; } + /// + /// Writes s in the given span to the current position and increases the + /// position by the number of s written. + /// + /// Calling this method has a similar effect as + /// Put(source, 0, source.Length). + /// + /// The source span. + /// This buffer. + /// Note to inheritors: This implementation writes s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is less than source.Length. + /// If no changes may be made to the contents of this buffer. + public virtual DoubleBuffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + for (int i = 0; i < length; i++) + { + Put(source[i]); + } + return this; + } + /// /// Writes all the remaining s of the to this /// buffer's current position, and increases both buffers' position by the @@ -462,10 +527,21 @@ public virtual DoubleBuffer Put(DoubleBuffer source) if (IsReadOnly) throw new ReadOnlyBufferException(); // J2N: Harmony has a bug - it shouldn't read the source and change its position unless this buffer is writable - double[] contents = new double[source.Remaining]; - source.Get(contents); - Put(contents); - return this; + int length = source.Remaining; + double[]? arrayToReturnToPool = null; + Span contents = length * sizeof(double) > ByteStackBufferSize + ? (arrayToReturnToPool = ArrayPool.Shared.Rent(length)).AsSpan(0, length) + : stackalloc double[length]; + try + { + source.Get(contents); + Put(contents); + return this; + } + finally + { + ArrayPool.Shared.ReturnIfNotNull(arrayToReturnToPool); + } } /// diff --git a/src/J2N/IO/DoubleToByteBufferAdapter.cs b/src/J2N/IO/DoubleToByteBufferAdapter.cs index 6c7cc296..b65cd5cf 100644 --- a/src/J2N/IO/DoubleToByteBufferAdapter.cs +++ b/src/J2N/IO/DoubleToByteBufferAdapter.cs @@ -153,6 +153,8 @@ public override double Get(int index) return byteBuffer.GetDouble(index << 3); } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) to doubles and override Get(Span) + //public override bool IsDirect => byteBuffer.IsDirect; public override bool IsReadOnly => byteBuffer.IsReadOnly; @@ -191,6 +193,8 @@ public override DoubleBuffer Put(int index, double value) return this; } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) from doubles and override Put(ReadOnlySpan) + public override DoubleBuffer Slice() { byteBuffer.SetLimit(limit << 3); diff --git a/src/J2N/IO/HeapByteBuffer.cs b/src/J2N/IO/HeapByteBuffer.cs index d399efa2..da12ebac 100644 --- a/src/J2N/IO/HeapByteBuffer.cs +++ b/src/J2N/IO/HeapByteBuffer.cs @@ -83,6 +83,17 @@ public override sealed ByteBuffer Get(byte[] destination, int offset, int length return this; } + public override sealed ByteBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + throw new BufferUnderflowException(); + + backingArray.AsSpan(offset + position, length).CopyTo(destination); + position += length; + return this; + } + public override sealed byte Get() { if (position == limit) diff --git a/src/J2N/IO/Int16ArrayBuffer.cs b/src/J2N/IO/Int16ArrayBuffer.cs index 5a4a988a..ef333417 100644 --- a/src/J2N/IO/Int16ArrayBuffer.cs +++ b/src/J2N/IO/Int16ArrayBuffer.cs @@ -86,6 +86,17 @@ public override sealed Int16Buffer Get(short[] destination, int offset, int leng return this; } + public override sealed Int16Buffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + throw new BufferUnderflowException(); + + backingArray.AsSpan(offset + position, length).CopyTo(destination); + position += length; + return this; + } + //public override sealed bool IsDirect => false; public override sealed ByteOrder Order => ByteOrder.NativeOrder; diff --git a/src/J2N/IO/Int16Buffer.cs b/src/J2N/IO/Int16Buffer.cs index 33c94a17..03f8a720 100644 --- a/src/J2N/IO/Int16Buffer.cs +++ b/src/J2N/IO/Int16Buffer.cs @@ -16,7 +16,9 @@ */ #endregion +using J2N.Buffers; using System; +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -162,6 +164,8 @@ internal Int16Buffer(int capacity) /// A negative value if this is less than ; 0 if /// this equals to ; a positive value if this is /// greater than . + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public virtual int CompareTo(Int16Buffer? other) { if (other is null) return 1; // Using 1 if other is null as specified here: https://stackoverflow.com/a/4852537 @@ -212,6 +216,8 @@ public virtual int CompareTo(Int16Buffer? other) /// /// The object to compare with this . /// true if this is equal to , false otherwise. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override bool Equals(object? other) { if (other is null || !(other is Int16Buffer otherBuffer)) @@ -272,6 +278,8 @@ public virtual Int16Buffer Get(short[] destination) /// The number of s to read, must be no less than zero and /// not greater than destination.Length - offset. /// This buffer. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// /// plus indicates a position not within this instance. /// @@ -301,6 +309,33 @@ public virtual Int16Buffer Get(short[] destination, int offset, int length) // J return this; } + /// + /// Reads s from the current position into the specified span, + /// and increases the position by the number of s read. + /// + /// The property is used to determine + /// how many s to read. + /// + /// The target span, sliced to the proper position, if necessary. + /// This buffer. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is greater than + /// . + public virtual Int16Buffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + { + throw new BufferUnderflowException(); + } + for (int i = 0; i < length; i++) + { + destination[i] = Get(); + } + return this; + } + /// /// Returns the at the specified ; the position is not changed. /// @@ -323,6 +358,8 @@ public virtual Int16Buffer Get(short[] destination, int offset, int length) // J /// position, limit, capacity and mark don't affect the hash code. /// /// The hash code calculated from the remaining s. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override int GetHashCode() { int myPosition = position; @@ -414,6 +451,8 @@ public Int16Buffer Put(short[] source) /// The number of s to write, must be no less than zero and /// not greater than source.Length - offset. /// This buffer. + /// Note to inheritors: This implementation writes s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// If is less than . /// /// plus indicates a position not within this instance. @@ -444,6 +483,32 @@ public virtual Int16Buffer Put(short[] source, int offset, int length) // J2N TO return this; } + /// + /// Writes s in the given span to the current position and increases the + /// position by the number of s written. + /// + /// Calling this method has a similar effect as + /// Put(source, 0, source.Length). + /// + /// The source span. + /// This buffer. + /// Note to inheritors: This implementation writes s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is less than source.Length. + /// If no changes may be made to the contents of this buffer. + public virtual Int16Buffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + for (int i = 0; i < length; i++) + { + Put(source[i]); + } + return this; + } + /// /// Writes all the remaining s of the to this /// buffer's current position, and increases both buffers' position by the @@ -466,10 +531,21 @@ public virtual Int16Buffer Put(Int16Buffer source) if (IsReadOnly) throw new ReadOnlyBufferException(); // J2N: Harmony has a bug - it shouldn't read the source and change its position unless this buffer is writable - short[] contents = new short[source.Remaining]; - source.Get(contents); - Put(contents); - return this; + int length = source.Remaining; + short[]? arrayToReturnToPool = null; + Span contents = length * sizeof(short) > ByteStackBufferSize + ? (arrayToReturnToPool = ArrayPool.Shared.Rent(length)).AsSpan(0, length) + : stackalloc short[length]; + try + { + source.Get(contents); + Put(contents); + return this; + } + finally + { + ArrayPool.Shared.ReturnIfNotNull(arrayToReturnToPool); + } } /// diff --git a/src/J2N/IO/Int16ToByteBufferAdapter.cs b/src/J2N/IO/Int16ToByteBufferAdapter.cs index 9a412bd5..bb7bc71b 100644 --- a/src/J2N/IO/Int16ToByteBufferAdapter.cs +++ b/src/J2N/IO/Int16ToByteBufferAdapter.cs @@ -156,6 +156,8 @@ public override short Get(int index) return byteBuffer.GetInt16(index << 1); } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) to shorts and override Get(Span) + //public override bool IsDirect => byteBuffer.IsDirect; public override bool IsReadOnly => byteBuffer.IsReadOnly; @@ -197,6 +199,8 @@ public override Int16Buffer Put(int index, short value) return this; } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) from shorts and override Put(ReadOnlySpan) + public override Int16Buffer Slice() { byteBuffer.Limit = (limit << 1); diff --git a/src/J2N/IO/Int32ArrayBuffer.cs b/src/J2N/IO/Int32ArrayBuffer.cs index 3e08aefb..2b31d4ba 100644 --- a/src/J2N/IO/Int32ArrayBuffer.cs +++ b/src/J2N/IO/Int32ArrayBuffer.cs @@ -86,6 +86,17 @@ public override sealed Int32Buffer Get(int[] destination, int offset, int length return this; } + public override sealed Int32Buffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + throw new BufferUnderflowException(); + + backingArray.AsSpan(offset + position, length).CopyTo(destination); + position += length; + return this; + } + //public override sealed bool IsDirect => false; public override sealed ByteOrder Order => ByteOrder.NativeOrder; diff --git a/src/J2N/IO/Int32Buffer.cs b/src/J2N/IO/Int32Buffer.cs index a35e5689..90625349 100644 --- a/src/J2N/IO/Int32Buffer.cs +++ b/src/J2N/IO/Int32Buffer.cs @@ -16,7 +16,9 @@ */ #endregion +using J2N.Buffers; using System; +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -161,6 +163,8 @@ internal Int32Buffer(int capacity) /// a negative value if this is less than ; 0 if this /// equals to ; a positive value if this is greater /// than . + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public virtual int CompareTo(Int32Buffer? other) { if (other is null) return 1; // Using 1 if other is null as specified here: https://stackoverflow.com/a/4852537 @@ -212,6 +216,8 @@ public virtual int CompareTo(Int32Buffer? other) /// The object to compare with this . /// true if this is equal to , /// false otherwise. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override bool Equals(object? other) { if (other is null || !(other is Int32Buffer otherBuffer)) @@ -271,6 +277,8 @@ public virtual Int32Buffer Get(int[] destination) /// The number of s to read, must be no less than zero and not /// greater than destination.Length - offset. /// This buffer. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// /// plus indicates a position not within this instance. /// @@ -300,6 +308,33 @@ public virtual Int32Buffer Get(int[] destination, int offset, int length) // J2N return this; } + /// + /// Reads s from the current position into the specified span, + /// and increases the position by the number of s read. + /// + /// The property is used to determine + /// how many s to read. + /// + /// The target span, sliced to the proper position, if necessary. + /// This buffer. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is greater than + /// . + public virtual Int32Buffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + { + throw new BufferUnderflowException(); + } + for (int i = 0; i < length; i++) + { + destination[i] = Get(); + } + return this; + } + /// /// Returns an at the specified ; the position is not changed. /// @@ -321,6 +356,8 @@ public virtual Int32Buffer Get(int[] destination, int offset, int length) // J2N /// position, limit, capacity and mark don't affect the hash code. /// /// The hash code calculated from the remaining s. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override int GetHashCode() { int myPosition = position; @@ -411,6 +448,8 @@ public Int32Buffer Put(int[] source) /// The number of s to write, must be no less than zero and not /// greater than source.Length - offset. /// This buffer. + /// Note to inheritors: This implementation writes s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// If is less than . /// /// plus indicates a position not within this instance. @@ -441,6 +480,32 @@ public virtual Int32Buffer Put(int[] source, int offset, int length) // J2N TODO return this; } + /// + /// Writes s in the given span to the current position and increases the + /// position by the number of s written. + /// + /// Calling this method has a similar effect as + /// Put(source, 0, source.Length). + /// + /// The source span. + /// This buffer. + /// Note to inheritors: This implementation writes s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is less than source.Length. + /// If no changes may be made to the contents of this buffer. + public virtual Int32Buffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + for (int i = 0; i < length; i++) + { + Put(source[i]); + } + return this; + } + /// /// Writes all the remaining s of the to this /// buffer's current position, and increases both buffers' position by the @@ -463,10 +528,21 @@ public virtual Int32Buffer Put(Int32Buffer source) if (IsReadOnly) throw new ReadOnlyBufferException(); // J2N: Harmony has a bug - it shouldn't read the source and change its position unless this buffer is writable - int[] contents = new int[source.Remaining]; - source.Get(contents); - Put(contents); - return this; + int length = source.Remaining; + int[]? arrayToReturnToPool = null; + Span contents = length * sizeof(int) > ByteStackBufferSize + ? (arrayToReturnToPool = ArrayPool.Shared.Rent(length)).AsSpan(0, length) + : stackalloc int[length]; + try + { + source.Get(contents); + Put(contents); + return this; + } + finally + { + ArrayPool.Shared.ReturnIfNotNull(arrayToReturnToPool); + } } /// diff --git a/src/J2N/IO/Int32ToByteBufferAdapter.cs b/src/J2N/IO/Int32ToByteBufferAdapter.cs index d024bc6d..85392164 100644 --- a/src/J2N/IO/Int32ToByteBufferAdapter.cs +++ b/src/J2N/IO/Int32ToByteBufferAdapter.cs @@ -163,6 +163,8 @@ public override int Get(int index) return byteBuffer.GetInt32(index << 2); } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) to ints and override Get(Span) + //public override bool IsDirect => byteBuffer.IsDirect; public override bool IsReadOnly => byteBuffer.IsReadOnly; @@ -201,6 +203,8 @@ public override Int32Buffer Put(int index, int value) return this; } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) from ints and override Put(ReadOnlySpan) + public override Int32Buffer Slice() { byteBuffer.Limit = limit << 2; diff --git a/src/J2N/IO/Int64ArrayBuffer.cs b/src/J2N/IO/Int64ArrayBuffer.cs index 283fb620..8b58ec02 100644 --- a/src/J2N/IO/Int64ArrayBuffer.cs +++ b/src/J2N/IO/Int64ArrayBuffer.cs @@ -88,6 +88,17 @@ public override sealed Int64Buffer Get(long[] destination, int offset, int lengt return this; } + public override sealed Int64Buffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + throw new BufferUnderflowException(); + + backingArray.AsSpan(offset + position, length).CopyTo(destination); + position += length; + return this; + } + //public override sealed bool IsDirect => false; public override sealed ByteOrder Order => ByteOrder.NativeOrder; diff --git a/src/J2N/IO/Int64Buffer.cs b/src/J2N/IO/Int64Buffer.cs index 516487f3..7e194796 100644 --- a/src/J2N/IO/Int64Buffer.cs +++ b/src/J2N/IO/Int64Buffer.cs @@ -16,7 +16,9 @@ */ #endregion +using J2N.Buffers; using System; +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -172,6 +174,8 @@ public int ArrayOffset /// A negative value if this is less than ; 0 if this /// equals to ; a positive value if this is greater /// than . + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public int CompareTo(Int64Buffer? other) { if (other is null) return 1; // Using 1 if other is null as specified here: https://stackoverflow.com/a/4852537 @@ -223,6 +227,8 @@ public int CompareTo(Int64Buffer? other) /// The object to compare with this . /// true if this is equal to , /// false otherwise. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override bool Equals(object? other) { if (other is null || !(other is Int64Buffer otherBuffer)) @@ -282,6 +288,8 @@ public virtual Int64Buffer Get(long[] destination) /// The number of s to read, must be no less than zero and not /// greater than destination.Length - offset. /// This buffer. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// /// plus indicates a position not within this instance. /// @@ -313,6 +321,33 @@ public virtual Int64Buffer Get(long[] destination, int offset, int length) // J2 return this; } + /// + /// Reads s from the current position into the specified span, + /// and increases the position by the number of s read. + /// + /// The property is used to determine + /// how many s to read. + /// + /// The target span, sliced to the proper position, if necessary. + /// This buffer. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is greater than + /// . + public virtual Int64Buffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + { + throw new BufferUnderflowException(); + } + for (int i = 0; i < length; i++) + { + destination[i] = Get(); + } + return this; + } + /// /// Returns an at the specified ; the position is not changed. /// @@ -337,6 +372,8 @@ public bool HasArray /// position, limit, capacity and mark don't affect the hash code. /// /// The hash code calculated from the remaining s. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override int GetHashCode() { int myPosition = position; @@ -429,6 +466,8 @@ public Int64Buffer Put(long[] source) /// The number of s to write, must be no less than zero and not /// greater than source.Length - offset. /// This buffer. + /// Note to inheritors: This implementation writes s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// If is less than . /// /// plus indicates a position not within this instance. @@ -459,6 +498,32 @@ public virtual Int64Buffer Put(long[] source, int offset, int length) // J2N TOD return this; } + /// + /// Writes s in the given span to the current position and increases the + /// position by the number of s written. + /// + /// Calling this method has a similar effect as + /// Put(source, 0, source.Length). + /// + /// The source span. + /// This buffer. + /// Note to inheritors: This implementation writes s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is less than source.Length. + /// If no changes may be made to the contents of this buffer. + public virtual Int64Buffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + for (int i = 0; i < length; i++) + { + Put(source[i]); + } + return this; + } + /// /// Writes all the remaining s of the to this /// buffer's current position, and increases both buffers' position by the @@ -481,10 +546,21 @@ public virtual Int64Buffer Put(Int64Buffer source) if (IsReadOnly) throw new ReadOnlyBufferException(); // J2N: Harmony has a bug - it shouldn't read the source and change its position unless this buffer is writable - long[] contents = new long[source.Remaining]; - source.Get(contents); - Put(contents); - return this; + int length = source.Remaining; + long[]? arrayToReturnToPool = null; + Span contents = length * sizeof(long) > ByteStackBufferSize + ? (arrayToReturnToPool = ArrayPool.Shared.Rent(length)).AsSpan(0, length) + : stackalloc long[length]; + try + { + source.Get(contents); + Put(contents); + return this; + } + finally + { + ArrayPool.Shared.ReturnIfNotNull(arrayToReturnToPool); + } } /// diff --git a/src/J2N/IO/Int64ToByteBufferAdapter.cs b/src/J2N/IO/Int64ToByteBufferAdapter.cs index 9dde15d9..7820c906 100644 --- a/src/J2N/IO/Int64ToByteBufferAdapter.cs +++ b/src/J2N/IO/Int64ToByteBufferAdapter.cs @@ -155,6 +155,8 @@ public override long Get(int index) return byteBuffer.GetInt64(index << 3); } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) to longs and override Get(Span) + //public override bool IsDirect => byteBuffer.IsDirect; public override bool IsReadOnly => byteBuffer.IsReadOnly; @@ -192,6 +194,9 @@ public override Int64Buffer Put(int index, long value) byteBuffer.PutInt64(index << 3, value); return this; } + + // J2N TODO: Need a way to convert the bytes (in the specified endianness) from longs and override Put(ReadOnlySpan) + public override Int64Buffer Slice() { byteBuffer.SetLimit(limit << 3); diff --git a/src/J2N/IO/ReadOnlyCharArrayBuffer.cs b/src/J2N/IO/ReadOnlyCharArrayBuffer.cs index 6560b152..14cfa250 100644 --- a/src/J2N/IO/ReadOnlyCharArrayBuffer.cs +++ b/src/J2N/IO/ReadOnlyCharArrayBuffer.cs @@ -79,12 +79,17 @@ public override CharBuffer Put(int index, char value) throw new ReadOnlyBufferException(); } - public override sealed CharBuffer Put(char[] source, int offset, int length) + public override CharBuffer Put(char[] source, int offset, int length) { throw new ReadOnlyBufferException(); } - public override sealed CharBuffer Put(CharBuffer src) + public override CharBuffer Put(ReadOnlySpan source) // J2N specific + { + throw new ReadOnlyBufferException(); + } + + public override CharBuffer Put(CharBuffer src) { throw new ReadOnlyBufferException(); } diff --git a/src/J2N/IO/ReadOnlyDoubleArrayBuffer.cs b/src/J2N/IO/ReadOnlyDoubleArrayBuffer.cs index a040fa0e..4e46b0a5 100644 --- a/src/J2N/IO/ReadOnlyDoubleArrayBuffer.cs +++ b/src/J2N/IO/ReadOnlyDoubleArrayBuffer.cs @@ -16,6 +16,8 @@ */ #endregion +using System; + namespace J2N.IO { /// @@ -82,7 +84,12 @@ public override DoubleBuffer Put(DoubleBuffer buffer) throw new ReadOnlyBufferException(); } - public override sealed DoubleBuffer Put(double[] source, int offset, int length) + public override DoubleBuffer Put(double[] source, int offset, int length) + { + throw new ReadOnlyBufferException(); + } + + public override DoubleBuffer Put(ReadOnlySpan source) // J2N specific { throw new ReadOnlyBufferException(); } diff --git a/src/J2N/IO/ReadOnlyHeapByteBuffer.cs b/src/J2N/IO/ReadOnlyHeapByteBuffer.cs index 7fcb8f11..d6f5f894 100644 --- a/src/J2N/IO/ReadOnlyHeapByteBuffer.cs +++ b/src/J2N/IO/ReadOnlyHeapByteBuffer.cs @@ -16,6 +16,8 @@ */ #endregion +using System; + namespace J2N.IO { /// @@ -80,6 +82,11 @@ public override ByteBuffer Put(byte[] source, int offset, int length) throw new ReadOnlyBufferException(); } + public override ByteBuffer Put(ReadOnlySpan source) // J2N specific + { + throw new ReadOnlyBufferException(); + } + public override ByteBuffer PutDouble(double value) { throw new ReadOnlyBufferException(); diff --git a/src/J2N/IO/ReadOnlyInt16ArrayBuffer.cs b/src/J2N/IO/ReadOnlyInt16ArrayBuffer.cs index 9ed2ba98..fb49c651 100644 --- a/src/J2N/IO/ReadOnlyInt16ArrayBuffer.cs +++ b/src/J2N/IO/ReadOnlyInt16ArrayBuffer.cs @@ -16,6 +16,8 @@ */ #endregion +using System; + namespace J2N.IO { /// @@ -81,7 +83,12 @@ public override Int16Buffer Put(int index, short value) throw new ReadOnlyBufferException(); } - public override sealed Int16Buffer Put(short[] source, int offset, int length) + public override Int16Buffer Put(short[] source, int offset, int length) + { + throw new ReadOnlyBufferException(); + } + + public override Int16Buffer Put(ReadOnlySpan source) // J2N specific { throw new ReadOnlyBufferException(); } diff --git a/src/J2N/IO/ReadOnlyInt32ArrayBuffer.cs b/src/J2N/IO/ReadOnlyInt32ArrayBuffer.cs index ab3ac3a1..93caa46a 100644 --- a/src/J2N/IO/ReadOnlyInt32ArrayBuffer.cs +++ b/src/J2N/IO/ReadOnlyInt32ArrayBuffer.cs @@ -16,7 +16,7 @@ */ #endregion -using System.Diagnostics.CodeAnalysis; +using System; namespace J2N.IO @@ -84,7 +84,12 @@ public override Int32Buffer Put(Int32Buffer buffer) throw new ReadOnlyBufferException(); } - public override sealed Int32Buffer Put(int[] source, int offset, int length) + public override Int32Buffer Put(int[] source, int offset, int length) + { + throw new ReadOnlyBufferException(); + } + + public override Int32Buffer Put(ReadOnlySpan source) // J2N specific { throw new ReadOnlyBufferException(); } diff --git a/src/J2N/IO/ReadOnlyInt64ArrayBuffer.cs b/src/J2N/IO/ReadOnlyInt64ArrayBuffer.cs index 45104f2d..6f4f00d2 100644 --- a/src/J2N/IO/ReadOnlyInt64ArrayBuffer.cs +++ b/src/J2N/IO/ReadOnlyInt64ArrayBuffer.cs @@ -16,6 +16,8 @@ */ #endregion +using System; + namespace J2N.IO { /// @@ -81,7 +83,12 @@ public override Int64Buffer Put(Int64Buffer buffer) throw new ReadOnlyBufferException(); } - public override sealed Int64Buffer Put(long[] source, int offset, int length) + public override Int64Buffer Put(long[] source, int offset, int length) + { + throw new ReadOnlyBufferException(); + } + + public override Int64Buffer Put(ReadOnlySpan source) // J2N specific { throw new ReadOnlyBufferException(); } diff --git a/src/J2N/IO/ReadOnlySingleArrayBuffer.cs b/src/J2N/IO/ReadOnlySingleArrayBuffer.cs index 8d9bd05d..cf9e733d 100644 --- a/src/J2N/IO/ReadOnlySingleArrayBuffer.cs +++ b/src/J2N/IO/ReadOnlySingleArrayBuffer.cs @@ -16,6 +16,8 @@ */ #endregion +using System; + namespace J2N.IO { /// @@ -86,6 +88,11 @@ public override sealed SingleBuffer Put(float[] source, int offset, int length) throw new ReadOnlyBufferException(); } + public override SingleBuffer Put(ReadOnlySpan source) // J2N specific + { + throw new ReadOnlyBufferException(); + } + public override SingleBuffer Slice() { return new ReadOnlySingleArrayBuffer(Remaining, backingArray, offset + position); diff --git a/src/J2N/IO/ReadWriteCharArrayBuffer.cs b/src/J2N/IO/ReadWriteCharArrayBuffer.cs index c690276e..13194d99 100644 --- a/src/J2N/IO/ReadWriteCharArrayBuffer.cs +++ b/src/J2N/IO/ReadWriteCharArrayBuffer.cs @@ -113,6 +113,17 @@ public override CharBuffer Put(char[] source, int offset, int length) // J2N TOD return this; } + public override CharBuffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + source.CopyTo(backingArray.AsSpan(offset + position, length)); + position += length; + return this; + } + public override CharBuffer Slice() { return new ReadWriteCharArrayBuffer(Remaining, backingArray, offset + position); diff --git a/src/J2N/IO/ReadWriteDoubleArrayBuffer.cs b/src/J2N/IO/ReadWriteDoubleArrayBuffer.cs index ddb2d94f..46996abe 100644 --- a/src/J2N/IO/ReadWriteDoubleArrayBuffer.cs +++ b/src/J2N/IO/ReadWriteDoubleArrayBuffer.cs @@ -113,6 +113,17 @@ public override DoubleBuffer Put(double[] source, int offset, int length) // J2N return this; } + public override DoubleBuffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + source.CopyTo(backingArray.AsSpan(offset + position, length)); + position += length; + return this; + } + public override DoubleBuffer Slice() { return new ReadWriteDoubleArrayBuffer(Remaining, backingArray, offset + position); diff --git a/src/J2N/IO/ReadWriteHeapByteBuffer.cs b/src/J2N/IO/ReadWriteHeapByteBuffer.cs index 414971b0..1d91ead7 100644 --- a/src/J2N/IO/ReadWriteHeapByteBuffer.cs +++ b/src/J2N/IO/ReadWriteHeapByteBuffer.cs @@ -119,14 +119,23 @@ public override ByteBuffer Put(byte[] source, int offset, int length) // J2N TOD ThrowHelper.ThrowArgumentOutOfRange_IndexLengthArray(offset, ExceptionArgument.offset, length); if (length > Remaining) throw new BufferOverflowException(); - if (IsReadOnly) - throw new ReadOnlyBufferException(); System.Array.Copy(source, offset, backingArray, base.offset + position, length); position += length; return this; } + public override ByteBuffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + source.CopyTo(backingArray.AsSpan(offset + position, length)); + position += length; + return this; + } + public override ByteBuffer PutDouble(double value) { return PutInt64(BitConversion.DoubleToRawInt64Bits(value)); diff --git a/src/J2N/IO/ReadWriteInt16ArrayBuffer.cs b/src/J2N/IO/ReadWriteInt16ArrayBuffer.cs index 379c9c6b..1caef670 100644 --- a/src/J2N/IO/ReadWriteInt16ArrayBuffer.cs +++ b/src/J2N/IO/ReadWriteInt16ArrayBuffer.cs @@ -116,6 +116,17 @@ public override Int16Buffer Put(short[] source, int offset, int length) // J2N T return this; } + public override Int16Buffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + source.CopyTo(backingArray.AsSpan(offset + position, length)); + position += length; + return this; + } + public override Int16Buffer Slice() { return new ReadWriteInt16ArrayBuffer(Remaining, backingArray, offset + position); diff --git a/src/J2N/IO/ReadWriteInt32ArrayBuffer.cs b/src/J2N/IO/ReadWriteInt32ArrayBuffer.cs index e55de91f..d6e1fff1 100644 --- a/src/J2N/IO/ReadWriteInt32ArrayBuffer.cs +++ b/src/J2N/IO/ReadWriteInt32ArrayBuffer.cs @@ -115,6 +115,17 @@ public override Int32Buffer Put(int[] source, int offset, int length) // J2N TOD return this; } + public override Int32Buffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + source.CopyTo(backingArray.AsSpan(offset + position, length)); + position += length; + return this; + } + public override Int32Buffer Slice() { return new ReadWriteInt32ArrayBuffer(Remaining, backingArray, offset + position); diff --git a/src/J2N/IO/ReadWriteInt64ArrayBuffer.cs b/src/J2N/IO/ReadWriteInt64ArrayBuffer.cs index ab6c3c66..748bad82 100644 --- a/src/J2N/IO/ReadWriteInt64ArrayBuffer.cs +++ b/src/J2N/IO/ReadWriteInt64ArrayBuffer.cs @@ -113,6 +113,17 @@ public override Int64Buffer Put(long[] source, int offset, int length) // J2N TO return this; } + public override Int64Buffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + source.CopyTo(backingArray.AsSpan(offset + position, length)); + position += length; + return this; + } + public override Int64Buffer Slice() { return new ReadWriteInt64ArrayBuffer(Remaining, backingArray, offset + position); diff --git a/src/J2N/IO/ReadWriteSingleArrayBuffer.cs b/src/J2N/IO/ReadWriteSingleArrayBuffer.cs index 7fd83660..916f1605 100644 --- a/src/J2N/IO/ReadWriteSingleArrayBuffer.cs +++ b/src/J2N/IO/ReadWriteSingleArrayBuffer.cs @@ -113,6 +113,17 @@ public override SingleBuffer Put(float[] source, int offset, int length) // J2N return this; } + public override SingleBuffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + source.CopyTo(backingArray.AsSpan(offset + position, length)); + position += length; + return this; + } + public override SingleBuffer Slice() { return new ReadWriteSingleArrayBuffer(Remaining, backingArray, offset + position); diff --git a/src/J2N/IO/SingleArrayBuffer.cs b/src/J2N/IO/SingleArrayBuffer.cs index 7cd4b7e7..369a7289 100644 --- a/src/J2N/IO/SingleArrayBuffer.cs +++ b/src/J2N/IO/SingleArrayBuffer.cs @@ -86,6 +86,17 @@ public override sealed SingleBuffer Get(float[] destination, int offset, int len return this; } + public override SingleBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + throw new BufferUnderflowException(); + + backingArray.AsSpan(offset + position, length).CopyTo(destination); + position += length; + return this; + } + //public override sealed bool IsDirect => false; public override sealed ByteOrder Order => ByteOrder.NativeOrder; diff --git a/src/J2N/IO/SingleBuffer.cs b/src/J2N/IO/SingleBuffer.cs index e73fa906..39ed0ad7 100644 --- a/src/J2N/IO/SingleBuffer.cs +++ b/src/J2N/IO/SingleBuffer.cs @@ -16,7 +16,9 @@ */ #endregion +using J2N.Buffers; using System; +using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Text; @@ -163,6 +165,8 @@ internal SingleBuffer(int capacity) /// a negative value if this is less than ; 0 if this /// equals to ; a positive value if this is greater /// than . + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public virtual int CompareTo(SingleBuffer? other) { if (other is null) return 1; // Using 1 if other is null as specified here: https://stackoverflow.com/a/4852537 @@ -205,6 +209,8 @@ public virtual int CompareTo(SingleBuffer? other) /// The object to compare with this . /// true if this is equal to , /// false otherwise. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override bool Equals(object? other) { if (other is null || !(other is SingleBuffer otherBuffer)) @@ -267,6 +273,8 @@ public virtual SingleBuffer Get(float[] destination) /// The number of s to read, must be no less than zero and not /// greater than destination.Length - offset. /// This buffer. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// /// plus indicates a position not within this instance. /// @@ -296,6 +304,33 @@ public virtual SingleBuffer Get(float[] destination, int offset, int length) // return this; } + /// + /// Reads s from the current position into the specified span, + /// and increases the position by the number of s read. + /// + /// The property is used to determine + /// how many s to read. + /// + /// The target span, sliced to the proper position, if necessary. + /// This buffer. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is greater than + /// . + public virtual SingleBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + { + throw new BufferUnderflowException(); + } + for (int i = 0; i < length; i++) + { + destination[i] = Get(); + } + return this; + } + /// /// Returns an at the specified ; the position is not changed. /// @@ -317,6 +352,8 @@ public virtual SingleBuffer Get(float[] destination, int offset, int length) // /// position, limit, capacity and mark don't affect the hash code. /// /// The hash code calculated from the remaining s. + /// Note to inheritors: This implementation reads s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. public override int GetHashCode() { int myPosition = position; @@ -407,6 +444,8 @@ public SingleBuffer Put(float[] source) /// The number of s to write, must be no less than zero and not /// greater than source.Length - offset. /// This buffer. + /// Note to inheritors: This implementation writes s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. /// If is less than . /// /// plus indicates a position not within this instance. @@ -437,6 +476,32 @@ public virtual SingleBuffer Put(float[] source, int offset, int length) // J2N T return this; } + /// + /// Writes s in the given span to the current position and increases the + /// position by the number of s written. + /// + /// Calling this method has a similar effect as + /// Put(source, 0, source.Length). + /// + /// The source span. + /// This buffer. + /// Note to inheritors: This implementation writes s one at a time and it is highly + /// recommended to override to provide a more optimized implementation. + /// If is less than source.Length. + /// If no changes may be made to the contents of this buffer. + public virtual SingleBuffer Put(ReadOnlySpan source) // J2N specific + { + int length = source.Length; + if (length > Remaining) + throw new BufferOverflowException(); + + for (int i = 0; i < length; i++) + { + Put(source[i]); + } + return this; + } + /// /// Writes all the remaining s of the to this /// buffer's current position, and increases both buffers' position by the @@ -459,10 +524,21 @@ public virtual SingleBuffer Put(SingleBuffer source) if (IsReadOnly) throw new ReadOnlyBufferException(); // J2N: Harmony has a bug - it shouldn't read the source and change its position unless this buffer is writable - float[] contents = new float[source.Remaining]; - source.Get(contents); - Put(contents); - return this; + int length = source.Remaining; + float[]? arrayToReturnToPool = null; + Span contents = length * sizeof(float) > ByteStackBufferSize + ? (arrayToReturnToPool = ArrayPool.Shared.Rent(length)).AsSpan(0, length) + : stackalloc float[length]; + try + { + source.Get(contents); + Put(contents); + return this; + } + finally + { + ArrayPool.Shared.ReturnIfNotNull(arrayToReturnToPool); + } } /// diff --git a/src/J2N/IO/SingleToByteBufferAdapter.cs b/src/J2N/IO/SingleToByteBufferAdapter.cs index 012c4fee..f803cfeb 100644 --- a/src/J2N/IO/SingleToByteBufferAdapter.cs +++ b/src/J2N/IO/SingleToByteBufferAdapter.cs @@ -156,6 +156,7 @@ public override float Get(int index) return byteBuffer.GetSingle(index << 2); } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) to floats and override Get(Span) //public override bool IsDirect => byteBuffer.IsDirect; @@ -196,6 +197,8 @@ public override SingleBuffer Put(int index, float value) return this; } + // J2N TODO: Need a way to convert the bytes (in the specified endianness) from floats and override Put(ReadOnlySpan) + public override SingleBuffer Slice() { byteBuffer.SetLimit(limit << 2); diff --git a/src/J2N/IO/StringBuilderAdapter.cs b/src/J2N/IO/StringBuilderAdapter.cs new file mode 100644 index 00000000..d351ca10 --- /dev/null +++ b/src/J2N/IO/StringBuilderAdapter.cs @@ -0,0 +1,195 @@ +#region Copyright 2010 by Apache Harmony, Licensed under the Apache License, Version 2.0 +/* 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. + */ +#endregion + +using J2N.Text; +using System; +using System.Text; + +namespace J2N.IO +{ + /// + /// This class wraps a char sequence to be a buffer. + /// + /// Implementation notice: + /// + /// Char sequence based buffer is always readonly. + /// + /// + internal sealed class StringBuilderAdapter : CharBuffer + { + internal static StringBuilderAdapter Copy(StringBuilderAdapter other) + { + return new StringBuilderAdapter(other.sequence) + { + limit = other.limit, + position = other.position, + mark = other.mark + }; + } + + internal readonly StringBuilder sequence; + + internal StringBuilderAdapter(StringBuilder chseq) + : base(chseq.Length) + { + sequence = chseq; + } + + public override CharBuffer AsReadOnlyBuffer() => Duplicate(); + + public override CharBuffer Compact() + { + throw new ReadOnlyBufferException(); + } + + public override CharBuffer Duplicate() => Copy(this); + + public override char Get() + { + if (position == limit) + { + throw new BufferUnderflowException(); + } + return sequence[position++]; + } + + public override char Get(int index) + { + if ((uint)index >= (uint)limit) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return sequence[index]; + } + + public override sealed CharBuffer Get(char[] destination, int offset, int length) // J2N TODO: API - rename startIndex instead of offset + { + if (destination is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destination); + if (offset < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(offset, ExceptionArgument.offset); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(length, ExceptionArgument.length); + if (offset > destination.Length - length) // Checks for int overflow + ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(offset, length); + if (length > Remaining) + throw new BufferUnderflowException(); + + + int newPosition = position + length; + sequence.CopyTo(position, destination, offset, length); + position = newPosition; + return this; + } + + public override CharBuffer Get(Span destination) // J2N specific + { + int length = destination.Length; + if (length > Remaining) + throw new BufferUnderflowException(); + + int newPosition = position + length; + sequence.CopyTo(position, destination, length); + position = newPosition; + return this; + } + + //public override bool IsDirect => false; + + public override bool IsReadOnly => true; + + public override ByteOrder Order => ByteOrder.NativeOrder; + + protected override char[] ProtectedArray + { + get { throw new NotSupportedException(); } + } + + protected override int ProtectedArrayOffset + { + get { throw new NotSupportedException(); } + } + + protected override bool ProtectedHasArray => false; + + public override CharBuffer Put(char value) + { + throw new ReadOnlyBufferException(); + } + + public override CharBuffer Put(int index, char value) + { + throw new ReadOnlyBufferException(); + } + + public override sealed CharBuffer Put(char[] source, int offset, int length) // J2N TODO: API - rename startIndex instead of offset + { + if (source is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); + if (offset < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(offset, ExceptionArgument.offset); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(length, ExceptionArgument.length); + if (offset > source.Length - length) // Checks for int overflow + ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(offset, length); + if (length > Remaining) + throw new BufferOverflowException(); + + throw new ReadOnlyBufferException(); + } + + public override CharBuffer Put(string source, int startIndex, int length) + { + if (source is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); + if (startIndex < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(startIndex, ExceptionArgument.startIndex); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(length, ExceptionArgument.length); + if (startIndex > source.Length - length) // Checks for int overflow + ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(startIndex, length); + + throw new ReadOnlyBufferException(); + } + + public override CharBuffer Slice() + { + int length = Remaining; + // J2N NOTE: If the caller slices again, this will be more efficient than using CharSequenceAdapter around a string + // and will also index faster if the StringBuilder has more than one chunk. + char[] chars = new char[length]; + sequence.CopyTo(position, chars, 0, length); + return new ReadOnlyCharArrayBuffer(length, chars, arrayOffset: 0); + } + + public override CharBuffer Subsequence(int startIndex, int length) + { + if (startIndex < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(startIndex, ExceptionArgument.startIndex); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRange_MustBeNonNegative(length, ExceptionArgument.length); + if (startIndex > Remaining - length) // Checks for int overflow + ThrowHelper.ThrowArgumentOutOfRange_IndexLengthString(startIndex, length); + + StringBuilderAdapter result = Copy(this); + result.position = position + startIndex; + result.limit = position + startIndex + length; + return result; + } + } +} diff --git a/src/J2N/MemoryExtensions.cs b/src/J2N/MemoryExtensions.cs index 79264001..0a397946 100644 --- a/src/J2N/MemoryExtensions.cs +++ b/src/J2N/MemoryExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -428,5 +429,35 @@ internal unsafe static void ReverseText(char* text, int count) } #endregion ReverseText + + #region TryGetReference + + /// + /// Sets the supplied to the underlying or + /// of this . This allows use of as a field + /// of a struct or class without having the underlying or go out of scope. + /// + /// This . + /// When this method returns successfully, the refernce will be set by ref to the + /// underlying or of . + /// true if the underlying reference could be retrieved; otherwise, false. + /// Note that if the underlying memory is not a or , + /// this method will always return false. + internal static bool TryGetReference(this ReadOnlyMemory text, [MaybeNullWhen(false)] ref object? reference) + { + if (MemoryMarshal.TryGetString(text, out string? stringValue, out _, out _) && stringValue is not null) + { + reference = stringValue; + return true; + } + else if (MemoryMarshal.TryGetArray(text, out ArraySegment arraySegment) && arraySegment.Array is not null) + { + reference = arraySegment.Array; + return true; + } + return false; + } + + #endregion } } diff --git a/tests/NUnit/J2N.Tests/IO/TestByteBuffer.cs b/tests/NUnit/J2N.Tests/IO/TestByteBuffer.cs index 6135834d..af7a66da 100644 --- a/tests/NUnit/J2N.Tests/IO/TestByteBuffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestByteBuffer.cs @@ -377,6 +377,42 @@ public virtual void TestGet() } } + /* + * Class under test for ByteBuffer Get(Span) + */ + [Test] + public virtual void TestGetbyteSpan() // J2N specific + { + Span array = new byte[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + ByteBuffer ret = buf.Get(array); + assertEquals(array[0], buf.Get(i)); + assertSame(ret, buf); + } + try + { + buf.Get(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferUnderflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Get((byte[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.ByteBuffer get(byte[]) */ @@ -669,6 +705,57 @@ public virtual void TestPutbyte() } } + /* + * Class under test for ByteBuffer Put(ReadOnlySpan) + */ + [Test] + public virtual void TestPutbyteSpan() // J2N specific + { + Span array = new byte[1]; + if (buf.IsReadOnly) + { + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ReadOnlyBufferException e) + { + // expected + } + return; + } + + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + array[0] = (byte)i; + ByteBuffer ret = buf.Put(array); + assertEquals(buf.Get(i), (byte)i); + assertSame(ret, buf); + } + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferOverflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Put((byte[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.ByteBuffer put(byte[]) */ diff --git a/tests/NUnit/J2N.Tests/IO/TestByteBuffer2.cs b/tests/NUnit/J2N.Tests/IO/TestByteBuffer2.cs index 28c21b08..60723d97 100644 --- a/tests/NUnit/J2N.Tests/IO/TestByteBuffer2.cs +++ b/tests/NUnit/J2N.Tests/IO/TestByteBuffer2.cs @@ -51,6 +51,15 @@ private static void bulkGet(ByteBuffer b) ck(b, (long)a[i + 7], (long)((byte)Ic(i))); } + private static void bulkGetSpan(ByteBuffer b) // J2N specific + { + int n = b.Capacity; + Span a = new byte[n + 7]; + b.Get(a.Slice(7, n)); + for (int i = 0; i < n; i++) + ck(b, (long)a[i + 7], (long)((byte)Ic(i))); + } + private static void relPut(ByteBuffer b) { int n = b.Capacity; @@ -81,6 +90,17 @@ private static void bulkPutArray(ByteBuffer b) b.Flip(); } + private static void bulkPutSpan(ByteBuffer b) // J2N specific + { + int n = b.Capacity; + b.Clear(); + Span a = new byte[n + 7]; + for (int i = 0; i < n; i++) + a[i + 7] = (byte)Ic(i); + b.Put(a.Slice(7, n)); + b.Flip(); + } + private static void bulkPutBuffer(ByteBuffer b) { int n = b.Capacity; @@ -312,9 +332,22 @@ public static void test(int level, ByteBuffer b, bool direct) absGet(b); bulkGet(b); + relPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + + absPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + bulkPutArray(b); relGet(b); + bulkPutSpan(b); // J2N specific + relGet(b); + bulkPutBuffer(b); relGet(b); @@ -538,6 +571,11 @@ public static void test(int level, ByteBuffer b, bool direct) bulkPutArray(rb); }); + tryCatch(b, typeof(ReadOnlyBufferException), () => + { + bulkPutSpan(rb); // J2N specific + }); + tryCatch(b, typeof(ReadOnlyBufferException), () => { bulkPutBuffer(rb); diff --git a/tests/NUnit/J2N.Tests/IO/TestCharBuffer.cs b/tests/NUnit/J2N.Tests/IO/TestCharBuffer.cs index bebf2707..e1631931 100644 --- a/tests/NUnit/J2N.Tests/IO/TestCharBuffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestCharBuffer.cs @@ -42,7 +42,10 @@ public virtual void TestWrap() string s = "A test string to test with."; char[] ca = s.ToCharArray(); StringBuilder sb = new StringBuilder(s); - ICharSequence cs = s.AsCharSequence(); + ICharSequence scs = s.AsCharSequence(); + ICharSequence sbcs = sb.AsCharSequence(); + ICharSequence cacs = ca.AsCharSequence(); + ICharSequence customcs = new MockCharSequence(s.AsMemory()); // Control - char[] was part of the original Java implementation // and used length rather than end index. So we will check that our conversion @@ -57,9 +60,52 @@ public virtual void TestWrap() Assert.AreEqual(CharBuffer.Wrap(ca).Limit, CharBuffer.Wrap(sb).Limit); Assert.AreEqual(CharBuffer.Wrap(ca, 6, 10).Limit, CharBuffer.Wrap(sb, 6, 10).Limit); - // ICharSequence - Assert.AreEqual(CharBuffer.Wrap(ca).Limit, CharBuffer.Wrap(cs).Limit); - Assert.AreEqual(CharBuffer.Wrap(ca, 6, 10).Limit, CharBuffer.Wrap(cs, 6, 10).Limit); + // ICharSequence (string) + Assert.AreEqual(CharBuffer.Wrap(ca).Limit, CharBuffer.Wrap(scs).Limit); + Assert.AreEqual(CharBuffer.Wrap(ca, 6, 10).Limit, CharBuffer.Wrap(scs, 6, 10).Limit); + + // ICharSequence (StringBuilder) + Assert.AreEqual(CharBuffer.Wrap(ca).Limit, CharBuffer.Wrap(sbcs).Limit); + Assert.AreEqual(CharBuffer.Wrap(ca, 6, 10).Limit, CharBuffer.Wrap(sbcs, 6, 10).Limit); + + // ICharSequence (char[]) + Assert.AreEqual(CharBuffer.Wrap(ca).Limit, CharBuffer.Wrap(cacs).Limit); + Assert.AreEqual(CharBuffer.Wrap(ca, 6, 10).Limit, CharBuffer.Wrap(cacs, 6, 10).Limit); + + // ICharSequence (custom) + Assert.AreEqual(CharBuffer.Wrap(ca).Limit, CharBuffer.Wrap(customcs).Limit); + Assert.AreEqual(CharBuffer.Wrap(ca, 6, 10).Limit, CharBuffer.Wrap(customcs, 6, 10).Limit); + } + + /// + /// A custom implementation used for testing unknown + /// implementations, since we generally optimize by using the underlying value of + /// , or + /// rather than the interface itself. + /// + private sealed class MockCharSequence : ICharSequence + { + private readonly ReadOnlyMemory value; + public MockCharSequence(ReadOnlyMemory value) + { + this.value = value; + } + + public char this[int index] => value.Span[index]; + + public bool HasValue => true; + + public int Length => value.Length; + + public ICharSequence Subsequence(int startIndex, int length) + { + return new MockCharSequence(value.Slice(startIndex, length)); + } + + public override string ToString() + { + return value.ToString(); + } } @@ -361,6 +407,32 @@ public virtual void TestGet() } } + /* + * Class under test for CharBuffer Get(Span) + */ + [Test] + public virtual void TestGetcharSpan() // J2N specific + { + Span array = new char[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + CharBuffer ret = buf.Get(array); + assertEquals(array[0], buf.Get(i)); + assertSame(ret, buf); + } + try + { + buf.Get(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferUnderflowException e) + { + // expected + } + } + /* * Class under test for java.nio.CharBuffer get(char[]) */ @@ -385,6 +457,15 @@ public virtual void TestGetcharArray() { // expected } + try // J2N: Added check for guard clause + { + buf.Get((char[])null); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ArgumentNullException e) + { + // expected + } } /* @@ -550,6 +631,44 @@ public virtual void TestPutchar() } } + /* + * Class under test for CharBuffer Put(ReadOnlySpan) + */ + [Test] + public virtual void TestPutcharSpan() // J2N specific + { + Span array = new char[1]; + + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + array[0] = (char)i; + CharBuffer ret = buf.Put(array); + assertEquals(buf.Get(i), (char)i); + assertSame(ret, buf); + } + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferOverflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Put((char[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.CharBuffer put(char[]) */ diff --git a/tests/NUnit/J2N.Tests/IO/TestCharBuffer2.cs b/tests/NUnit/J2N.Tests/IO/TestCharBuffer2.cs index ab78a043..8e55ad0c 100644 --- a/tests/NUnit/J2N.Tests/IO/TestCharBuffer2.cs +++ b/tests/NUnit/J2N.Tests/IO/TestCharBuffer2.cs @@ -54,6 +54,15 @@ private static void bulkGet(CharBuffer b) ck(b, (long)a[i + 7], (long)((char)Ic(i))); } + private static void bulkGetSpan(CharBuffer b) // J2N specific + { + int n = b.Capacity; + Span a = new char[n + 7]; + b.Get(a.Slice(7, n)); + for (int i = 0; i < n; i++) + ck(b, (long)a[i + 7], (long)((char)Ic(i))); + } + private static void relPut(CharBuffer b) { int n = b.Capacity; @@ -84,6 +93,17 @@ private static void bulkPutArray(CharBuffer b) b.Flip(); } + private static void bulkPutSpan(CharBuffer b) // J2N specific + { + int n = b.Capacity; + b.Clear(); + Span a = new char[n + 7]; + for (int i = 0; i < n; i++) + a[i + 7] = (char)Ic(i); + b.Put(a.Slice(7, n)); + b.Flip(); + } + private static void bulkPutBuffer(CharBuffer b) { int n = b.Capacity; @@ -210,9 +230,22 @@ public static void test(int level, CharBuffer b, bool direct) absGet(b); bulkGet(b); + relPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + + absPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + bulkPutArray(b); relGet(b); + bulkPutSpan(b); // J2N specific + relGet(b); + bulkPutBuffer(b); relGet(b); @@ -433,6 +466,11 @@ public static void test(int level, CharBuffer b, bool direct) bulkPutArray(rb); }); + tryCatch(b, typeof(ReadOnlyBufferException), () => + { + bulkPutSpan(rb); // J2N specific + }); + tryCatch(b, typeof(ReadOnlyBufferException), () => { bulkPutBuffer(rb); diff --git a/tests/NUnit/J2N.Tests/IO/TestDoubleBuffer.cs b/tests/NUnit/J2N.Tests/IO/TestDoubleBuffer.cs index 0e5229b5..b96b0d43 100644 --- a/tests/NUnit/J2N.Tests/IO/TestDoubleBuffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestDoubleBuffer.cs @@ -303,6 +303,32 @@ public virtual void TestGet() } } + /* + * Class under test for DoubleBuffer Get(Span) + */ + [Test] + public virtual void TestGetdoubleSpan() // J2N specific + { + Span array = new double[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + DoubleBuffer ret = buf.Get(array); + assertEquals(array[0], buf.Get(i), 0.01); + assertSame(ret, buf); + } + try + { + buf.Get(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferUnderflowException e) + { + // expected + } + } + /* * Class under test for java.nio.DoubleBuffer get(double[]) */ @@ -327,6 +353,15 @@ public virtual void TestGetdoubleArray() { // expected } + try // J2N: Added check for guard clause + { + buf.Get((double[])null); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ArgumentNullException e) + { + // expected + } } /* @@ -511,6 +546,44 @@ public virtual void TestPutdouble() } } + /* + * Class under test for DoubleBuffer Put(ReadOnlySpan) + */ + [Test] + public virtual void TestPutdoubleSpan() // J2N specific + { + Span array = new double[1]; + + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + array[0] = (double)i; + DoubleBuffer ret = buf.Put(array); + assertEquals(buf.Get(i), (double)i, 0.0); + assertSame(ret, buf); + } + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferOverflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Put((double[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.DoubleBuffer put(double[]) */ @@ -537,6 +610,15 @@ public virtual void TestPutdoubleArray() { // expected } + try + { + buf.Put((double[])null); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ArgumentNullException e) + { + // expected + } } /* diff --git a/tests/NUnit/J2N.Tests/IO/TestDoubleBuffer2.cs b/tests/NUnit/J2N.Tests/IO/TestDoubleBuffer2.cs index 9770e629..6dde204c 100644 --- a/tests/NUnit/J2N.Tests/IO/TestDoubleBuffer2.cs +++ b/tests/NUnit/J2N.Tests/IO/TestDoubleBuffer2.cs @@ -57,6 +57,15 @@ private static void bulkGet(DoubleBuffer b) ck(b, (long)a[i + 7], (long)((double)Ic(i))); } + private static void bulkGetSpan(DoubleBuffer b) // J2N specific + { + int n = b.Capacity; + Span a = new double[n + 7]; + b.Get(a.Slice(7, n)); + for (int i = 0; i < n; i++) + ck(b, (long)a[i + 7], (long)((double)Ic(i))); + } + private static void relPut(DoubleBuffer b) { int n = b.Capacity; @@ -87,6 +96,17 @@ private static void bulkPutArray(DoubleBuffer b) b.Flip(); } + private static void bulkPutSpan(DoubleBuffer b) // J2N specific + { + int n = b.Capacity; + b.Clear(); + Span a = new double[n + 7]; + for (int i = 0; i < n; i++) + a[i + 7] = (double)Ic(i); + b.Put(a.Slice(7, n)); + b.Flip(); + } + private static void bulkPutBuffer(DoubleBuffer b) { int n = b.Capacity; @@ -201,9 +221,22 @@ public static void test(int level, DoubleBuffer b, bool direct) absGet(b); bulkGet(b); + relPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + + absPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + bulkPutArray(b); relGet(b); + bulkPutSpan(b); // J2N specific + relGet(b); + bulkPutBuffer(b); relGet(b); @@ -415,6 +448,11 @@ public static void test(int level, DoubleBuffer b, bool direct) bulkPutArray(rb); }); + tryCatch(b, typeof(ReadOnlyBufferException), () => + { + bulkPutSpan(rb); // J2N specific + }); + tryCatch(b, typeof(ReadOnlyBufferException), () => { bulkPutBuffer(rb); diff --git a/tests/NUnit/J2N.Tests/IO/TestInt16Buffer.cs b/tests/NUnit/J2N.Tests/IO/TestInt16Buffer.cs index cc752e44..1b74d714 100644 --- a/tests/NUnit/J2N.Tests/IO/TestInt16Buffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestInt16Buffer.cs @@ -269,6 +269,41 @@ public virtual void TestGet() } } + /* + * Class under test for Int16Buffer Get(Span) + */ + public virtual void TestGetshortSpan() // J2N specific + { + Span array = new short[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + Int16Buffer ret = buf.Get(array); + assertEquals(array[0], buf.Get(i)); + assertSame(ret, buf); + } + try + { + buf.Get(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferUnderflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Get((short[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.Int16Buffer get(short[]) */ @@ -292,6 +327,15 @@ public virtual void TestGetshortArray() { // expected } + try // J2N: Added check for guard clause + { + buf.Get((short[])null); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ArgumentNullException e) + { + // expected + } } /* @@ -467,6 +511,44 @@ public virtual void TestPutshort() } } + /* + * Class under test for Int16Buffer Put(ReadOnlySpan) + */ + [Test] + public virtual void TestPutshortSpan() // J2N specific + { + Span array = new short[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + array[0] = (short)i; + Int16Buffer ret = buf.Put(array); + assertEquals(buf.Get(i), (short)i); + assertSame(ret, buf); + } + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferOverflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Position = (buf.Limit); + // buf.Put((short[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.Int16Buffer put(short[]) */ diff --git a/tests/NUnit/J2N.Tests/IO/TestInt16Buffer2.cs b/tests/NUnit/J2N.Tests/IO/TestInt16Buffer2.cs index afa9b1d2..c05473ee 100644 --- a/tests/NUnit/J2N.Tests/IO/TestInt16Buffer2.cs +++ b/tests/NUnit/J2N.Tests/IO/TestInt16Buffer2.cs @@ -53,6 +53,15 @@ private static void bulkGet(Int16Buffer b) ck(b, (long)a[i + 7], (long)((short)Ic(i))); } + private static void bulkGetSpan(Int16Buffer b) // J2N specific + { + int n = b.Capacity; + Span a = new short[n + 7]; + b.Get(a.Slice(7, n)); + for (int i = 0; i < n; i++) + ck(b, (long)a[i + 7], (long)((short)Ic(i))); + } + private static void relPut(Int16Buffer b) { int n = b.Capacity; @@ -83,6 +92,17 @@ private static void bulkPutArray(Int16Buffer b) b.Flip(); } + private static void bulkPutSpan(Int16Buffer b) // J2N specific + { + int n = b.Capacity; + b.Clear(); + Span a = new short[n + 7]; + for (int i = 0; i < n; i++) + a[i + 7] = (short)Ic(i); + b.Put(a.Slice(7, n)); + b.Flip(); + } + private static void bulkPutBuffer(Int16Buffer b) { int n = b.Capacity; @@ -196,9 +216,22 @@ public static void test(int level, Int16Buffer b, bool direct) absGet(b); bulkGet(b); + relPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + + absPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + bulkPutArray(b); relGet(b); + bulkPutSpan(b); // J2N specific + relGet(b); + bulkPutBuffer(b); relGet(b); @@ -391,6 +424,11 @@ public static void test(int level, Int16Buffer b, bool direct) bulkPutArray(rb); }); + tryCatch(b, typeof(ReadOnlyBufferException), () => + { + bulkPutSpan(rb); // J2N specific + }); + tryCatch(b, typeof(ReadOnlyBufferException), () => { bulkPutBuffer(rb); diff --git a/tests/NUnit/J2N.Tests/IO/TestInt32Buffer.cs b/tests/NUnit/J2N.Tests/IO/TestInt32Buffer.cs index d32f4945..4bfc9a32 100644 --- a/tests/NUnit/J2N.Tests/IO/TestInt32Buffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestInt32Buffer.cs @@ -267,6 +267,42 @@ public virtual void TestGet() } } + /* + * Class under test for Int32Buffer Get(Span) + */ + [Test] + public virtual void TestGetintSpan() // J2N specific + { + Span array = new int[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + Int32Buffer ret = buf.Get(array); + assertEquals(array[0], buf.Get(i)); + assertSame(ret, buf); + } + try + { + buf.Get(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferUnderflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Get((int[])null); + // fail("Should throw NPE"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.Int32Buffer get(int[]) */ @@ -484,6 +520,44 @@ public virtual void TestPutint() } } + /* + * Class under test for Int32Buffer Put(ReadOnlySpan) + */ + [Test] + public virtual void TestPutintSpan() // J2N specific + { + Span array = new int[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + array[0] = (int)i; + Int32Buffer ret = buf.Put(array); + assertEquals(buf.Get(i), (int)i); + assertSame(ret, buf); + } + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferOverflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Position = (buf.Limit); + // buf.Put((int[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.Int32Buffer put(int[]) */ diff --git a/tests/NUnit/J2N.Tests/IO/TestInt32Buffer2.cs b/tests/NUnit/J2N.Tests/IO/TestInt32Buffer2.cs index e4cbdc4c..560733f0 100644 --- a/tests/NUnit/J2N.Tests/IO/TestInt32Buffer2.cs +++ b/tests/NUnit/J2N.Tests/IO/TestInt32Buffer2.cs @@ -54,6 +54,15 @@ private static void bulkGet(Int32Buffer b) ck(b, (long)a[i + 7], (long)((int)Ic(i))); } + private static void bulkGetSpan(Int32Buffer b) // J2N specific + { + int n = b.Capacity; + Span a = new int[n + 7]; + b.Get(a.Slice(7, n)); + for (int i = 0; i < n; i++) + ck(b, (long)a[i + 7], (long)((int)Ic(i))); + } + private static void relPut(Int32Buffer b) { int n = b.Capacity; @@ -84,6 +93,17 @@ private static void bulkPutArray(Int32Buffer b) b.Flip(); } + private static void bulkPutSpan(Int32Buffer b) // J2N specific + { + int n = b.Capacity; + b.Clear(); + Span a = new int[n + 7]; + for (int i = 0; i < n; i++) + a[i + 7] = (int)Ic(i); + b.Put(a.Slice(7, n)); + b.Flip(); + } + private static void bulkPutBuffer(Int32Buffer b) { int n = b.Capacity; @@ -200,9 +220,22 @@ public static void test(int level, Int32Buffer b, bool direct) absGet(b); bulkGet(b); + relPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + + absPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + bulkPutArray(b); relGet(b); + bulkPutSpan(b); // J2N specific + relGet(b); + bulkPutBuffer(b); relGet(b); @@ -396,6 +429,11 @@ public static void test(int level, Int32Buffer b, bool direct) bulkPutArray(rb); }); + tryCatch(b, typeof(ReadOnlyBufferException), () => + { + bulkPutSpan(rb); // J2N specific + }); + tryCatch(b, typeof(ReadOnlyBufferException), () => { bulkPutBuffer(rb); diff --git a/tests/NUnit/J2N.Tests/IO/TestInt64Buffer.cs b/tests/NUnit/J2N.Tests/IO/TestInt64Buffer.cs index b38dc9f2..bba272f1 100644 --- a/tests/NUnit/J2N.Tests/IO/TestInt64Buffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestInt64Buffer.cs @@ -269,6 +269,43 @@ public virtual void TestGet() } } + /* + * Class under test for Int64Buffer Get(Span) + */ + [Test] + public virtual void TestGetlongSpan() // J2N specific + { + Span array = new long[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + Int64Buffer ret = buf.Get(array); + assertEquals(array[0], buf.Get(i)); + assertSame(ret, buf); + } + try + { + buf.Get(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferUnderflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Position = (buf.Limit); + // buf.Get((long[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.Int64Buffer get(long[]) */ @@ -487,6 +524,44 @@ public virtual void TestPutlong() } } + /* + * Class under test for Int64Buffer Put(ReadOnlySpan) + */ + [Test] + public virtual void TestPutlongSpan() // J2N specific + { + Span array = new long[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + array[0] = (long)i; + Int64Buffer ret = buf.Put(array); + assertEquals(buf.Get(i), (long)i); + assertSame(ret, buf); + } + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferOverflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Position = (buf.Limit); + // buf.Put((long[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.Int64Buffer put(long[]) */ diff --git a/tests/NUnit/J2N.Tests/IO/TestInt64Buffer2.cs b/tests/NUnit/J2N.Tests/IO/TestInt64Buffer2.cs index 9c226afc..253644ec 100644 --- a/tests/NUnit/J2N.Tests/IO/TestInt64Buffer2.cs +++ b/tests/NUnit/J2N.Tests/IO/TestInt64Buffer2.cs @@ -53,6 +53,15 @@ private static void bulkGet(Int64Buffer b) ck(b, (long)a[i + 7], (long)((long)Ic(i))); } + private static void bulkGetSpan(Int64Buffer b) // J2N specific + { + int n = b.Capacity; + Span a = new long[n + 7]; + b.Get(a.Slice(7, n)); + for (int i = 0; i < n; i++) + ck(b, (long)a[i + 7], (long)((long)Ic(i))); + } + private static void relPut(Int64Buffer b) { int n = b.Capacity; @@ -83,6 +92,17 @@ private static void bulkPutArray(Int64Buffer b) b.Flip(); } + private static void bulkPutSpan(Int64Buffer b) // J2N specific + { + int n = b.Capacity; + b.Clear(); + Span a = new long[n + 7]; + for (int i = 0; i < n; i++) + a[i + 7] = (long)Ic(i); + b.Put(a.Slice(7, n)); + b.Flip(); + } + private static void bulkPutBuffer(Int64Buffer b) { int n = b.Capacity; @@ -199,9 +219,22 @@ public static void test(int level, Int64Buffer b, bool direct) absGet(b); bulkGet(b); + relPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + + absPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + bulkPutArray(b); relGet(b); + bulkPutSpan(b); // J2N specific + relGet(b); + bulkPutBuffer(b); relGet(b); @@ -398,6 +431,11 @@ public static void test(int level, Int64Buffer b, bool direct) bulkPutArray(rb); }); + tryCatch(b, typeof(ReadOnlyBufferException), () => + { + bulkPutSpan(rb); // J2N specific + }); + tryCatch(b, typeof(ReadOnlyBufferException), () => { bulkPutBuffer(rb); diff --git a/tests/NUnit/J2N.Tests/IO/TestReadOnlyCharBuffer.cs b/tests/NUnit/J2N.Tests/IO/TestReadOnlyCharBuffer.cs index 24de0b6b..19dc2719 100644 --- a/tests/NUnit/J2N.Tests/IO/TestReadOnlyCharBuffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestReadOnlyCharBuffer.cs @@ -93,6 +93,31 @@ public override void TestPutchar() } } + [Test] + public override void TestPutcharSpan() // J2N specific + { + Span array = new char[1]; + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ReadOnlyBufferException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Put((char[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + [Test] public override void TestPutcharArray() { diff --git a/tests/NUnit/J2N.Tests/IO/TestReadOnlyDoubleBuffer.cs b/tests/NUnit/J2N.Tests/IO/TestReadOnlyDoubleBuffer.cs index 8f1e527f..95bb6754 100644 --- a/tests/NUnit/J2N.Tests/IO/TestReadOnlyDoubleBuffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestReadOnlyDoubleBuffer.cs @@ -90,6 +90,31 @@ public override void TestPutdouble() } } + [Test] + public override void TestPutdoubleSpan() // J2N specific + { + Span array = new double[1]; + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ReadOnlyBufferException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Put((double[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + [Test] public override void TestPutdoubleArray() { diff --git a/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt16Buffer.cs b/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt16Buffer.cs index 5de6090f..0c449880 100644 --- a/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt16Buffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt16Buffer.cs @@ -92,6 +92,31 @@ public override void TestPutshort() } } + [Test] + public override void TestPutshortSpan() // J2N specific + { + Span array = new short[1]; + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ReadOnlyBufferException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Put((short[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + [Test] public override void TestPutshortArray() { diff --git a/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt32Buffer.cs b/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt32Buffer.cs index da7ee1c9..86c53034 100644 --- a/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt32Buffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt32Buffer.cs @@ -92,6 +92,31 @@ public override void TestPutint() } } + [Test] + public override void TestPutintSpan() // J2N specific + { + Span array = new int[1]; + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ReadOnlyBufferException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Put((int[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + [Test] public override void TestPutintArray() { diff --git a/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt64Buffer.cs b/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt64Buffer.cs index 6a81e9d9..95ab9757 100644 --- a/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt64Buffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestReadOnlyInt64Buffer.cs @@ -92,6 +92,31 @@ public override void TestPutlong() } } + [Test] + public override void TestPutlongSpan() // J2N specific + { + Span array = new long[1]; + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ReadOnlyBufferException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Put((long[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + [Test] public override void TestPutlongArray() { diff --git a/tests/NUnit/J2N.Tests/IO/TestReadOnlySingleBuffer.cs b/tests/NUnit/J2N.Tests/IO/TestReadOnlySingleBuffer.cs index aa7fea94..0fe85486 100644 --- a/tests/NUnit/J2N.Tests/IO/TestReadOnlySingleBuffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestReadOnlySingleBuffer.cs @@ -92,6 +92,31 @@ public override void TestPutfloat() } } + [Test] + public override void TestPutfloatSpan() // J2N specific + { + Span array = new float[1]; + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ReadOnlyBufferException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Put((float[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + [Test] public override void TestPutfloatArray() { diff --git a/tests/NUnit/J2N.Tests/IO/TestSingleBuffer.cs b/tests/NUnit/J2N.Tests/IO/TestSingleBuffer.cs index ab33bff7..d876defa 100644 --- a/tests/NUnit/J2N.Tests/IO/TestSingleBuffer.cs +++ b/tests/NUnit/J2N.Tests/IO/TestSingleBuffer.cs @@ -292,6 +292,44 @@ public virtual void TestGet() } } + /* + * Class under test for SingleBuffer Get(Span) + */ + [Test] + public virtual void TestGetfloatSpan() // J2N specific + { + Span array = new float[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + SingleBuffer ret = buf.Get(array); + assertEquals(array[0], buf.Get(i), 0.01); + assertSame(ret, buf); + } + try + { + buf.Get(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferUnderflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Position = (buf.Limit); + // buf.Get((float[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + buf.Get(new float[0].AsSpan()); + } + /* * Class under test for java.nio.SingleBuffer get(float[]) */ @@ -514,6 +552,44 @@ public virtual void TestPutfloat() } } + /* + * Class under test for SingleBuffer Put(ReadOnlySpan) + */ + [Test] + public virtual void TestPutfloatSpan() // J2N specific + { + Span array = new float[1]; + buf.Clear(); + for (int i = 0; i < buf.Capacity; i++) + { + assertEquals(buf.Position, i); + array[0] = (float)i; + SingleBuffer ret = buf.Put(array); + assertEquals(buf.Get(i), (float)i, 0.0); + assertSame(ret, buf); + } + try + { + buf.Put(array); + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (BufferOverflowException e) + { + // expected + } + // J2N: Null converts to empty span, should not throw + //try + //{ + // buf.Position = (buf.Limit); + // buf.Put((float[])null); + // fail("Should throw Exception"); //$NON-NLS-1$ + //} + //catch (ArgumentNullException e) + //{ + // // expected + //} + } + /* * Class under test for java.nio.SingleBuffer put(float[]) */ diff --git a/tests/NUnit/J2N.Tests/IO/TestSingleBuffer2.cs b/tests/NUnit/J2N.Tests/IO/TestSingleBuffer2.cs index 756dfc23..ea2f1604 100644 --- a/tests/NUnit/J2N.Tests/IO/TestSingleBuffer2.cs +++ b/tests/NUnit/J2N.Tests/IO/TestSingleBuffer2.cs @@ -58,6 +58,15 @@ private static void bulkGet(SingleBuffer b) ck(b, (long)a[i + 7], (long)((float)Ic(i))); } + private static void bulkGetSpan(SingleBuffer b) // J2N specific + { + int n = b.Capacity; + Span a = new float[n + 7]; + b.Get(a.Slice(7, n)); + for (int i = 0; i < n; i++) + ck(b, (long)a[i + 7], (long)((float)Ic(i))); + } + private static void relPut(SingleBuffer b) { int n = b.Capacity; @@ -88,6 +97,17 @@ private static void bulkPutArray(SingleBuffer b) b.Flip(); } + private static void bulkPutSpan(SingleBuffer b) // J2N specific + { + int n = b.Capacity; + b.Clear(); + Span a = new float[n + 7]; + for (int i = 0; i < n; i++) + a[i + 7] = (float)Ic(i); + b.Put(a.Slice(7, n)); + b.Flip(); + } + private static void bulkPutBuffer(SingleBuffer b) { int n = b.Capacity; @@ -203,9 +223,22 @@ public static void test(int level, SingleBuffer b, bool direct) absGet(b); bulkGet(b); + relPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + + absPut(b); // J2N specific + relGet(b); + absGet(b); + bulkGetSpan(b); + bulkPutArray(b); relGet(b); + bulkPutSpan(b); // J2N specific + relGet(b); + bulkPutBuffer(b); relGet(b); @@ -421,6 +454,11 @@ public static void test(int level, SingleBuffer b, bool direct) bulkPutArray(rb); }); + tryCatch(b, typeof(ReadOnlyBufferException), () => + { + bulkPutSpan(rb); // J2N specific + }); + tryCatch(b, typeof(ReadOnlyBufferException), () => { bulkPutBuffer(rb); diff --git a/tests/NUnit/J2N.Tests/IO/TestWrappedCharBuffer3.cs b/tests/NUnit/J2N.Tests/IO/TestWrappedCharBuffer3.cs new file mode 100644 index 00000000..301c0953 --- /dev/null +++ b/tests/NUnit/J2N.Tests/IO/TestWrappedCharBuffer3.cs @@ -0,0 +1,165 @@ +using NUnit.Framework; +using System; +using System.Text; + +namespace J2N.IO +{ + internal class TestWrappedCharBuffer3 : TestReadOnlyCharBuffer + { + protected static readonly StringBuilder TEST_STRINGBUILDER = new StringBuilder("123456789abcdef12345"); + + public override void SetUp() + { + base.SetUp(); + buf = CharBuffer.Wrap(TEST_STRINGBUILDER); + baseBuf = buf; + } + + public override void TearDown() + { + base.TearDown(); + baseBuf = null; + buf = null; + } + + [Test] + public void TestWrappedCharSequence_IllegalArg() + { + StringBuilder str = TEST_STRINGBUILDER; + try + { + CharBuffer.Wrap(str, -1, 0 - -1); // J2N: Corrected 3rd parameter + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ArgumentOutOfRangeException e) + { + // expected + } + try + { + CharBuffer.Wrap(str, 21, 21 - 21); // J2N: Corrected 3rd parameter + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ArgumentOutOfRangeException e) + { + // expected + } + try + { + CharBuffer.Wrap(str, 2, 1 - 2); // J2N: Corrected 3rd parameter + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ArgumentOutOfRangeException e) + { + // expected + } + try + { + CharBuffer.Wrap(str, 0, 21 - 0); // J2N: Corrected 3rd parameter + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ArgumentOutOfRangeException e) + { + // expected + } + try + { + CharBuffer.Wrap((String)null, -1, 21 - -1); // J2N: Corrected 3rd parameter + fail("Should throw Exception"); //$NON-NLS-1$ + } + catch (ArgumentNullException e) + { + // expected + } + } + + [Test] + public override void TestArray() + { + try + { + var _ = buf.Array; + fail("Should throw UnsupportedOperationException"); //$NON-NLS-1$ + } + catch (NotSupportedException e) + { + } + } + + [Test] + public override void TestPutcharArrayintint() + { + char[] array = new char[1]; + try + { + buf.Put(array, 0, array.Length - 0); // J2N: Corrected 3rd parameter + fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$ + } + catch (ReadOnlyBufferException e) + { + // expected + } + try + { + buf.Put((char[])null, 0, 1 - 0); // J2N: Corrected 3rd parameter + fail("Should throw NullPointerException"); //$NON-NLS-1$ + } + catch (ArgumentNullException e) + { + // expected + } + try + { + buf.Put(new char[buf.Capacity + 1], 0, (buf.Capacity + 1) - 0); // J2N: Corrected 3rd parameter + fail("Should throw BufferOverflowException"); //$NON-NLS-1$ + } + catch (BufferOverflowException e) + { + // expected + } + try + { + buf.Put(array, -1, array.Length - -1); // J2N: Corrected 3rd parameter + fail("Should throw IndexOutOfBoundsException"); //$NON-NLS-1$ + } + catch (ArgumentOutOfRangeException e) + { + // expected + } + } + + [Test] + public override void TestPutCharBuffer() + { + CharBuffer other = CharBuffer.Allocate(1); + try + { + buf.Put(other); + fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$ + } + catch (ReadOnlyBufferException e) + { + // expected + } + try + { + buf.Put((CharBuffer)null); + fail("Should throw NullPointerException"); //$NON-NLS-1$ + } + catch (ArgumentNullException e) + { + // expected + } + try + { + buf.Put(buf); + fail("Should throw IllegalArgumentException"); //$NON-NLS-1$ + } + catch (ArgumentException e) + { + // expected + } + } + + } +}