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.Net.Mime;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -82,9 +83,7 @@ namespace Emby.Dlna.Eventing
|
||||||
if (!string.IsNullOrEmpty(header))
|
if (!string.IsNullOrEmpty(header))
|
||||||
{
|
{
|
||||||
// Starts with SECOND-
|
// Starts with SECOND-
|
||||||
header = header.Split('-')[^1];
|
if (int.TryParse(header.AsSpan().RightPart('-'), NumberStyles.Integer, _usCulture, out var val))
|
||||||
|
|
||||||
if (int.TryParse(header, NumberStyles.Integer, _usCulture, out var val))
|
|
||||||
{
|
{
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
using Jellyfin.XmlTv;
|
using Jellyfin.XmlTv;
|
||||||
using Jellyfin.XmlTv.Entities;
|
using Jellyfin.XmlTv.Entities;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
|
@ -89,11 +90,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||||
return UnzipIfNeeded(path, cacheFile);
|
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
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,7 +36,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly IServerApplicationHost _appHost;
|
private readonly IServerApplicationHost _appHost;
|
||||||
private readonly ISocketFactory _socketFactory;
|
private readonly ISocketFactory _socketFactory;
|
||||||
private readonly INetworkManager _networkManager;
|
|
||||||
private readonly IStreamHelper _streamHelper;
|
private readonly IStreamHelper _streamHelper;
|
||||||
|
|
||||||
private readonly JsonSerializerOptions _jsonOptions;
|
private readonly JsonSerializerOptions _jsonOptions;
|
||||||
|
@ -50,7 +49,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
IServerApplicationHost appHost,
|
IServerApplicationHost appHost,
|
||||||
ISocketFactory socketFactory,
|
ISocketFactory socketFactory,
|
||||||
INetworkManager networkManager,
|
|
||||||
IStreamHelper streamHelper,
|
IStreamHelper streamHelper,
|
||||||
IMemoryCache memoryCache)
|
IMemoryCache memoryCache)
|
||||||
: base(config, logger, fileSystem, memoryCache)
|
: base(config, logger, fileSystem, memoryCache)
|
||||||
|
@ -58,7 +56,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
_httpClientFactory = httpClientFactory;
|
_httpClientFactory = httpClientFactory;
|
||||||
_appHost = appHost;
|
_appHost = appHost;
|
||||||
_socketFactory = socketFactory;
|
_socketFactory = socketFactory;
|
||||||
_networkManager = networkManager;
|
|
||||||
_streamHelper = streamHelper;
|
_streamHelper = streamHelper;
|
||||||
|
|
||||||
_jsonOptions = JsonDefaults.Options;
|
_jsonOptions = JsonDefaults.Options;
|
||||||
|
@ -70,7 +67,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
|
|
||||||
protected override string ChannelIdPrefix => "hdhr_";
|
protected override string ChannelIdPrefix => "hdhr_";
|
||||||
|
|
||||||
private string GetChannelId(TunerHostInfo info, Channels i)
|
private string GetChannelId(Channels i)
|
||||||
=> ChannelIdPrefix + i.GuideNumber;
|
=> ChannelIdPrefix + i.GuideNumber;
|
||||||
|
|
||||||
internal async Task<List<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
|
internal async Task<List<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
|
||||||
|
@ -103,7 +100,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
{
|
{
|
||||||
Name = i.GuideName,
|
Name = i.GuideName,
|
||||||
Number = i.GuideNumber,
|
Number = i.GuideNumber,
|
||||||
Id = GetChannelId(tuner, i),
|
Id = GetChannelId(i),
|
||||||
IsFavorite = i.Favorite,
|
IsFavorite = i.Favorite,
|
||||||
TunerHostId = tuner.Id,
|
TunerHostId = tuner.Id,
|
||||||
IsHD = i.HD,
|
IsHD = i.HD,
|
||||||
|
@ -255,7 +252,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
{
|
{
|
||||||
var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
|
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));
|
var uri = new Uri(GetApiUrl(info));
|
||||||
|
|
||||||
|
@ -264,10 +261,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
// Legacy HdHomeruns are IPv4 only
|
// Legacy HdHomeruns are IPv4 only
|
||||||
var ipInfo = IPAddress.Parse(uri.Host);
|
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 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 isAvailable = await manager.CheckTunerAvailability(ipInfo, i, cancellationToken).ConfigureAwait(false);
|
||||||
var status = isAvailable ? LiveTvTunerStatus.Available : LiveTvTunerStatus.LiveTv;
|
var status = isAvailable ? LiveTvTunerStatus.Available : LiveTvTunerStatus.LiveTv;
|
||||||
tuners.Add(new LiveTvTunerInfo
|
tuners.Add(new LiveTvTunerInfo
|
||||||
|
@ -455,28 +452,28 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
Path = url,
|
Path = url,
|
||||||
Protocol = MediaProtocol.Udp,
|
Protocol = MediaProtocol.Udp,
|
||||||
MediaStreams = new List<MediaStream>
|
MediaStreams = new List<MediaStream>
|
||||||
{
|
{
|
||||||
new MediaStream
|
new MediaStream
|
||||||
{
|
{
|
||||||
Type = MediaStreamType.Video,
|
Type = MediaStreamType.Video,
|
||||||
// Set the index to -1 because we don't know the exact index of the video stream within the container
|
// Set the index to -1 because we don't know the exact index of the video stream within the container
|
||||||
Index = -1,
|
Index = -1,
|
||||||
IsInterlaced = isInterlaced,
|
IsInterlaced = isInterlaced,
|
||||||
Codec = videoCodec,
|
Codec = videoCodec,
|
||||||
Width = width,
|
Width = width,
|
||||||
Height = height,
|
Height = height,
|
||||||
BitRate = videoBitrate,
|
BitRate = videoBitrate,
|
||||||
NalLengthSize = nal
|
NalLengthSize = nal
|
||||||
},
|
},
|
||||||
new MediaStream
|
new MediaStream
|
||||||
{
|
{
|
||||||
Type = MediaStreamType.Audio,
|
Type = MediaStreamType.Audio,
|
||||||
// Set the index to -1 because we don't know the exact index of the audio stream within the container
|
// Set the index to -1 because we don't know the exact index of the audio stream within the container
|
||||||
Index = -1,
|
Index = -1,
|
||||||
Codec = audioCodec,
|
Codec = audioCodec,
|
||||||
BitRate = audioBitrate
|
BitRate = audioBitrate
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RequiresOpening = true,
|
RequiresOpening = true,
|
||||||
RequiresClosing = true,
|
RequiresClosing = true,
|
||||||
BufferMs = 0,
|
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);
|
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
|
try
|
||||||
{
|
{
|
||||||
numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/')[^1]);
|
numberString = Path.GetFileNameWithoutExtension(mediaUrl.AsSpan().RightPart('/')).ToString();
|
||||||
|
|
||||||
if (!IsValidChannelNumber(numberString))
|
if (!IsValidChannelNumber(numberString))
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,7 @@ using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
|
@ -199,7 +200,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
throw new ResourceNotFoundException(nameof(response.Content.Headers.ContentType));
|
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 fullCachePath = GetFullCachePath(urlHash + "." + ext);
|
||||||
|
|
||||||
var fullCacheDirectory = Path.GetDirectoryName(fullCachePath) ?? throw new ResourceNotFoundException($"Provided path ({fullCachePath}) is not valid.");
|
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;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Models.StreamingDtos;
|
using Jellyfin.Api.Models.StreamingDtos;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
@ -81,7 +82,7 @@ namespace Jellyfin.Api.Helpers
|
||||||
throw new ResourceNotFoundException(nameof(httpRequest.Path));
|
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))
|
if (string.IsNullOrEmpty(streamingRequest.AudioCodec))
|
||||||
{
|
{
|
||||||
|
|
|
@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
var size = part.Split('=', 2)[^1];
|
var size = part.Split('=', 2)[^1];
|
||||||
|
|
||||||
int? scale = null;
|
int? scale = null;
|
||||||
if (size.IndexOf("kb", StringComparison.OrdinalIgnoreCase) != -1)
|
if (size.Contains("kb", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
scale = 1024;
|
scale = 1024;
|
||||||
size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase);
|
size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||||
|
@ -139,7 +139,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
var rate = part.Split('=', 2)[^1];
|
var rate = part.Split('=', 2)[^1];
|
||||||
|
|
||||||
int? scale = null;
|
int? scale = null;
|
||||||
if (rate.IndexOf("kbits/s", StringComparison.OrdinalIgnoreCase) != -1)
|
if (rate.Contains("kbits/s", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
scale = 1024;
|
scale = 1024;
|
||||||
rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);
|
rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
@ -331,7 +332,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(text))
|
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;
|
item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1378,7 +1378,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||||
{
|
{
|
||||||
var disc = tags.GetValueOrDefault(tagName);
|
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;
|
return discNum;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||||
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
||||||
{
|
{
|
||||||
writer.WriteLine("WEBVTT");
|
writer.WriteLine("WEBVTT");
|
||||||
writer.WriteLine(string.Empty);
|
writer.WriteLine();
|
||||||
writer.WriteLine("REGION");
|
writer.WriteLine("REGION");
|
||||||
writer.WriteLine("id:subtitle");
|
writer.WriteLine("id:subtitle");
|
||||||
writer.WriteLine("width:80%");
|
writer.WriteLine("width:80%");
|
||||||
writer.WriteLine("lines:3");
|
writer.WriteLine("lines:3");
|
||||||
writer.WriteLine("regionanchor:50%,100%");
|
writer.WriteLine("regionanchor:50%,100%");
|
||||||
writer.WriteLine("viewportanchor:50%,90%");
|
writer.WriteLine("viewportanchor:50%,90%");
|
||||||
writer.WriteLine(string.Empty);
|
writer.WriteLine();
|
||||||
foreach (var trackEvent in info.TrackEvents)
|
foreach (var trackEvent in info.TrackEvents)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Net
|
namespace MediaBrowser.Model.Net
|
||||||
{
|
{
|
||||||
|
@ -221,7 +222,7 @@ namespace MediaBrowser.Model.Net
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle text/html; charset=UTF-8
|
// handle text/html; charset=UTF-8
|
||||||
mimeType = mimeType.Split(';')[0];
|
mimeType = mimeType.AsSpan().LeftPart(';').ToString();
|
||||||
|
|
||||||
if (_extensionLookup.TryGetValue(mimeType, out string? result))
|
if (_extensionLookup.TryGetValue(mimeType, out string? result))
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,7 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Providers;
|
using MediaBrowser.Common.Providers;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
@ -474,7 +475,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(text))
|
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;
|
item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,5 +27,39 @@ namespace Jellyfin.Extensions
|
||||||
|
|
||||||
return count;
|
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));
|
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…
Reference in New Issue
Block a user