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

namespace Gelf.Extensions.Logging
{
    public class UdpGelfClient : IGelfClient
    {
        private const int MaxChunks = 128;
        private const int MaxChunkSize = 8192;
        private const int MessageHeaderSize = 12;
        private const int MessageIdSize = 8;
        private const int MaxMessageBodySize = MaxChunkSize - MessageHeaderSize;

        private readonly UdpClient _udpClient;
        private readonly GelfLoggerOptions _options;
        private readonly Random _random;

        public UdpGelfClient(GelfLoggerOptions options)
        {
            _options = options;
            _udpClient = new UdpClient(_options.Host, _options.Port);
            _random = new Random();
        }

        public async Task SendMessageAsync(GelfMessage message)
        {
            var messageBytes = Encoding.UTF8.GetBytes(message.ToJson());

            if (_options.CompressUdp && messageBytes.Length > _options.UdpCompressionThreshold)
            {
                messageBytes = await CompressMessageAsync(messageBytes);
            }

            foreach (var messageChunk in ChunkMessage(messageBytes))
            {
                await _udpClient.SendAsync(messageChunk, messageChunk.Length);
            }
        }

        private static async Task<byte[]> CompressMessageAsync(byte[] messageBytes)
        {
            using var outputStream = new MemoryStream();
            using (var gzipStream = new GZipStream(outputStream, CompressionMode.Compress))
            {
                await gzipStream.WriteAsync(messageBytes, 0, messageBytes.Length);
            }
            return outputStream.ToArray();
        }

        private IEnumerable<byte[]> ChunkMessage(byte[] messageBytes)
        {
            if (messageBytes.Length < MaxChunkSize)
            {
                yield return messageBytes;
                yield break;
            }

            var sequenceCount = (int) Math.Ceiling(messageBytes.Length / (double) MaxMessageBodySize);
            if (sequenceCount > MaxChunks)
            {
                Debug.Fail($"GELF message contains {sequenceCount} chunks, exceeding the maximum of {MaxChunks}.");
                yield break;
            }

            var messageId = GetMessageId();
            for (var sequenceNumber = 0; sequenceNumber < sequenceCount; sequenceNumber++)
            {
                var messageHeader = GetMessageHeader(sequenceNumber, sequenceCount, messageId);
                var chunkStartIndex = sequenceNumber * MaxMessageBodySize;
                var messageBodySize = Math.Min(messageBytes.Length - chunkStartIndex, MaxMessageBodySize);
                var chunk = new byte[messageBodySize + MessageHeaderSize];

                Array.Copy(messageHeader, chunk, MessageHeaderSize);
                Array.ConstrainedCopy(messageBytes, chunkStartIndex, chunk, MessageHeaderSize, messageBodySize);<commit_msg>Use UdpMaxChunkSize option instead of MaxChunkSizeConst<commit_after>﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Gelf.Extensions.Logging
{
    public class UdpGelfClient : IGelfClient
    {
        private const int MaxChunks = 128;
        private const int MessageHeaderSize = 12;
        private const int MessageIdSize = 8;

        private readonly int _maxMessageBodySize;

        private readonly UdpClient _udpClient;
        private readonly GelfLoggerOptions _options;
        private readonly Random _random;

        public UdpGelfClient(GelfLoggerOptions options)
        {
            _options = options;
            _maxMessageBodySize = options.UdpMaxChunkSize - MessageHeaderSize;
            _udpClient = new UdpClient(_options.Host, _options.Port);
            _random = new Random();
        }

        public async Task SendMessageAsync(GelfMessage message)
        {
            var messageBytes = Encoding.UTF8.GetBytes(message.ToJson());

            if (_options.CompressUdp && messageBytes.Length > _options.UdpCompressionThreshold)
            {
                messageBytes = await CompressMessageAsync(messageBytes);
            }

            foreach (var messageChunk in ChunkMessage(messageBytes))
            {
                await _udpClient.SendAsync(messageChunk, messageChunk.Length);
            }
        }

        private static async Task<byte[]> CompressMessageAsync(byte[] messageBytes)
        {
            using var outputStream = new MemoryStream();
            using (var gzipStream = new GZipStream(outputStream, CompressionMode.Compress))
            {
                await gzipStream.WriteAsync(messageBytes, 0, messageBytes.Length);
            }
            return outputStream.ToArray();
        }

        private IEnumerable<byte[]> ChunkMessage(byte[] messageBytes)
        {
            if (messageBytes.Length < _options.UdpMaxChunkSize)
            {
                yield return messageBytes;
                yield break;
            }

            var sequenceCount = (int) Math.Ceiling(messageBytes.Length / (double) _maxMessageBodySize);
            if (sequenceCount > MaxChunks)
            {
                Debug.Fail($"GELF message contains {sequenceCount} chunks, exceeding the maximum of {MaxChunks}.");
                yield break;
            }

            var messageId = GetMessageId();
            for (var sequenceNumber = 0; sequenceNumber < sequenceCount; sequenceNumber++)
            {
                var messageHeader = GetMessageHeader(sequenceNumber, sequenceCount, messageId);
                var chunkStartIndex = sequenceNumber * _maxMessageBodySize;
                var messageBodySize = Math.Min(messageBytes.Length - chunkStartIndex, _maxMessageBodySize);
                var chunk = new byte[messageBodySize + MessageHeaderSize];

                Array.Copy(messageHeader, chunk, MessageHeaderSize);
                Array.ConstrainedCopy(messageBytes, chunkStartIndex, chunk, MessageHeaderSize, messageBodySize);
{'fraction_non_alphanumeric': 0.07271280827366747, 'fraction_numerical': 0.0035003977724741447, 'mean_word_length': 2.2773722627737225, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, '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': '3738842', 'n_tokens_mistral': 1578, 'n_tokens_neox': 1511, 'n_words': 376}