time: Make TimeZoneRule blittable and avoid copies (#3361)

* time: Make TimeZoneRule blittable and avoid copies

This drastically reduce overhead of using TimeZoneRule around the
codebase.

Effect on games is unknown

* Add missing Box type

* Ensure we clean the structure still

This doesn't perform any copies

* Address gdkchan's comments

* Simplify Box
This commit is contained in:
Mary 2022-06-24 19:04:57 +02:00 committed by GitHub
parent 232b1012b0
commit 30ee70a9bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 157 additions and 147 deletions

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Common.Memory
{
public class Box<T> where T : unmanaged
{
public T Data;
public Box()
{
Data = new T();
}
}
}

View file

@ -2,7 +2,10 @@ using Ryujinx.Common.Logging;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
using Ryujinx.Memory;
using System; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
namespace Ryujinx.HLE.HOS.Services.Time.StaticService namespace Ryujinx.HLE.HOS.Services.Time.StaticService
@ -100,15 +103,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24); string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24);
ResultCode resultCode = _timeZoneContentManager.LoadTimeZoneRule(out TimeZoneRule rules, locationName); using (WritableRegion region = context.Memory.GetWritableRegion(bufferPosition, Unsafe.SizeOf<TimeZoneRule>()))
// Write TimeZoneRule if success
if (resultCode == ResultCode.Success)
{ {
MemoryHelper.Write(context.Memory, bufferPosition, rules); ref TimeZoneRule rules = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0];
}
return resultCode; return _timeZoneContentManager.LoadTimeZoneRule(ref rules, locationName);
}
} }
[CommandHipc(100)] [CommandHipc(100)]

View file

@ -4,9 +4,12 @@ using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
using Ryujinx.Memory;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.StaticService namespace Ryujinx.HLE.HOS.Services.Time.StaticService
{ {
@ -165,11 +168,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
{ {
result = _timeZoneManager.ParseTimeZoneRuleBinary(out TimeZoneRule timeZoneRule, timeZoneBinaryStream); using (WritableRegion region = context.Memory.GetWritableRegion(timeZoneRuleBufferPosition, Unsafe.SizeOf<TimeZoneRule>()))
if (result == ResultCode.Success)
{ {
MemoryHelper.Write(context.Memory, timeZoneRuleBufferPosition, timeZoneRule); ref TimeZoneRule rule = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0];
result = _timeZoneManager.ParseTimeZoneRuleBinary(ref rule, timeZoneBinaryStream);
} }
} }
@ -199,9 +202,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, bufferPosition); ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(bufferPosition, (int)bufferSize));
ResultCode resultCode = _timeZoneManager.ToCalendarTime(rules, posixTime, out CalendarInfo calendar); ResultCode resultCode = _timeZoneManager.ToCalendarTime(in rules[0], posixTime, out CalendarInfo calendar);
if (resultCode == 0) if (resultCode == 0)
{ {
@ -244,9 +247,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, inBufferPosition); ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(inBufferPosition, (int)inBufferSize));
ResultCode resultCode = _timeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime); ResultCode resultCode = _timeZoneManager.ToPosixTime(in rules[0], calendarTime, out long posixTime);
if (resultCode == ResultCode.Success) if (resultCode == ResultCode.Success)
{ {

View file

@ -1,4 +1,5 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
using System; using System;
@ -38,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
}; };
private const string TimeZoneDefaultRule = ",M4.1.0,M10.5.0"; private static readonly byte[] TimeZoneDefaultRule = Encoding.ASCII.GetBytes(",M4.1.0,M10.5.0");
[StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x10)]
private struct CalendarTimeInternal private struct CalendarTimeInternal
@ -133,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return (t1 - t0) == SecondsPerRepeat; return (t1 - t0) == SecondsPerRepeat;
} }
private static bool TimeTypeEquals(TimeZoneRule outRules, byte aIndex, byte bIndex) private static bool TimeTypeEquals(in TimeZoneRule outRules, byte aIndex, byte bIndex)
{ {
if (aIndex < 0 || aIndex >= outRules.TypeCount || bIndex < 0 || bIndex >= outRules.TypeCount) if (aIndex < 0 || aIndex >= outRules.TypeCount || bIndex < 0 || bIndex >= outRules.TypeCount)
{ {
@ -150,7 +151,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
StringUtils.CompareCStr(outRules.Chars[a.AbbreviationListIndex..], outRules.Chars[b.AbbreviationListIndex..]) == 0; StringUtils.CompareCStr(outRules.Chars[a.AbbreviationListIndex..], outRules.Chars[b.AbbreviationListIndex..]) == 0;
} }
private static int GetQZName(ReadOnlySpan<char> name, int namePosition, char delimiter) private static int GetQZName(ReadOnlySpan<byte> name, int namePosition, char delimiter)
{ {
int i = namePosition; int i = namePosition;
@ -162,13 +163,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return i; return i;
} }
private static int GetTZName(char[] name, int namePosition) private static int GetTZName(ReadOnlySpan<byte> name, int namePosition)
{ {
int i = namePosition; int i = namePosition;
char c; char c;
while ((c = name[i]) != '\0' && !char.IsDigit(c) && c != ',' && c != '-' && c != '+') while ((c = (char)name[i]) != '\0' && !char.IsDigit(c) && c != ',' && c != '-' && c != '+')
{ {
i++; i++;
} }
@ -176,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return i; return i;
} }
private static bool GetNum(char[] name, ref int namePosition, out int num, int min, int max) private static bool GetNum(ReadOnlySpan<byte> name, ref int namePosition, out int num, int min, int max)
{ {
num = 0; num = 0;
@ -185,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return false; return false;
} }
char c = name[namePosition]; char c = (char)name[namePosition];
if (!char.IsDigit(c)) if (!char.IsDigit(c))
{ {
@ -205,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return false; return false;
} }
c = name[namePosition]; c = (char)name[namePosition];
} }
while (char.IsDigit(c)); while (char.IsDigit(c));
@ -217,7 +218,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return true; return true;
} }
private static bool GetSeconds(char[] name, ref int namePosition, out int seconds) private static bool GetSeconds(ReadOnlySpan<byte> name, ref int namePosition, out int seconds)
{ {
seconds = 0; seconds = 0;
@ -266,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return true; return true;
} }
private static bool GetOffset(char[] name, ref int namePosition, ref int offset) private static bool GetOffset(ReadOnlySpan<byte> name, ref int namePosition, ref int offset)
{ {
bool isNegative = false; bool isNegative = false;
@ -304,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return true; return true;
} }
private static bool GetRule(char[] name, ref int namePosition, out Rule rule) private static bool GetRule(ReadOnlySpan<byte> name, ref int namePosition, out Rule rule)
{ {
rule = new Rule(); rule = new Rule();
@ -347,7 +348,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerWekk - 1); isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerWekk - 1);
} }
else if (char.IsDigit(name[namePosition])) else if (char.IsDigit((char)name[namePosition]))
{ {
rule.Type = RuleType.DayOfYear; rule.Type = RuleType.DayOfYear;
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerLYear - 1); isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerLYear - 1);
@ -385,19 +386,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return 0; return 0;
} }
private static bool ParsePosixName(ReadOnlySpan<char> name, out TimeZoneRule outRules, bool lastDitch) private static bool ParsePosixName(ReadOnlySpan<byte> name, ref TimeZoneRule outRules, bool lastDitch)
{ {
outRules = new TimeZoneRule outRules = new TimeZoneRule();
{
Ats = new long[TzMaxTimes],
Types = new byte[TzMaxTimes],
Ttis = new TimeTypeInfo[TzMaxTypes],
Chars = new char[TzCharsArraySize]
};
int stdLen; int stdLen;
ReadOnlySpan<char> stdName = name; ReadOnlySpan<byte> stdName = name;
int namePosition = 0; int namePosition = 0;
int stdOffset = 0; int stdOffset = 0;
@ -428,7 +423,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
} }
else else
{ {
namePosition = GetTZName(name.ToArray(), namePosition); namePosition = GetTZName(name, namePosition);
stdLen = namePosition; stdLen = namePosition;
} }
@ -449,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
int destLen = 0; int destLen = 0;
int dstOffset = 0; int dstOffset = 0;
ReadOnlySpan<char> destName = name.Slice(namePosition); ReadOnlySpan<byte> destName = name.Slice(namePosition);
if (TzCharsArraySize < charCount) if (TzCharsArraySize < charCount)
{ {
@ -476,7 +471,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
else else
{ {
destName = name.Slice(namePosition); destName = name.Slice(namePosition);
namePosition = GetTZName(name.ToArray(), namePosition); namePosition = GetTZName(name, namePosition);
destLen = namePosition; destLen = namePosition;
} }
@ -507,7 +502,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
if (name[namePosition] == '\0') if (name[namePosition] == '\0')
{ {
name = TimeZoneDefaultRule.ToCharArray(); name = TimeZoneDefaultRule;
namePosition = 0; namePosition = 0;
} }
@ -515,7 +510,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
namePosition++; namePosition++;
bool IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule start); bool IsRuleValid = GetRule(name, ref namePosition, out Rule start);
if (!IsRuleValid) if (!IsRuleValid)
{ {
return false; return false;
@ -526,7 +521,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return false; return false;
} }
IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule end); IsRuleValid = GetRule(name, ref namePosition, out Rule end);
if (!IsRuleValid) if (!IsRuleValid)
{ {
return false; return false;
@ -738,7 +733,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
} }
charsPosition += stdLen; charsPosition += stdLen;
outRules.Chars[charsPosition++] = '\0'; outRules.Chars[charsPosition++] = 0;
if (destLen != 0) if (destLen != 0)
{ {
@ -746,7 +741,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
outRules.Chars[charsPosition + i] = destName[i]; outRules.Chars[charsPosition + i] = destName[i];
} }
outRules.Chars[charsPosition + destLen] = '\0'; outRules.Chars[charsPosition + destLen] = 0;
} }
return true; return true;
@ -882,20 +877,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
} }
} }
internal static bool ParsePosixName(string name, out TimeZoneRule outRules) internal static bool ParsePosixName(string name, ref TimeZoneRule outRules)
{ {
return ParsePosixName(name.ToCharArray(), out outRules, false); return ParsePosixName(Encoding.ASCII.GetBytes(name), ref outRules, false);
} }
internal static bool ParseTimeZoneBinary(out TimeZoneRule outRules, Stream inputData) internal static bool ParseTimeZoneBinary(ref TimeZoneRule outRules, Stream inputData)
{ {
outRules = new TimeZoneRule outRules = new TimeZoneRule();
{
Ats = new long[TzMaxTimes],
Types = new byte[TzMaxTimes],
Ttis = new TimeTypeInfo[TzMaxTypes],
Chars = new char[TzCharsArraySize]
};
BinaryReader reader = new BinaryReader(inputData); BinaryReader reader = new BinaryReader(inputData);
@ -1020,10 +1009,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
outRules.Ttis[i] = ttis; outRules.Ttis[i] = ttis;
} }
Encoding.ASCII.GetChars(p[..outRules.CharCount].ToArray()).CopyTo(outRules.Chars.AsSpan()); p[..outRules.CharCount].CopyTo(outRules.Chars);
p = p[outRules.CharCount..]; p = p[outRules.CharCount..];
outRules.Chars[outRules.CharCount] = '\0'; outRules.Chars[outRules.CharCount] = 0;
for (int i = 0; i < outRules.TypeCount; i++) for (int i = 0; i < outRules.TypeCount; i++)
{ {
@ -1077,27 +1066,30 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
char[] tempName = new char[TzNameMax + 1]; byte[] tempName = new byte[TzNameMax + 1];
Array.Copy(workBuffer, position, tempName, 0, nRead); Array.Copy(workBuffer, position, tempName, 0, nRead);
if (nRead > 2 && tempName[0] == '\n' && tempName[nRead - 1] == '\n' && outRules.TypeCount + 2 <= TzMaxTypes) if (nRead > 2 && tempName[0] == '\n' && tempName[nRead - 1] == '\n' && outRules.TypeCount + 2 <= TzMaxTypes)
{ {
tempName[nRead - 1] = '\0'; tempName[nRead - 1] = 0;
char[] name = new char[TzNameMax]; byte[] name = new byte[TzNameMax];
Array.Copy(tempName, 1, name, 0, nRead - 1); Array.Copy(tempName, 1, name, 0, nRead - 1);
if (ParsePosixName(name, out TimeZoneRule tempRules, false)) Box<TimeZoneRule> tempRulesBox = new Box<TimeZoneRule>();
ref TimeZoneRule tempRules = ref tempRulesBox.Data;
if (ParsePosixName(name, ref tempRulesBox.Data, false))
{ {
int abbreviationCount = 0; int abbreviationCount = 0;
charCount = outRules.CharCount; charCount = outRules.CharCount;
Span<char> chars = outRules.Chars; Span<byte> chars = outRules.Chars;
for (int i = 0; i < tempRules.TypeCount; i++) for (int i = 0; i < tempRules.TypeCount; i++)
{ {
ReadOnlySpan<char> tempChars = tempRules.Chars; ReadOnlySpan<byte> tempChars = tempRules.Chars;
ReadOnlySpan<char> tempAbbreviation = tempChars[tempRules.Ttis[i].AbbreviationListIndex..]; ReadOnlySpan<byte> tempAbbreviation = tempChars[tempRules.Ttis[i].AbbreviationListIndex..];
int j; int j;
@ -1175,7 +1167,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
for (int i = 1; i < outRules.TimeCount; i++) for (int i = 1; i < outRules.TimeCount; i++)
{ {
if (TimeTypeEquals(outRules, outRules.Types[i], outRules.Types[0]) && DifferByRepeat(outRules.Ats[i], outRules.Ats[0])) if (TimeTypeEquals(in outRules, outRules.Types[i], outRules.Types[0]) && DifferByRepeat(outRules.Ats[i], outRules.Ats[0]))
{ {
outRules.GoBack = true; outRules.GoBack = true;
break; break;
@ -1184,7 +1176,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
for (int i = outRules.TimeCount - 2; i >= 0; i--) for (int i = outRules.TimeCount - 2; i >= 0; i--)
{ {
if (TimeTypeEquals(outRules, outRules.Types[outRules.TimeCount - 1], outRules.Types[i]) && DifferByRepeat(outRules.Ats[outRules.TimeCount - 1], outRules.Ats[i])) if (TimeTypeEquals(in outRules, outRules.Types[outRules.TimeCount - 1], outRules.Types[i]) && DifferByRepeat(outRules.Ats[outRules.TimeCount - 1], outRules.Ats[i]))
{ {
outRules.GoAhead = true; outRules.GoAhead = true;
break; break;
@ -1259,10 +1251,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
long remainingSeconds = time % SecondsPerDay; long remainingSeconds = time % SecondsPerDay;
calendarTime = new CalendarTimeInternal(); calendarTime = new CalendarTimeInternal();
calendarAdditionalInfo = new CalendarAdditionalInfo() calendarAdditionalInfo = new CalendarAdditionalInfo();
{
TimezoneName = new char[8]
};
while (timeDays < 0 || timeDays >= YearLengths[IsLeap((int)year)]) while (timeDays < 0 || timeDays >= YearLengths[IsLeap((int)year)])
{ {
@ -1353,13 +1342,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return 0; return 0;
} }
private static ResultCode ToCalendarTimeInternal(TimeZoneRule rules, long time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo) private static ResultCode ToCalendarTimeInternal(in TimeZoneRule rules, long time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo)
{ {
calendarTime = new CalendarTimeInternal(); calendarTime = new CalendarTimeInternal();
calendarAdditionalInfo = new CalendarAdditionalInfo() calendarAdditionalInfo = new CalendarAdditionalInfo();
{
TimezoneName = new char[8]
};
ResultCode result; ResultCode result;
@ -1398,7 +1384,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return ResultCode.TimeNotFound; return ResultCode.TimeNotFound;
} }
result = ToCalendarTimeInternal(rules, newTime, out calendarTime, out calendarAdditionalInfo); result = ToCalendarTimeInternal(in rules, newTime, out calendarTime, out calendarAdditionalInfo);
if (result != 0) if (result != 0)
{ {
return result; return result;
@ -1450,17 +1436,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
calendarAdditionalInfo.IsDaySavingTime = rules.Ttis[ttiIndex].IsDaySavingTime; calendarAdditionalInfo.IsDaySavingTime = rules.Ttis[ttiIndex].IsDaySavingTime;
ReadOnlySpan<char> timeZoneAbbreviation = rules.Chars.AsSpan()[rules.Ttis[ttiIndex].AbbreviationListIndex..]; ReadOnlySpan<byte> timeZoneAbbreviation = rules.Chars[rules.Ttis[ttiIndex].AbbreviationListIndex..];
int timeZoneSize = Math.Min(StringUtils.LengthCstr(timeZoneAbbreviation), 8); int timeZoneSize = Math.Min(StringUtils.LengthCstr(timeZoneAbbreviation), 8);
timeZoneAbbreviation[..timeZoneSize].CopyTo(calendarAdditionalInfo.TimezoneName.AsSpan()); timeZoneAbbreviation[..timeZoneSize].CopyTo(calendarAdditionalInfo.TimezoneName.ToSpan());
} }
return result; return result;
} }
private static ResultCode ToPosixTimeInternal(TimeZoneRule rules, CalendarTimeInternal calendarTime, out long posixTime) private static ResultCode ToPosixTimeInternal(in TimeZoneRule rules, CalendarTimeInternal calendarTime, out long posixTime)
{ {
posixTime = 0; posixTime = 0;
@ -1604,7 +1590,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
int direction; int direction;
ResultCode result = ToCalendarTimeInternal(rules, pivot, out CalendarTimeInternal candidateCalendarTime, out _); ResultCode result = ToCalendarTimeInternal(in rules, pivot, out CalendarTimeInternal candidateCalendarTime, out _);
if (result != 0) if (result != 0)
{ {
if (pivot > 0) if (pivot > 0)
@ -1675,9 +1661,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return ResultCode.Success; return ResultCode.Success;
} }
internal static ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar) internal static ResultCode ToCalendarTime(in TimeZoneRule rules, long time, out CalendarInfo calendar)
{ {
ResultCode result = ToCalendarTimeInternal(rules, time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo); ResultCode result = ToCalendarTimeInternal(in rules, time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo);
calendar = new CalendarInfo() calendar = new CalendarInfo()
{ {
@ -1697,7 +1683,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return result; return result;
} }
internal static ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime) internal static ResultCode ToPosixTime(in TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
{ {
CalendarTimeInternal calendarTimeInternal = new CalendarTimeInternal() CalendarTimeInternal calendarTimeInternal = new CalendarTimeInternal()
{ {
@ -1710,7 +1696,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
Second = calendarTime.Second Second = calendarTime.Second
}; };
return ToPosixTimeInternal(rules, calendarTimeInternal, out posixTime); return ToPosixTimeInternal(in rules, calendarTimeInternal, out posixTime);
} }
} }
} }

View file

@ -16,7 +16,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule; using TimeZoneRuleBox = Ryujinx.Common.Memory.Box<Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule>;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
@ -149,7 +149,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
continue; continue;
} }
TimeZone.ParseTimeZoneBinary(out TimeZoneRule tzRule, tzif.Get.AsStream()); TimeZoneRuleBox tzRuleBox = new TimeZoneRuleBox();
ref TimeZoneRule tzRule = ref tzRuleBox.Data;
TimeZone.ParseTimeZoneBinary(ref tzRule, tzif.Get.AsStream());
TimeTypeInfo ttInfo; TimeTypeInfo ttInfo;
if (tzRule.TimeCount > 0) // Find the current transition period if (tzRule.TimeCount > 0) // Find the current transition period
@ -174,10 +178,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
continue; continue;
} }
var abbrStart = tzRule.Chars.AsSpan(ttInfo.AbbreviationListIndex); var abbrStart = tzRule.Chars[ttInfo.AbbreviationListIndex..];
int abbrEnd = abbrStart.IndexOf('\0'); int abbrEnd = abbrStart.IndexOf((byte)0);
outList.Add((ttInfo.GmtOffset, locName, abbrStart.Slice(0, abbrEnd).ToString())); outList.Add((ttInfo.GmtOffset, locName, abbrStart[..abbrEnd].ToString()));
} }
} }
@ -276,15 +280,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return (ResultCode)result.Value; return (ResultCode)result.Value;
} }
internal ResultCode LoadTimeZoneRule(out TimeZoneRule outRules, string locationName) internal ResultCode LoadTimeZoneRule(ref TimeZoneRule rules, string locationName)
{ {
outRules = new TimeZoneRule rules = default;
{
Ats = new long[TzMaxTimes],
Types = new byte[TzMaxTimes],
Ttis = new TimeTypeInfo[TzMaxTypes],
Chars = new char[TzCharsArraySize]
};
if (!HasTimeZoneBinaryTitle()) if (!HasTimeZoneBinaryTitle())
{ {
@ -295,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
if (result == ResultCode.Success) if (result == ResultCode.Success)
{ {
result = Manager.ParseTimeZoneRuleBinary(out outRules, timeZoneBinaryStream); result = Manager.ParseTimeZoneRuleBinary(ref rules, timeZoneBinaryStream);
ncaFile.Dispose(); ncaFile.Dispose();
} }

View file

@ -1,14 +1,14 @@
using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
using System.IO; using System.IO;
using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
class TimeZoneManager class TimeZoneManager
{ {
private bool _isInitialized; private bool _isInitialized;
private TimeZoneRule _myRules; private Box<TimeZoneRule> _myRules;
private string _deviceLocationName; private string _deviceLocationName;
private UInt128 _timeZoneRuleVersion; private UInt128 _timeZoneRuleVersion;
private uint _totalLocationNameCount; private uint _totalLocationNameCount;
@ -21,15 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
_deviceLocationName = "UTC"; _deviceLocationName = "UTC";
_timeZoneRuleVersion = new UInt128(); _timeZoneRuleVersion = new UInt128();
_lock = new object(); _lock = new object();
_myRules = new Box<TimeZoneRule>();
// Empty rules
_myRules = new TimeZoneRule
{
Ats = new long[TzMaxTimes],
Types = new byte[TzMaxTimes],
Ttis = new TimeTypeInfo[TzMaxTypes],
Chars = new char[TzCharsArraySize]
};
_timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom(); _timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom();
} }
@ -78,7 +70,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
lock (_lock) lock (_lock)
{ {
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out TimeZoneRule rules, timeZoneBinaryStream); Box<TimeZoneRule> rules = new Box<TimeZoneRule>();
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref rules.Data, timeZoneBinaryStream);
if (timeZoneConversionSuccess) if (timeZoneConversionSuccess)
{ {
@ -154,13 +148,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return result; return result;
} }
public ResultCode ParseTimeZoneRuleBinary(out TimeZoneRule outRules, Stream timeZoneBinaryStream) public ResultCode ParseTimeZoneRuleBinary(ref TimeZoneRule outRules, Stream timeZoneBinaryStream)
{ {
ResultCode result = ResultCode.Success; ResultCode result = ResultCode.Success;
lock (_lock) lock (_lock)
{ {
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out outRules, timeZoneBinaryStream); bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref outRules, timeZoneBinaryStream);
if (!timeZoneConversionSuccess) if (!timeZoneConversionSuccess)
{ {
@ -208,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
if (_isInitialized) if (_isInitialized)
{ {
result = ToCalendarTime(_myRules, time, out calendar); result = ToCalendarTime(in _myRules.Data, time, out calendar);
} }
else else
{ {
@ -220,13 +214,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return result; return result;
} }
public ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar) public ResultCode ToCalendarTime(in TimeZoneRule rules, long time, out CalendarInfo calendar)
{ {
ResultCode result; ResultCode result;
lock (_lock) lock (_lock)
{ {
result = TimeZone.ToCalendarTime(rules, time, out calendar); result = TimeZone.ToCalendarTime(in rules, time, out calendar);
} }
return result; return result;
@ -240,7 +234,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
if (_isInitialized) if (_isInitialized)
{ {
result = ToPosixTime(_myRules, calendarTime, out posixTime); result = ToPosixTime(in _myRules.Data, calendarTime, out posixTime);
} }
else else
{ {
@ -252,13 +246,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return result; return result;
} }
public ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime) public ResultCode ToPosixTime(in TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
{ {
ResultCode result; ResultCode result;
lock (_lock) lock (_lock)
{ {
result = TimeZone.ToPosixTime(rules, calendarTime, out posixTime); result = TimeZone.ToPosixTime(in rules, calendarTime, out posixTime);
} }
return result; return result;

View file

@ -1,4 +1,5 @@
using System.Runtime.InteropServices; using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
@ -8,14 +9,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
public uint DayOfWeek; public uint DayOfWeek;
public uint DayOfYear; public uint DayOfYear;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public Array8<byte> TimezoneName;
public char[] TimezoneName;
[MarshalAs(UnmanagedType.I1)] [MarshalAs(UnmanagedType.I1)]
public bool IsDaySavingTime; public bool IsDaySavingTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public Array3<byte> Padding;
public char[] Padding;
public int GmtOffset; public int GmtOffset;
} }

View file

@ -1,17 +1,19 @@
using System.Runtime.InteropServices; using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)] [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 4)]
struct TimeTypeInfo struct TimeTypeInfo
{ {
public const int Size = 0x10;
public int GmtOffset; public int GmtOffset;
[MarshalAs(UnmanagedType.I1)] [MarshalAs(UnmanagedType.I1)]
public bool IsDaySavingTime; public bool IsDaySavingTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public Array3<byte> Padding1;
public char[] Padding1;
public int AbbreviationListIndex; public int AbbreviationListIndex;
@ -21,7 +23,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
[MarshalAs(UnmanagedType.I1)] [MarshalAs(UnmanagedType.I1)]
public bool IsGMT; public bool IsGMT;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public ushort Padding2;
public char[] Padding2;
} }
} }

View file

@ -1,16 +1,18 @@
using System.Runtime.InteropServices; using Ryujinx.Common.Utilities;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{ {
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x4000, CharSet = CharSet.Ansi)] [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x4000, CharSet = CharSet.Ansi)]
struct TimeZoneRule struct TimeZoneRule
{ {
public const int TzMaxTypes = 128; public const int TzMaxTypes = 128;
public const int TzMaxChars = 50; public const int TzMaxChars = 50;
public const int TzMaxLeaps = 50; public const int TzMaxLeaps = 50;
public const int TzMaxTimes = 1000; public const int TzMaxTimes = 1000;
public const int TzNameMax = 255; public const int TzNameMax = 255;
public const int TzCharsArraySize = 2 * (TzNameMax + 1); public const int TzCharsArraySize = 2 * (TzNameMax + 1);
public int TimeCount; public int TimeCount;
public int TypeCount; public int TypeCount;
@ -22,17 +24,32 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
[MarshalAs(UnmanagedType.I1)] [MarshalAs(UnmanagedType.I1)]
public bool GoAhead; public bool GoAhead;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTimes)] [StructLayout(LayoutKind.Sequential, Size = sizeof(long) * TzMaxTimes)]
public long[] Ats; private struct AtsStorageStruct {}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTimes)] private AtsStorageStruct _ats;
public byte[] Types;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTypes)] public Span<long> Ats => SpanHelpers.AsSpan<AtsStorageStruct, long>(ref _ats);
public TimeTypeInfo[] Ttis;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzCharsArraySize)] [StructLayout(LayoutKind.Sequential, Size = sizeof(byte) * TzMaxTimes)]
public char[] Chars; private struct TypesStorageStruct {}
private TypesStorageStruct _types;
public Span<byte> Types => SpanHelpers.AsByteSpan<TypesStorageStruct>(ref _types);
[StructLayout(LayoutKind.Sequential, Size = TimeTypeInfo.Size * TzMaxTimes)]
private struct TimeTypeInfoStorageStruct { }
private TimeTypeInfoStorageStruct _ttis;
public Span<TimeTypeInfo> Ttis => SpanHelpers.AsSpan<TimeTypeInfoStorageStruct, TimeTypeInfo>(ref _ttis);
[StructLayout(LayoutKind.Sequential, Size = sizeof(byte) * TzCharsArraySize)]
private struct CharsStorageStruct {}
private CharsStorageStruct _chars;
public Span<byte> Chars => SpanHelpers.AsByteSpan(ref _chars);
public int DefaultType; public int DefaultType;
} }

View file

@ -128,7 +128,7 @@ namespace Ryujinx.HLE.Utilities
} }
} }
public static int CompareCStr(ReadOnlySpan<char> s1, ReadOnlySpan<char> s2) public static int CompareCStr(ReadOnlySpan<byte> s1, ReadOnlySpan<byte> s2)
{ {
int s1Index = 0; int s1Index = 0;
int s2Index = 0; int s2Index = 0;
@ -142,11 +142,11 @@ namespace Ryujinx.HLE.Utilities
return s2[s2Index] - s1[s1Index]; return s2[s2Index] - s1[s1Index];
} }
public static int LengthCstr(ReadOnlySpan<char> s) public static int LengthCstr(ReadOnlySpan<byte> s)
{ {
int i = 0; int i = 0;
while (s[i] != '\0') while (s[i] != 0)
{ {
i++; i++;
} }