mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-02-03 19:22:59 +00:00
Reduce some allocations with the magic of spans etc.
This commit is contained in:
@@ -1007,15 +1007,12 @@ namespace Emby.Server.Implementations.Data
|
||||
return;
|
||||
}
|
||||
|
||||
var parts = value.Split('|', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var part in parts)
|
||||
foreach (var part in value.SpanSplit('|'))
|
||||
{
|
||||
var idParts = part.Split('=');
|
||||
|
||||
if (idParts.Length == 2)
|
||||
var providerDelimiterIndex = part.IndexOf('=');
|
||||
if (providerDelimiterIndex != -1 && providerDelimiterIndex == part.LastIndexOf('='))
|
||||
{
|
||||
item.SetProviderId(idParts[0], idParts[1]);
|
||||
item.SetProviderId(part.Slice(0, providerDelimiterIndex), part.Slice(providerDelimiterIndex + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1057,9 +1054,8 @@ namespace Emby.Server.Implementations.Data
|
||||
return;
|
||||
}
|
||||
|
||||
var parts = value.Split('|' , StringSplitOptions.RemoveEmptyEntries);
|
||||
var list = new List<ItemImageInfo>();
|
||||
foreach (var part in parts)
|
||||
foreach (var part in value.SpanSplit('|'))
|
||||
{
|
||||
var image = ItemImageInfoFromValueString(part);
|
||||
|
||||
@@ -1094,41 +1090,89 @@ namespace Emby.Server.Implementations.Data
|
||||
.Append(hash.Replace('*', '/').Replace('|', '\\'));
|
||||
}
|
||||
|
||||
public ItemImageInfo ItemImageInfoFromValueString(string value)
|
||||
private ItemImageInfo ItemImageInfoFromValueString(ReadOnlySpan<char> value)
|
||||
{
|
||||
var parts = value.Split('*', StringSplitOptions.None);
|
||||
|
||||
if (parts.Length < 3)
|
||||
var nextSegment = value.IndexOf('*');
|
||||
if (nextSegment == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var image = new ItemImageInfo();
|
||||
ReadOnlySpan<char> path = value[..nextSegment];
|
||||
value = value[(nextSegment + 1)..];
|
||||
nextSegment = value.IndexOf('*');
|
||||
if (nextSegment == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
image.Path = RestorePath(parts[0]);
|
||||
ReadOnlySpan<char> dateModified = value[..nextSegment];
|
||||
value = value[(nextSegment + 1)..];
|
||||
nextSegment = value.IndexOf('*');
|
||||
if (nextSegment == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (long.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks))
|
||||
ReadOnlySpan<char> imageType = value[..nextSegment];
|
||||
|
||||
var image = new ItemImageInfo
|
||||
{
|
||||
Path = RestorePath(path.ToString())
|
||||
};
|
||||
|
||||
if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks))
|
||||
{
|
||||
image.DateModified = new DateTime(ticks, DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
if (Enum.TryParse(parts[2], true, out ImageType type))
|
||||
if (Enum.TryParse(imageType.ToString(), true, out ImageType type))
|
||||
{
|
||||
image.Type = type;
|
||||
}
|
||||
|
||||
if (parts.Length >= 5)
|
||||
// Optional parameters: width*height*blurhash
|
||||
if (nextSegment + 1 < value.Length - 1)
|
||||
{
|
||||
if (int.TryParse(parts[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out var width)
|
||||
&& int.TryParse(parts[4], NumberStyles.Integer, CultureInfo.InvariantCulture, out var height))
|
||||
value = value[(nextSegment + 1)..];
|
||||
nextSegment = value.IndexOf('*');
|
||||
ReadOnlySpan<char> widthSpan = value[..nextSegment];
|
||||
|
||||
value = value[(nextSegment + 1)..];
|
||||
nextSegment = value.IndexOf('*');
|
||||
if (nextSegment == -1)
|
||||
{
|
||||
return image;
|
||||
}
|
||||
|
||||
ReadOnlySpan<char> heightSpan = value[..nextSegment];
|
||||
|
||||
if (int.TryParse(widthSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var width)
|
||||
&& int.TryParse(heightSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var height))
|
||||
{
|
||||
image.Width = width;
|
||||
image.Height = height;
|
||||
}
|
||||
|
||||
if (parts.Length >= 6)
|
||||
nextSegment += 1;
|
||||
if (nextSegment < value.Length - 1)
|
||||
{
|
||||
image.BlurHash = parts[5].Replace('/', '*').Replace('\\', '|');
|
||||
value = value[nextSegment..];
|
||||
var length = value.Length;
|
||||
|
||||
Span<char> blurHashSpan = stackalloc char[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
var c = value[i];
|
||||
blurHashSpan[i] = c switch
|
||||
{
|
||||
'/' => '*',
|
||||
'\\' => '|',
|
||||
_ => c
|
||||
};
|
||||
}
|
||||
|
||||
image.BlurHash = new string(blurHashSpan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2118,27 +2162,6 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
private readonly ItemFields[] _allFields = Enum.GetValues<ItemFields>();
|
||||
|
||||
private string[] GetColumnNamesFromField(ItemFields field)
|
||||
{
|
||||
switch (field)
|
||||
{
|
||||
case ItemFields.Settings:
|
||||
return new[] { "IsLocked", "PreferredMetadataCountryCode", "PreferredMetadataLanguage", "LockedFields" };
|
||||
case ItemFields.ServiceName:
|
||||
return new[] { "ExternalServiceId" };
|
||||
case ItemFields.SortName:
|
||||
return new[] { "ForcedSortName" };
|
||||
case ItemFields.Taglines:
|
||||
return new[] { "Tagline" };
|
||||
case ItemFields.Tags:
|
||||
return new[] { "Tags" };
|
||||
case ItemFields.IsHD:
|
||||
return Array.Empty<string>();
|
||||
default:
|
||||
return new[] { field.ToString() };
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasField(InternalItemsQuery query, ItemFields name)
|
||||
{
|
||||
switch (name)
|
||||
@@ -2327,9 +2350,32 @@ namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
if (!HasField(query, field))
|
||||
{
|
||||
foreach (var fieldToRemove in GetColumnNamesFromField(field))
|
||||
switch (field)
|
||||
{
|
||||
list.Remove(fieldToRemove);
|
||||
case ItemFields.Settings:
|
||||
list.Remove("IsLocked");
|
||||
list.Remove("PreferredMetadataCountryCode");
|
||||
list.Remove("PreferredMetadataLanguage");
|
||||
list.Remove("LockedFields");
|
||||
break;
|
||||
case ItemFields.ServiceName:
|
||||
list.Remove("ExternalServiceId");
|
||||
break;
|
||||
case ItemFields.SortName:
|
||||
list.Remove("ForcedSortName");
|
||||
break;
|
||||
case ItemFields.Taglines:
|
||||
list.Remove("Tagline");
|
||||
break;
|
||||
case ItemFields.Tags:
|
||||
list.Remove("Tags");
|
||||
break;
|
||||
case ItemFields.IsHD:
|
||||
// do nothing
|
||||
break;
|
||||
default:
|
||||
list.Remove(field.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2575,10 +2621,21 @@ namespace Emby.Server.Implementations.Data
|
||||
query.Limit = query.Limit.Value + 4;
|
||||
}
|
||||
|
||||
var commandText = "select "
|
||||
+ string.Join(',', GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" }))
|
||||
+ GetFromText()
|
||||
+ GetJoinUserDataText(query);
|
||||
var commandText = "select ";
|
||||
if (EnableGroupByPresentationUniqueKey(query))
|
||||
{
|
||||
commandText += "count (distinct PresentationUniqueKey)";
|
||||
}
|
||||
else if (query.GroupBySeriesPresentationUniqueKey)
|
||||
{
|
||||
commandText += "count (distinct SeriesPresentationUniqueKey)";
|
||||
}
|
||||
else
|
||||
{
|
||||
commandText += "count (guid)";
|
||||
}
|
||||
|
||||
commandText += GetFromText() + GetJoinUserDataText(query);
|
||||
|
||||
var whereClauses = GetWhereClauses(query, null);
|
||||
if (whereClauses.Count != 0)
|
||||
|
||||
@@ -590,18 +590,9 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
public Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken)
|
||||
{
|
||||
var info = _openStreams.Values.FirstOrDefault(i =>
|
||||
{
|
||||
var liveStream = i as ILiveStream;
|
||||
if (liveStream != null)
|
||||
{
|
||||
return string.Equals(liveStream.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
var info = _openStreams.FirstOrDefault(i => i.Value != null && string.Equals(i.Value.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return Task.FromResult(info as IDirectStreamProvider);
|
||||
return Task.FromResult(info.Value as IDirectStreamProvider);
|
||||
}
|
||||
|
||||
public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
|
||||
|
||||
@@ -801,22 +801,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
public ActiveRecordingInfo GetActiveRecordingInfo(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
if (string.IsNullOrWhiteSpace(path) || _activeRecordings.IsEmpty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var recording in _activeRecordings.Values)
|
||||
foreach (var (_, recordingInfo) in _activeRecordings)
|
||||
{
|
||||
if (string.Equals(recording.Path, path, StringComparison.Ordinal) && !recording.CancellationTokenSource.IsCancellationRequested)
|
||||
if (string.Equals(recordingInfo.Path, path, StringComparison.Ordinal) && !recordingInfo.CancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
var timer = recording.Timer;
|
||||
var timer = recordingInfo.Timer;
|
||||
if (timer.Status != RecordingStatus.InProgress)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return recording;
|
||||
return recordingInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1621,9 +1621,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
}
|
||||
|
||||
return _activeRecordings
|
||||
.Values
|
||||
.ToList()
|
||||
.Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
||||
.Any(i => string.Equals(i.Value.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Value.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private IRecorder GetRecorder(MediaSourceInfo mediaSource)
|
||||
|
||||
@@ -315,10 +315,9 @@ namespace Emby.Server.Implementations.Localization
|
||||
}
|
||||
|
||||
const string Prefix = "Core";
|
||||
var key = Prefix + culture;
|
||||
|
||||
return _dictionaries.GetOrAdd(
|
||||
key,
|
||||
culture,
|
||||
f => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult());
|
||||
}
|
||||
|
||||
|
||||
@@ -257,20 +257,17 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||
}
|
||||
|
||||
// Expire stale connection requests
|
||||
var code = string.Empty;
|
||||
var values = _currentRequests.Values.ToList();
|
||||
|
||||
for (int i = 0; i < values.Count; i++)
|
||||
foreach (var (_, currentRequest) in _currentRequests)
|
||||
{
|
||||
var added = values[i].DateAdded ?? DateTime.UnixEpoch;
|
||||
if (DateTime.UtcNow > added.AddMinutes(Timeout) || expireAll)
|
||||
var added = currentRequest.DateAdded ?? DateTime.UnixEpoch;
|
||||
if (expireAll || DateTime.UtcNow > added.AddMinutes(Timeout))
|
||||
{
|
||||
code = values[i].Code;
|
||||
_logger.LogDebug("Removing expired request {code}", code);
|
||||
var code = currentRequest.Code;
|
||||
_logger.LogDebug("Removing expired request {Code}", code);
|
||||
|
||||
if (!_currentRequests.TryRemove(code, out _))
|
||||
{
|
||||
_logger.LogWarning("Request {code} already expired", code);
|
||||
_logger.LogWarning("Request {Code} already expired", code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,14 +269,17 @@ namespace Emby.Server.Implementations.SyncPlay
|
||||
var user = _userManager.GetUserById(session.UserId);
|
||||
List<GroupInfoDto> list = new List<GroupInfoDto>();
|
||||
|
||||
foreach (var group in _groups.Values)
|
||||
lock (_groupsLock)
|
||||
{
|
||||
// Locking required as group is not thread-safe.
|
||||
lock (group)
|
||||
foreach (var (_, group) in _groups)
|
||||
{
|
||||
if (group.HasAccessToPlayQueue(user))
|
||||
// Locking required as group is not thread-safe.
|
||||
lock (group)
|
||||
{
|
||||
list.Add(group.GetInfo());
|
||||
if (group.HasAccessToPlayQueue(user))
|
||||
{
|
||||
list.Add(group.GetInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user