<commit_before>// dnlib: See LICENSE.txt for more info

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using dnlib.DotNet.Writer;

namespace dnlib.IO {
	/// <summary>
	/// Thrown by a <see cref="DataReader"/> when it can't read data or if the caller tries to set an invalid offset
	/// </summary>
	[Serializable]
	public sealed class DataReaderException : IOException {
		internal DataReaderException(string message) : base(message) { }
		internal DataReaderException(SerializationInfo info, StreamingContext context) : base(info, context) { }
	}

	/// <summary>
	/// Reads data
	/// </summary>
	[DebuggerDisplay("{StartOffset,h}-{EndOffset,h} Length={Length} BytesLeft={BytesLeft}")]
	public struct DataReader {
		/// <summary>
		/// Gets the start offset of the data
		/// </summary>
		public readonly uint StartOffset => startOffset;

		/// <summary>
		/// Gets the end offset of the data, not inclusive
		/// </summary>
		public readonly uint EndOffset => endOffset;

		/// <summary>
		/// Gets the total length of the data
		/// </summary>
		public readonly uint Length => endOffset - startOffset;

		/// <summary>
		/// Gets the current offset. This is between <see cref="StartOffset"/> and <see cref="EndOffset"/> (inclusive)
		/// </summary>
		public uint CurrentOffset {
			readonly get => currentOffset;
			set {
				VerifyState();
				if (value < startOffset || value > endOffset) {
					// Invalid offsets should be an IOException and not an ArgumentException
					ThrowDataReaderException("Invalid new " + nameof(CurrentOffset));
				}
				currentOffset = value;
				VerifyState();
			}
		}

		/// <summary>
		/// Gets/sets the position relative to <see cref="StartOffset"/>
		/// </summary>
		public uint Position {
			readonly get => currentOffset - startOffset;
			set {
				VerifyState();
				if (value > Length) {
					// Invalid positions should be an IOException and not an ArgumentException
					ThrowDataReaderException("Invalid new " + nameof(Position));
				}
				currentOffset = startOffset + value;
				VerifyState();
			}
		}

		/// <summary>
		/// Gets the number of bytes that can be read without throwing an exception
		/// </summary>
		public readonly uint BytesLeft => endOffset - currentOffset;

		readonly DataStream stream;
		readonly uint startOffset;
		readonly uint endOffset;
		uint currentOffset;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="stream">Stream</param>
		/// <param name="offset">Start offset of data</param>
		/// <param name="length">Length of data</param>
		public DataReader(DataStream stream, uint offset, uint length) {
			Debug.Assert(!(stream is null) || (offset == 0 && length == 0));
			Debug.Assert(offset + length >= offset);
			this.stream = stream;
			startOffset = offset;
			endOffset = offset + length;
			currentOffset = offset;
			VerifyState();
		}

		[Conditional("DEBUG")]
		readonly void VerifyState() {
			Debug.Assert(startOffset <= currentOffset);
			Debug.Assert(currentOffset <= endOffset);
		}

		static void ThrowNoMoreBytesLeft() => throw new DataReaderException("There's not enough bytes left to read");
		static void ThrowDataReaderException(string message) => throw new DataReaderException(message);
		static void ThrowInvalidOperationException() => throw new InvalidOperationException();
		static void ThrowArgumentNullException(string paramName) => throw new ArgumentNullException(paramName);
		static void ThrowInvalidArgument(string paramName) => throw new DataReaderException("Invalid argument value");

		/// <summary>
		/// Resets the reader so it points to the start of the data
		/// </summary>
		public void Reset() => currentOffset = startOffset;

		/// <summary>
		/// Creates a new reader that can access a smaller part of this reader
		/// </summary>
		/// <param name="start">Start position relative to <see cref="StartOffset"/></param>
		/// <param name="length">Length of data</param>
		/// <returns></returns>
		public DataReader Slice(uint start, uint length) {
			if ((ulong)start + length > Length)
				ThrowInvalidArgument(nameof(length));
			return new DataReader(stream, startOffset + start, length);
		}

		/// <summary>
		/// Creates a new reader that can access everything from <paramref name="start"/> to the end of the data
		/// </summary>
		/// <param name="start">Start position relative to <see cref="StartOffset"/></param>
		/// <returns></returns>
		public DataReader Slice(uint start) {
			if (start > Length)
				ThrowInvalidArgument(nameof(start));
			return Slice(start, Length - start);
		}

		/// <summary>
		/// Creates a new reader that can access a smaller part of this reader
		/// </summary>
		/// <param name="start">Start position relative to <see cref="StartOffset"/></param>
		/// <param name="length">Length of data</param>
		/// <returns></returns>
		public DataReader Slice(int start, int length) {
			if (start < 0)
				ThrowInvalidArgument(nameof(start));
			if (length < 0)
				ThrowInvalidArgument(nameof(length));
			return Slice((uint)start, (uint)length);
		}

		/// <summary>
		/// Creates a new reader that can access everything from <paramref name="start"/> to the end of the data
		/// </summary>
		/// <param name="start">Start position relative to <see cref="StartOffset"/></param>
		/// <returns></returns>
		public DataReader Slice(int start) {
			if (start < 0)
				ThrowInvalidArgument(nameof(start));
			if ((uint)start > Length)
				ThrowInvalidArgument(nameof(start));
			return Slice((uint)start, Length - (uint)start);
		}

		/// <summary>
		/// Checks if it's possible to read <paramref name="length"/> bytes
		/// </summary>
		/// <param name="length">Length of data</param>
		/// <returns></returns>
		public readonly bool CanRead(int length) => length >= 0 && (uint)length <= BytesLeft;

		/// <summary>
		/// Checks if it's possible to read <paramref name="length"/> bytes
		/// </summary>
		/// <param name="length">Length of data</param><commit_msg>Add readonly to DataReader.Slice()<commit_after>// dnlib: See LICENSE.txt for more info

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using dnlib.DotNet.Writer;

namespace dnlib.IO {
	/// <summary>
	/// Thrown by a <see cref="DataReader"/> when it can't read data or if the caller tries to set an invalid offset
	/// </summary>
	[Serializable]
	public sealed class DataReaderException : IOException {
		internal DataReaderException(string message) : base(message) { }
		internal DataReaderException(SerializationInfo info, StreamingContext context) : base(info, context) { }
	}

	/// <summary>
	/// Reads data
	/// </summary>
	[DebuggerDisplay("{StartOffset,h}-{EndOffset,h} Length={Length} BytesLeft={BytesLeft}")]
	public struct DataReader {
		/// <summary>
		/// Gets the start offset of the data
		/// </summary>
		public readonly uint StartOffset => startOffset;

		/// <summary>
		/// Gets the end offset of the data, not inclusive
		/// </summary>
		public readonly uint EndOffset => endOffset;

		/// <summary>
		/// Gets the total length of the data
		/// </summary>
		public readonly uint Length => endOffset - startOffset;

		/// <summary>
		/// Gets the current offset. This is between <see cref="StartOffset"/> and <see cref="EndOffset"/> (inclusive)
		/// </summary>
		public uint CurrentOffset {
			readonly get => currentOffset;
			set {
				VerifyState();
				if (value < startOffset || value > endOffset) {
					// Invalid offsets should be an IOException and not an ArgumentException
					ThrowDataReaderException("Invalid new " + nameof(CurrentOffset));
				}
				currentOffset = value;
				VerifyState();
			}
		}

		/// <summary>
		/// Gets/sets the position relative to <see cref="StartOffset"/>
		/// </summary>
		public uint Position {
			readonly get => currentOffset - startOffset;
			set {
				VerifyState();
				if (value > Length) {
					// Invalid positions should be an IOException and not an ArgumentException
					ThrowDataReaderException("Invalid new " + nameof(Position));
				}
				currentOffset = startOffset + value;
				VerifyState();
			}
		}

		/// <summary>
		/// Gets the number of bytes that can be read without throwing an exception
		/// </summary>
		public readonly uint BytesLeft => endOffset - currentOffset;

		readonly DataStream stream;
		readonly uint startOffset;
		readonly uint endOffset;
		uint currentOffset;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="stream">Stream</param>
		/// <param name="offset">Start offset of data</param>
		/// <param name="length">Length of data</param>
		public DataReader(DataStream stream, uint offset, uint length) {
			Debug.Assert(!(stream is null) || (offset == 0 && length == 0));
			Debug.Assert(offset + length >= offset);
			this.stream = stream;
			startOffset = offset;
			endOffset = offset + length;
			currentOffset = offset;
			VerifyState();
		}

		[Conditional("DEBUG")]
		readonly void VerifyState() {
			Debug.Assert(startOffset <= currentOffset);
			Debug.Assert(currentOffset <= endOffset);
		}

		static void ThrowNoMoreBytesLeft() => throw new DataReaderException("There's not enough bytes left to read");
		static void ThrowDataReaderException(string message) => throw new DataReaderException(message);
		static void ThrowInvalidOperationException() => throw new InvalidOperationException();
		static void ThrowArgumentNullException(string paramName) => throw new ArgumentNullException(paramName);
		static void ThrowInvalidArgument(string paramName) => throw new DataReaderException("Invalid argument value");

		/// <summary>
		/// Resets the reader so it points to the start of the data
		/// </summary>
		public void Reset() => currentOffset = startOffset;

		/// <summary>
		/// Creates a new reader that can access a smaller part of this reader
		/// </summary>
		/// <param name="start">Start position relative to <see cref="StartOffset"/></param>
		/// <param name="length">Length of data</param>
		/// <returns></returns>
		public readonly DataReader Slice(uint start, uint length) {
			if ((ulong)start + length > Length)
				ThrowInvalidArgument(nameof(length));
			return new DataReader(stream, startOffset + start, length);
		}

		/// <summary>
		/// Creates a new reader that can access everything from <paramref name="start"/> to the end of the data
		/// </summary>
		/// <param name="start">Start position relative to <see cref="StartOffset"/></param>
		/// <returns></returns>
		public readonly DataReader Slice(uint start) {
			if (start > Length)
				ThrowInvalidArgument(nameof(start));
			return Slice(start, Length - start);
		}

		/// <summary>
		/// Creates a new reader that can access a smaller part of this reader
		/// </summary>
		/// <param name="start">Start position relative to <see cref="StartOffset"/></param>
		/// <param name="length">Length of data</param>
		/// <returns></returns>
		public readonly DataReader Slice(int start, int length) {
			if (start < 0)
				ThrowInvalidArgument(nameof(start));
			if (length < 0)
				ThrowInvalidArgument(nameof(length));
			return Slice((uint)start, (uint)length);
		}

		/// <summary>
		/// Creates a new reader that can access everything from <paramref name="start"/> to the end of the data
		/// </summary>
		/// <param name="start">Start position relative to <see cref="StartOffset"/></param>
		/// <returns></returns>
		public readonly DataReader Slice(int start) {
			if (start < 0)
				ThrowInvalidArgument(nameof(start));
			if ((uint)start > Length)
				ThrowInvalidArgument(nameof(start));
			return Slice((uint)start, Length - (uint)start);
		}

		/// <summary>
		/// Checks if it's possible to read <paramref name="length"/> bytes
		/// </summary>
		/// <param name="length">Length of data</param>
		/// <returns></returns>
		public readonly bool CanRead(int length) => length >= 0 && (uint)length <= BytesLeft;

		/// <summary>
		/// Checks if it's possible to read <paramref name="length"/> bytes
		/// </summary>
		/// <param name="length">Length of data</param>
{'fraction_non_alphanumeric': 0.1384080178911621, 'fraction_numerical': 0.0009939534498467654, 'mean_word_length': 3.4934871604019353, 'pattern_counts': {'":': 0, '<': 169, '<?xml version=': 0, '>': 195, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2717482', 'n_tokens_mistral': 3799, 'n_tokens_neox': 3338, 'n_words': 1102}