<commit_before>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace GladNet
{
	//TODO: Make this thread safe
	/// <summary>
	/// Base implementation of a TCP network client for PSOBB.
	/// It is built around the <see cref="TcpClient"/> provided in .NET and manages, destroys
	/// and creates them depending on the externally provided API.
	/// </summary>
	public sealed class DotNetTcpClientNetworkClient : NetworkClientBase, IConnectable, IDisconnectable, IDisposable,
		IBytesWrittable, IBytesReadable
	{
		//Can't be readonly because clients may want to reconnect
		private TcpClient InternalTcpClient { get; set; }

		/// <summary>
		/// Creates a new <see cref="DotNetTcpClientNetworkClient"/> with an intialized
		/// internal <see cref="InternalTcpClient"/>. If you want to supply your own
		/// <see cref="TcpClient"/> then call the ctor overload.
		/// </summary>
		public DotNetTcpClientNetworkClient()
			: this(new TcpClient())
		{
		}

		/// <summary>
		/// Creates a new <see cref="DotNetTcpClientNetworkClient"/> with the provided
		/// non-null <see cref="tcpClient"/>. This overload should be used if you for some reason
		/// want to use an externally created <see cref="TcpClient"/>.
		/// </summary>
		/// <param name="tcpClient">The <see cref="TcpClient"/> to use.</param>
		public DotNetTcpClientNetworkClient(TcpClient tcpClient)
		{
			InternalTcpClient = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
		}

		//TODO: This no longer works properly due to the stream setting.
		/// <inheritdoc />
		public override async Task<bool> ConnectAsync(string address, int port)
		{
			if(address == null) throw new ArgumentNullException(nameof(address));
			if(port <= 0) throw new ArgumentOutOfRangeException(nameof(port));

			await DisconnectAsync(10)
				.ConfigureAwait(false);

			InternalTcpClient = new TcpClient();

			//TODO: Logging
			//TODO: Should we allow reconnects?
			await InternalTcpClient.ConnectAsync(address, port)
				.ConfigureAwait(false);

			return true;
		}

		/// <inheritdoc />
		public override Task ClearReadBuffers()
		{
			//We can't do anything, there is no buffer
			return Task.CompletedTask;
		}

		//TODO: This no longer works properly due to the stream setting.
		/// <inheritdoc />
		public override Task DisconnectAsync(int delay)
		{
			if(InternalTcpClient == null)
				return Task.CompletedTask;

			//TODO: Is this ok? Will it still work on netstandard?
#if NET46
			if(InternalTcpClient.Connected)
				InternalTcpClient.GetStream().Close(delay);

			InternalTcpClient.Close();
#endif
			InternalTcpClient.Dispose();

			return Task.CompletedTask;
		}

		/// <inheritdoc />
		public override Task WriteAsync(byte[] bytes, int offset, int count)
		{
			if(!InternalTcpClient.Connected)
				throw new InvalidOperationException($"The internal {nameof(TcpClient)}: {nameof(InternalTcpClient)} is not connected to an endpoint. You must call {nameof(ConnectAsync)} before writing any bytes.");

			//We can just write the bytes to the stream if we're connected.
			return InternalTcpClient.GetStream().WriteAsync(bytes, offset, count);
		}

		/// <inheritdoc />
		public override async Task<int> ReadAsync(byte[] buffer, int start, int count, CancellationToken token)
		{
			if(!InternalTcpClient.Connected)
				throw new InvalidOperationException($"The internal {nameof(TcpClient)}: {nameof(InternalTcpClient)} is not connected to an endpoint. You must call {nameof(ConnectAsync)} before reading any bytes.");

			Stream stream = InternalTcpClient.GetStream(); //TODO: Is it expensive to call GetStream? Should we cache it

			//Sockets nor NetworkStreams allow us to cancel
			//They will block even if you give them the token and then
			//throw when disposed or closed
			//So we do a check on the token in the catch to see if it threw
			//because of a requested cancellation
			int i = 0;
			try
			{
				int end = count + start;
				for(i = start; i < end && !token.IsCancellationRequested;)
				{
					int countRead = await stream.ReadAsync(buffer, i, end - i, token)
						.ConfigureAwait(false);

					//If we encounter a 0 that means the socket closed most likely
					//We should indicate we read nothing. This should be handled and propagated back up
					//the pipeline
					if(countRead == 0)
						throw new InvalidOperationException($"Socket read 0. Likely disconnection.");<commit_msg>Made DotNetTcpClientNetworkClient throw networkdisconnection exceptions<commit_after>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GladNet;

namespace GladNet
{
	//TODO: Make this thread safe
	/// <summary>
	/// Base implementation of a TCP network client for PSOBB.
	/// It is built around the <see cref="TcpClient"/> provided in .NET and manages, destroys
	/// and creates them depending on the externally provided API.
	/// </summary>
	public sealed class DotNetTcpClientNetworkClient : NetworkClientBase, IConnectable, IDisconnectable, IDisposable,
		IBytesWrittable, IBytesReadable
	{
		//TODO: Expose the connectivity of this better, so that we don't have to call and pray.
		//Can't be readonly because clients may want to reconnect
		private TcpClient InternalTcpClient { get; set; }

		/// <summary>
		/// Creates a new <see cref="DotNetTcpClientNetworkClient"/> with an intialized
		/// internal <see cref="InternalTcpClient"/>. If you want to supply your own
		/// <see cref="TcpClient"/> then call the ctor overload.
		/// </summary>
		public DotNetTcpClientNetworkClient()
			: this(new TcpClient())
		{
		}

		/// <summary>
		/// Creates a new <see cref="DotNetTcpClientNetworkClient"/> with the provided
		/// non-null <see cref="tcpClient"/>. This overload should be used if you for some reason
		/// want to use an externally created <see cref="TcpClient"/>.
		/// </summary>
		/// <param name="tcpClient">The <see cref="TcpClient"/> to use.</param>
		public DotNetTcpClientNetworkClient(TcpClient tcpClient)
		{
			InternalTcpClient = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
		}

		//TODO: This no longer works properly due to the stream setting.
		/// <inheritdoc />
		public override async Task<bool> ConnectAsync(string address, int port)
		{
			if(address == null) throw new ArgumentNullException(nameof(address));
			if(port <= 0) throw new ArgumentOutOfRangeException(nameof(port));

			await DisconnectAsync(10)
				.ConfigureAwait(false);

			InternalTcpClient = new TcpClient();

			//TODO: Logging
			//TODO: Should we allow reconnects?
			await InternalTcpClient.ConnectAsync(address, port)
				.ConfigureAwait(false);

			return true;
		}

		/// <inheritdoc />
		public override Task ClearReadBuffers()
		{
			//We can't do anything, there is no buffer
			return Task.CompletedTask;
		}

		//TODO: This no longer works properly due to the stream setting.
		/// <inheritdoc />
		public override Task DisconnectAsync(int delay)
		{
			if(InternalTcpClient == null)
				return Task.CompletedTask;

			//TODO: Is this ok? Will it still work on netstandard?
#if NET46
			if(InternalTcpClient.Connected)
				InternalTcpClient.GetStream().Close(delay);

			InternalTcpClient.Close();
#endif
			InternalTcpClient.Dispose();

			return Task.CompletedTask;
		}

		/// <inheritdoc />
		public override Task WriteAsync(byte[] bytes, int offset, int count)
		{
			if(!InternalTcpClient.Connected)
				throw new NetworkDisconnectedException($"The internal {nameof(TcpClient)}: {nameof(InternalTcpClient)} is not connected to an endpoint. You must call {nameof(ConnectAsync)} before writing any bytes.");

			//We can just write the bytes to the stream if we're connected.
			return InternalTcpClient.GetStream().WriteAsync(bytes, offset, count);
		}

		/// <inheritdoc />
		public override async Task<int> ReadAsync(byte[] buffer, int start, int count, CancellationToken token)
		{
			if(!InternalTcpClient.Connected)
				throw new NetworkDisconnectedException($"The internal {nameof(TcpClient)}: {nameof(InternalTcpClient)} is not connected to an endpoint. You must call {nameof(ConnectAsync)} before reading any bytes.");

			Stream stream = InternalTcpClient.GetStream(); //TODO: Is it expensive to call GetStream? Should we cache it

			//Sockets nor NetworkStreams allow us to cancel
			//They will block even if you give them the token and then
			//throw when disposed or closed
			//So we do a check on the token in the catch to see if it threw
			//because of a requested cancellation
			int i = 0;
			try
			{
				int end = count + start;
				for(i = start; i < end && !token.IsCancellationRequested;)
				{
					int countRead = await stream.ReadAsync(buffer, i, end - i, token)
						.ConfigureAwait(false);

					//If we encounter a 0 that means the socket closed most likely
					//We should indicate we read nothing. This should be handled and propagated back up
					//the pipeline
					if(countRead == 0)
						throw new InvalidOperationException($"Socket read 0. Likely disconnection.");
{'fraction_non_alphanumeric': 0.10373174689021092, 'fraction_numerical': 0.0019469983775013521, 'mean_word_length': 3.726993865030675, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 49, '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': '5713532', 'n_tokens_mistral': 3028, 'n_tokens_neox': 2638, 'n_words': 943}