mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-04 03:27:21 -05:00 
			
		
		
		
	Merge pull request #6567 from Bond-009/alloc
This commit is contained in:
		
						commit
						b51a3cbf98
					
				@ -11,6 +11,7 @@ using System.Net.Http;
 | 
			
		||||
using System.Net.Mime;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Jellyfin.Extensions;
 | 
			
		||||
using MediaBrowser.Common.Extensions;
 | 
			
		||||
using MediaBrowser.Common.Net;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
@ -82,9 +83,7 @@ namespace Emby.Dlna.Eventing
 | 
			
		||||
            if (!string.IsNullOrEmpty(header))
 | 
			
		||||
            {
 | 
			
		||||
                // Starts with SECOND-
 | 
			
		||||
                header = header.Split('-')[^1];
 | 
			
		||||
 | 
			
		||||
                if (int.TryParse(header, NumberStyles.Integer, _usCulture, out var val))
 | 
			
		||||
                if (int.TryParse(header.AsSpan().RightPart('-'), NumberStyles.Integer, _usCulture, out var val))
 | 
			
		||||
                {
 | 
			
		||||
                    return val;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ using System.Linq;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Jellyfin.Extensions;
 | 
			
		||||
using Jellyfin.XmlTv;
 | 
			
		||||
using Jellyfin.XmlTv.Entities;
 | 
			
		||||
using MediaBrowser.Common.Extensions;
 | 
			
		||||
@ -89,11 +90,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 | 
			
		||||
            return UnzipIfNeeded(path, cacheFile);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private string UnzipIfNeeded(string originalUrl, string file)
 | 
			
		||||
        private string UnzipIfNeeded(ReadOnlySpan<char> originalUrl, string file)
 | 
			
		||||
        {
 | 
			
		||||
            string ext = Path.GetExtension(originalUrl.Split('?')[0]);
 | 
			
		||||
            ReadOnlySpan<char> ext = Path.GetExtension(originalUrl.LeftPart('?'));
 | 
			
		||||
 | 
			
		||||
            if (string.Equals(ext, ".gz", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
        private readonly IHttpClientFactory _httpClientFactory;
 | 
			
		||||
        private readonly IServerApplicationHost _appHost;
 | 
			
		||||
        private readonly ISocketFactory _socketFactory;
 | 
			
		||||
        private readonly INetworkManager _networkManager;
 | 
			
		||||
        private readonly IStreamHelper _streamHelper;
 | 
			
		||||
 | 
			
		||||
        private readonly JsonSerializerOptions _jsonOptions;
 | 
			
		||||
@ -50,7 +49,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
            IHttpClientFactory httpClientFactory,
 | 
			
		||||
            IServerApplicationHost appHost,
 | 
			
		||||
            ISocketFactory socketFactory,
 | 
			
		||||
            INetworkManager networkManager,
 | 
			
		||||
            IStreamHelper streamHelper,
 | 
			
		||||
            IMemoryCache memoryCache)
 | 
			
		||||
            : base(config, logger, fileSystem, memoryCache)
 | 
			
		||||
@ -58,7 +56,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
            _httpClientFactory = httpClientFactory;
 | 
			
		||||
            _appHost = appHost;
 | 
			
		||||
            _socketFactory = socketFactory;
 | 
			
		||||
            _networkManager = networkManager;
 | 
			
		||||
            _streamHelper = streamHelper;
 | 
			
		||||
 | 
			
		||||
            _jsonOptions = JsonDefaults.Options;
 | 
			
		||||
@ -70,7 +67,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
 | 
			
		||||
        protected override string ChannelIdPrefix => "hdhr_";
 | 
			
		||||
 | 
			
		||||
        private string GetChannelId(TunerHostInfo info, Channels i)
 | 
			
		||||
        private string GetChannelId(Channels i)
 | 
			
		||||
            => ChannelIdPrefix + i.GuideNumber;
 | 
			
		||||
 | 
			
		||||
        internal async Task<List<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
 | 
			
		||||
@ -103,7 +100,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
            {
 | 
			
		||||
                Name = i.GuideName,
 | 
			
		||||
                Number = i.GuideNumber,
 | 
			
		||||
                Id = GetChannelId(tuner, i),
 | 
			
		||||
                Id = GetChannelId(i),
 | 
			
		||||
                IsFavorite = i.Favorite,
 | 
			
		||||
                TunerHostId = tuner.Id,
 | 
			
		||||
                IsHD = i.HD,
 | 
			
		||||
@ -255,7 +252,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
        {
 | 
			
		||||
            var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
 | 
			
		||||
 | 
			
		||||
            var tuners = new List<LiveTvTunerInfo>();
 | 
			
		||||
            var tuners = new List<LiveTvTunerInfo>(model.TunerCount);
 | 
			
		||||
 | 
			
		||||
            var uri = new Uri(GetApiUrl(info));
 | 
			
		||||
 | 
			
		||||
@ -264,10 +261,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
                // Legacy HdHomeruns are IPv4 only
 | 
			
		||||
                var ipInfo = IPAddress.Parse(uri.Host);
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < model.TunerCount; ++i)
 | 
			
		||||
                for (int i = 0; i < model.TunerCount; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    var name = string.Format(CultureInfo.InvariantCulture, "Tuner {0}", i + 1);
 | 
			
		||||
                    var currentChannel = "none"; // @todo Get current channel and map back to Station Id
 | 
			
		||||
                    var currentChannel = "none"; // TODO: Get current channel and map back to Station Id
 | 
			
		||||
                    var isAvailable = await manager.CheckTunerAvailability(ipInfo, i, cancellationToken).ConfigureAwait(false);
 | 
			
		||||
                    var status = isAvailable ? LiveTvTunerStatus.Available : LiveTvTunerStatus.LiveTv;
 | 
			
		||||
                    tuners.Add(new LiveTvTunerInfo
 | 
			
		||||
@ -455,28 +452,28 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
                Path = url,
 | 
			
		||||
                Protocol = MediaProtocol.Udp,
 | 
			
		||||
                MediaStreams = new List<MediaStream>
 | 
			
		||||
                        {
 | 
			
		||||
                            new MediaStream
 | 
			
		||||
                            {
 | 
			
		||||
                                Type = MediaStreamType.Video,
 | 
			
		||||
                                // Set the index to -1 because we don't know the exact index of the video stream within the container
 | 
			
		||||
                                Index = -1,
 | 
			
		||||
                                IsInterlaced = isInterlaced,
 | 
			
		||||
                                Codec = videoCodec,
 | 
			
		||||
                                Width = width,
 | 
			
		||||
                                Height = height,
 | 
			
		||||
                                BitRate = videoBitrate,
 | 
			
		||||
                                NalLengthSize = nal
 | 
			
		||||
                            },
 | 
			
		||||
                            new MediaStream
 | 
			
		||||
                            {
 | 
			
		||||
                                Type = MediaStreamType.Audio,
 | 
			
		||||
                                // Set the index to -1 because we don't know the exact index of the audio stream within the container
 | 
			
		||||
                                Index = -1,
 | 
			
		||||
                                Codec = audioCodec,
 | 
			
		||||
                                BitRate = audioBitrate
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                {
 | 
			
		||||
                    new MediaStream
 | 
			
		||||
                    {
 | 
			
		||||
                        Type = MediaStreamType.Video,
 | 
			
		||||
                        // Set the index to -1 because we don't know the exact index of the video stream within the container
 | 
			
		||||
                        Index = -1,
 | 
			
		||||
                        IsInterlaced = isInterlaced,
 | 
			
		||||
                        Codec = videoCodec,
 | 
			
		||||
                        Width = width,
 | 
			
		||||
                        Height = height,
 | 
			
		||||
                        BitRate = videoBitrate,
 | 
			
		||||
                        NalLengthSize = nal
 | 
			
		||||
                    },
 | 
			
		||||
                    new MediaStream
 | 
			
		||||
                    {
 | 
			
		||||
                        Type = MediaStreamType.Audio,
 | 
			
		||||
                        // Set the index to -1 because we don't know the exact index of the audio stream within the container
 | 
			
		||||
                        Index = -1,
 | 
			
		||||
                        Codec = audioCodec,
 | 
			
		||||
                        BitRate = audioBitrate
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                RequiresOpening = true,
 | 
			
		||||
                RequiresClosing = true,
 | 
			
		||||
                BufferMs = 0,
 | 
			
		||||
@ -551,7 +548,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var profile = streamId.Split('_')[0];
 | 
			
		||||
            var profile = streamId.AsSpan().LeftPart('_').ToString();
 | 
			
		||||
 | 
			
		||||
            Logger.LogInformation("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channel.Id, streamId, profile);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -238,7 +238,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/')[^1]);
 | 
			
		||||
                        numberString = Path.GetFileNameWithoutExtension(mediaUrl.AsSpan().RightPart('/')).ToString();
 | 
			
		||||
 | 
			
		||||
                        if (!IsValidChannelNumber(numberString))
 | 
			
		||||
                        {
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ using System.Net.Http;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Jellyfin.Api.Constants;
 | 
			
		||||
using Jellyfin.Extensions;
 | 
			
		||||
using MediaBrowser.Common.Extensions;
 | 
			
		||||
using MediaBrowser.Common.Net;
 | 
			
		||||
using MediaBrowser.Controller;
 | 
			
		||||
@ -199,7 +200,7 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
                throw new ResourceNotFoundException(nameof(response.Content.Headers.ContentType));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ext = response.Content.Headers.ContentType.MediaType.Split('/')[^1];
 | 
			
		||||
            var ext = response.Content.Headers.ContentType.MediaType.AsSpan().RightPart('/').ToString();
 | 
			
		||||
            var fullCachePath = GetFullCachePath(urlHash + "." + ext);
 | 
			
		||||
 | 
			
		||||
            var fullCacheDirectory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid.");
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ using System.Linq;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Jellyfin.Api.Models.StreamingDtos;
 | 
			
		||||
using Jellyfin.Extensions;
 | 
			
		||||
using MediaBrowser.Common.Configuration;
 | 
			
		||||
using MediaBrowser.Common.Extensions;
 | 
			
		||||
using MediaBrowser.Controller.Configuration;
 | 
			
		||||
@ -81,7 +82,7 @@ namespace Jellyfin.Api.Helpers
 | 
			
		||||
                throw new ResourceNotFoundException(nameof(httpRequest.Path));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var url = httpRequest.Path.Value.Split('.')[^1];
 | 
			
		||||
            var url = httpRequest.Path.Value.AsSpan().RightPart('.').ToString();
 | 
			
		||||
 | 
			
		||||
            if (string.IsNullOrEmpty(streamingRequest.AudioCodec))
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.MediaEncoding
 | 
			
		||||
                    var size = part.Split('=', 2)[^1];
 | 
			
		||||
 | 
			
		||||
                    int? scale = null;
 | 
			
		||||
                    if (size.IndexOf("kb", StringComparison.OrdinalIgnoreCase) != -1)
 | 
			
		||||
                    if (size.Contains("kb", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                    {
 | 
			
		||||
                        scale = 1024;
 | 
			
		||||
                        size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase);
 | 
			
		||||
@ -139,7 +139,7 @@ namespace MediaBrowser.Controller.MediaEncoding
 | 
			
		||||
                    var rate = part.Split('=', 2)[^1];
 | 
			
		||||
 | 
			
		||||
                    int? scale = null;
 | 
			
		||||
                    if (rate.IndexOf("kbits/s", StringComparison.OrdinalIgnoreCase) != -1)
 | 
			
		||||
                    if (rate.Contains("kbits/s", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                    {
 | 
			
		||||
                        scale = 1024;
 | 
			
		||||
                        rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using Jellyfin.Extensions;
 | 
			
		||||
using MediaBrowser.Controller.Entities;
 | 
			
		||||
using MediaBrowser.Controller.Providers;
 | 
			
		||||
using MediaBrowser.Model.Entities;
 | 
			
		||||
@ -331,7 +332,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
 | 
			
		||||
 | 
			
		||||
                    if (!string.IsNullOrWhiteSpace(text))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, _usCulture, out var runtime))
 | 
			
		||||
                        if (int.TryParse(text.AsSpan().LeftPart(' '), NumberStyles.Integer, _usCulture, out var runtime))
 | 
			
		||||
                        {
 | 
			
		||||
                            item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
@ -1378,7 +1378,7 @@ namespace MediaBrowser.MediaEncoding.Probing
 | 
			
		||||
        {
 | 
			
		||||
            var disc = tags.GetValueOrDefault(tagName);
 | 
			
		||||
 | 
			
		||||
            if (!string.IsNullOrEmpty(disc) && int.TryParse(disc.Split('/')[0], out var discNum))
 | 
			
		||||
            if (!string.IsNullOrEmpty(disc) && int.TryParse(disc.AsSpan().LeftPart('/'), out var discNum))
 | 
			
		||||
            {
 | 
			
		||||
                return discNum;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -18,14 +18,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 | 
			
		||||
            using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
 | 
			
		||||
            {
 | 
			
		||||
                writer.WriteLine("WEBVTT");
 | 
			
		||||
                writer.WriteLine(string.Empty);
 | 
			
		||||
                writer.WriteLine();
 | 
			
		||||
                writer.WriteLine("REGION");
 | 
			
		||||
                writer.WriteLine("id:subtitle");
 | 
			
		||||
                writer.WriteLine("width:80%");
 | 
			
		||||
                writer.WriteLine("lines:3");
 | 
			
		||||
                writer.WriteLine("regionanchor:50%,100%");
 | 
			
		||||
                writer.WriteLine("viewportanchor:50%,90%");
 | 
			
		||||
                writer.WriteLine(string.Empty);
 | 
			
		||||
                writer.WriteLine();
 | 
			
		||||
                foreach (var trackEvent in info.TrackEvents)
 | 
			
		||||
                {
 | 
			
		||||
                    cancellationToken.ThrowIfCancellationRequested();
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Jellyfin.Extensions;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Net
 | 
			
		||||
{
 | 
			
		||||
@ -221,7 +222,7 @@ namespace MediaBrowser.Model.Net
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // handle text/html; charset=UTF-8
 | 
			
		||||
            mimeType = mimeType.Split(';')[0];
 | 
			
		||||
            mimeType = mimeType.AsSpan().LeftPart(';').ToString();
 | 
			
		||||
 | 
			
		||||
            if (_extensionLookup.TryGetValue(mimeType, out string? result))
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using Jellyfin.Extensions;
 | 
			
		||||
using MediaBrowser.Common.Configuration;
 | 
			
		||||
using MediaBrowser.Common.Providers;
 | 
			
		||||
using MediaBrowser.Controller.Entities;
 | 
			
		||||
@ -474,7 +475,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
 | 
			
		||||
 | 
			
		||||
                        if (!string.IsNullOrWhiteSpace(text))
 | 
			
		||||
                        {
 | 
			
		||||
                            if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, UsCulture, out var runtime))
 | 
			
		||||
                            if (int.TryParse(text.AsSpan().LeftPart(' '), NumberStyles.Integer, UsCulture, out var runtime))
 | 
			
		||||
                            {
 | 
			
		||||
                                item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
@ -27,5 +27,39 @@ namespace Jellyfin.Extensions
 | 
			
		||||
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns the part on the left of the <c>needle</c>.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="haystack">The string to seek.</param>
 | 
			
		||||
        /// <param name="needle">The needle to find.</param>
 | 
			
		||||
        /// <returns>The part left of the <paramref name="needle" />.</returns>
 | 
			
		||||
        public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> haystack, char needle)
 | 
			
		||||
        {
 | 
			
		||||
            var pos = haystack.IndexOf(needle);
 | 
			
		||||
            return pos == -1 ? haystack : haystack[..pos];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns the part on the right of the <c>needle</c>.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="haystack">The string to seek.</param>
 | 
			
		||||
        /// <param name="needle">The needle to find.</param>
 | 
			
		||||
        /// <returns>The part right of the <paramref name="needle" />.</returns>
 | 
			
		||||
        public static ReadOnlySpan<char> RightPart(this ReadOnlySpan<char> haystack, char needle)
 | 
			
		||||
        {
 | 
			
		||||
            var pos = haystack.LastIndexOf(needle);
 | 
			
		||||
            if (pos == -1)
 | 
			
		||||
            {
 | 
			
		||||
                return haystack;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (pos == haystack.Length - 1)
 | 
			
		||||
            {
 | 
			
		||||
                return ReadOnlySpan<char>.Empty;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return haystack[(pos + 1)..];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,5 +14,28 @@ namespace Jellyfin.Extensions.Tests
 | 
			
		||||
        {
 | 
			
		||||
            Assert.Equal(count, str.AsSpan().Count(needle));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Theory]
 | 
			
		||||
        [InlineData("", 'q', "")]
 | 
			
		||||
        [InlineData("Banana split", ' ', "Banana")]
 | 
			
		||||
        [InlineData("Banana split", 'q', "Banana split")]
 | 
			
		||||
        [InlineData("Banana split 2", ' ', "Banana")]
 | 
			
		||||
        public void LeftPart_ValidArgsCharNeedle_Correct(string str, char needle, string expectedResult)
 | 
			
		||||
        {
 | 
			
		||||
            var result = str.AsSpan().LeftPart(needle).ToString();
 | 
			
		||||
            Assert.Equal(expectedResult, result);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Theory]
 | 
			
		||||
        [InlineData("", 'q', "")]
 | 
			
		||||
        [InlineData("Banana split", ' ', "split")]
 | 
			
		||||
        [InlineData("Banana split", 'q', "Banana split")]
 | 
			
		||||
        [InlineData("Banana split.", '.', "")]
 | 
			
		||||
        [InlineData("Banana split 2", ' ', "2")]
 | 
			
		||||
        public void RightPart_ValidArgsCharNeedle_Correct(string str, char needle, string expectedResult)
 | 
			
		||||
        {
 | 
			
		||||
            var result = str.AsSpan().RightPart(needle).ToString();
 | 
			
		||||
            Assert.Equal(expectedResult, result);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user