From 30ee70a9bc9529772198a51b1b9a605932d2ea96 Mon Sep 17 00:00:00 2001 From: Mary Date: Fri, 24 Jun 2022 19:04:57 +0200 Subject: [PATCH] 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 --- Ryujinx.Common/Memory/Box.cs | 12 ++ .../StaticService/ITimeZoneServiceForGlue.cs | 14 +- .../StaticService/ITimeZoneServiceForPsc.cs | 19 +-- .../HOS/Services/Time/TimeZone/TimeZone.cs | 122 ++++++++---------- .../Time/TimeZone/TimeZoneContentManager.cs | 26 ++-- .../Services/Time/TimeZone/TimeZoneManager.cs | 36 +++--- .../TimeZone/Types/CalendarAdditionalInfo.cs | 9 +- .../Time/TimeZone/Types/TimeTypeInfo.cs | 13 +- .../Time/TimeZone/Types/TimeZoneRule.cs | 47 ++++--- Ryujinx.HLE/Utilities/StringUtils.cs | 6 +- 10 files changed, 157 insertions(+), 147 deletions(-) create mode 100644 Ryujinx.Common/Memory/Box.cs diff --git a/Ryujinx.Common/Memory/Box.cs b/Ryujinx.Common/Memory/Box.cs new file mode 100644 index 0000000000..743cc31dc6 --- /dev/null +++ b/Ryujinx.Common/Memory/Box.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Common.Memory +{ + public class Box where T : unmanaged + { + public T Data; + + public Box() + { + Data = new T(); + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs index 98d4b617ca..265cec5dde 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs @@ -2,7 +2,10 @@ using Ryujinx.Common.Logging; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.HLE.Utilities; +using Ryujinx.Memory; using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; 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); - ResultCode resultCode = _timeZoneContentManager.LoadTimeZoneRule(out TimeZoneRule rules, locationName); - - // Write TimeZoneRule if success - if (resultCode == ResultCode.Success) + using (WritableRegion region = context.Memory.GetWritableRegion(bufferPosition, Unsafe.SizeOf())) { - MemoryHelper.Write(context.Memory, bufferPosition, rules); - } + ref TimeZoneRule rules = ref MemoryMarshal.Cast(region.Memory.Span)[0]; - return resultCode; + return _timeZoneContentManager.LoadTimeZoneRule(ref rules, locationName); + } } [CommandHipc(100)] diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs index 202099b052..24032999b6 100644 --- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs +++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs @@ -4,9 +4,12 @@ using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.HLE.Utilities; +using Ryujinx.Memory; using System; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Time.StaticService { @@ -165,11 +168,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp)) { - result = _timeZoneManager.ParseTimeZoneRuleBinary(out TimeZoneRule timeZoneRule, timeZoneBinaryStream); - - if (result == ResultCode.Success) + using (WritableRegion region = context.Memory.GetWritableRegion(timeZoneRuleBufferPosition, Unsafe.SizeOf())) { - MemoryHelper.Write(context.Memory, timeZoneRuleBufferPosition, timeZoneRule); + ref TimeZoneRule rule = ref MemoryMarshal.Cast(region.Memory.Span)[0]; + + result = _timeZoneManager.ParseTimeZoneRuleBinary(ref rule, timeZoneBinaryStream); } } @@ -199,9 +202,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService throw new InvalidOperationException(); } - TimeZoneRule rules = MemoryHelper.Read(context.Memory, bufferPosition); + ReadOnlySpan rules = MemoryMarshal.Cast(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) { @@ -244,9 +247,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService throw new InvalidOperationException(); } - TimeZoneRule rules = MemoryHelper.Read(context.Memory, inBufferPosition); + ReadOnlySpan rules = MemoryMarshal.Cast(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) { diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs index e1cdc16b79..6cd16f75dd 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; using Ryujinx.HLE.Utilities; 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 } }; - 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)] private struct CalendarTimeInternal @@ -133,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone 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) { @@ -150,7 +151,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone StringUtils.CompareCStr(outRules.Chars[a.AbbreviationListIndex..], outRules.Chars[b.AbbreviationListIndex..]) == 0; } - private static int GetQZName(ReadOnlySpan name, int namePosition, char delimiter) + private static int GetQZName(ReadOnlySpan name, int namePosition, char delimiter) { int i = namePosition; @@ -162,13 +163,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return i; } - private static int GetTZName(char[] name, int namePosition) + private static int GetTZName(ReadOnlySpan name, int namePosition) { int i = namePosition; 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++; } @@ -176,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return i; } - private static bool GetNum(char[] name, ref int namePosition, out int num, int min, int max) + private static bool GetNum(ReadOnlySpan name, ref int namePosition, out int num, int min, int max) { num = 0; @@ -185,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return false; } - char c = name[namePosition]; + char c = (char)name[namePosition]; if (!char.IsDigit(c)) { @@ -205,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return false; } - c = name[namePosition]; + c = (char)name[namePosition]; } while (char.IsDigit(c)); @@ -217,7 +218,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return true; } - private static bool GetSeconds(char[] name, ref int namePosition, out int seconds) + private static bool GetSeconds(ReadOnlySpan name, ref int namePosition, out int seconds) { seconds = 0; @@ -266,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return true; } - private static bool GetOffset(char[] name, ref int namePosition, ref int offset) + private static bool GetOffset(ReadOnlySpan name, ref int namePosition, ref int offset) { bool isNegative = false; @@ -304,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return true; } - private static bool GetRule(char[] name, ref int namePosition, out Rule rule) + private static bool GetRule(ReadOnlySpan name, ref int namePosition, out Rule 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); } - else if (char.IsDigit(name[namePosition])) + else if (char.IsDigit((char)name[namePosition])) { rule.Type = RuleType.DayOfYear; isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerLYear - 1); @@ -385,19 +386,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return 0; } - private static bool ParsePosixName(ReadOnlySpan name, out TimeZoneRule outRules, bool lastDitch) + private static bool ParsePosixName(ReadOnlySpan name, ref TimeZoneRule outRules, bool lastDitch) { - outRules = new TimeZoneRule - { - Ats = new long[TzMaxTimes], - Types = new byte[TzMaxTimes], - Ttis = new TimeTypeInfo[TzMaxTypes], - Chars = new char[TzCharsArraySize] - }; + outRules = new TimeZoneRule(); int stdLen; - ReadOnlySpan stdName = name; + ReadOnlySpan stdName = name; int namePosition = 0; int stdOffset = 0; @@ -428,7 +423,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone } else { - namePosition = GetTZName(name.ToArray(), namePosition); + namePosition = GetTZName(name, namePosition); stdLen = namePosition; } @@ -449,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone int destLen = 0; int dstOffset = 0; - ReadOnlySpan destName = name.Slice(namePosition); + ReadOnlySpan destName = name.Slice(namePosition); if (TzCharsArraySize < charCount) { @@ -476,7 +471,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone else { destName = name.Slice(namePosition); - namePosition = GetTZName(name.ToArray(), namePosition); + namePosition = GetTZName(name, namePosition); destLen = namePosition; } @@ -507,7 +502,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone if (name[namePosition] == '\0') { - name = TimeZoneDefaultRule.ToCharArray(); + name = TimeZoneDefaultRule; namePosition = 0; } @@ -515,7 +510,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { namePosition++; - bool IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule start); + bool IsRuleValid = GetRule(name, ref namePosition, out Rule start); if (!IsRuleValid) { return false; @@ -526,7 +521,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return false; } - IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule end); + IsRuleValid = GetRule(name, ref namePosition, out Rule end); if (!IsRuleValid) { return false; @@ -738,7 +733,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone } charsPosition += stdLen; - outRules.Chars[charsPosition++] = '\0'; + outRules.Chars[charsPosition++] = 0; if (destLen != 0) { @@ -746,7 +741,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { outRules.Chars[charsPosition + i] = destName[i]; } - outRules.Chars[charsPosition + destLen] = '\0'; + outRules.Chars[charsPosition + destLen] = 0; } 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 - { - Ats = new long[TzMaxTimes], - Types = new byte[TzMaxTimes], - Ttis = new TimeTypeInfo[TzMaxTypes], - Chars = new char[TzCharsArraySize] - }; + outRules = new TimeZoneRule(); BinaryReader reader = new BinaryReader(inputData); @@ -1020,10 +1009,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone 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..]; - outRules.Chars[outRules.CharCount] = '\0'; + outRules.Chars[outRules.CharCount] = 0; for (int i = 0; i < outRules.TypeCount; i++) { @@ -1077,27 +1066,30 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone throw new InvalidOperationException(); } - char[] tempName = new char[TzNameMax + 1]; + byte[] tempName = new byte[TzNameMax + 1]; Array.Copy(workBuffer, position, tempName, 0, nRead); 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); - if (ParsePosixName(name, out TimeZoneRule tempRules, false)) + Box tempRulesBox = new Box(); + ref TimeZoneRule tempRules = ref tempRulesBox.Data; + + if (ParsePosixName(name, ref tempRulesBox.Data, false)) { int abbreviationCount = 0; charCount = outRules.CharCount; - Span chars = outRules.Chars; + Span chars = outRules.Chars; for (int i = 0; i < tempRules.TypeCount; i++) { - ReadOnlySpan tempChars = tempRules.Chars; - ReadOnlySpan tempAbbreviation = tempChars[tempRules.Ttis[i].AbbreviationListIndex..]; + ReadOnlySpan tempChars = tempRules.Chars; + ReadOnlySpan tempAbbreviation = tempChars[tempRules.Ttis[i].AbbreviationListIndex..]; int j; @@ -1175,7 +1167,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { 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; break; @@ -1184,7 +1176,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone 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; break; @@ -1259,10 +1251,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone long remainingSeconds = time % SecondsPerDay; calendarTime = new CalendarTimeInternal(); - calendarAdditionalInfo = new CalendarAdditionalInfo() - { - TimezoneName = new char[8] - }; + calendarAdditionalInfo = new CalendarAdditionalInfo(); while (timeDays < 0 || timeDays >= YearLengths[IsLeap((int)year)]) { @@ -1353,13 +1342,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone 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(); - calendarAdditionalInfo = new CalendarAdditionalInfo() - { - TimezoneName = new char[8] - }; + calendarAdditionalInfo = new CalendarAdditionalInfo(); ResultCode result; @@ -1398,7 +1384,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return ResultCode.TimeNotFound; } - result = ToCalendarTimeInternal(rules, newTime, out calendarTime, out calendarAdditionalInfo); + result = ToCalendarTimeInternal(in rules, newTime, out calendarTime, out calendarAdditionalInfo); if (result != 0) { return result; @@ -1450,17 +1436,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { calendarAdditionalInfo.IsDaySavingTime = rules.Ttis[ttiIndex].IsDaySavingTime; - ReadOnlySpan timeZoneAbbreviation = rules.Chars.AsSpan()[rules.Ttis[ttiIndex].AbbreviationListIndex..]; + ReadOnlySpan timeZoneAbbreviation = rules.Chars[rules.Ttis[ttiIndex].AbbreviationListIndex..]; int timeZoneSize = Math.Min(StringUtils.LengthCstr(timeZoneAbbreviation), 8); - timeZoneAbbreviation[..timeZoneSize].CopyTo(calendarAdditionalInfo.TimezoneName.AsSpan()); + timeZoneAbbreviation[..timeZoneSize].CopyTo(calendarAdditionalInfo.TimezoneName.ToSpan()); } 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; @@ -1604,7 +1590,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone 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 (pivot > 0) @@ -1675,9 +1661,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone 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() { @@ -1697,7 +1683,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone 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() { @@ -1710,7 +1696,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone Second = calendarTime.Second }; - return ToPosixTimeInternal(rules, calendarTimeInternal, out posixTime); + return ToPosixTimeInternal(in rules, calendarTimeInternal, out posixTime); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs index 141c2b4a57..081e45a2fa 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs @@ -16,7 +16,7 @@ using System; using System.Collections.Generic; using System.IO; -using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule; +using TimeZoneRuleBox = Ryujinx.Common.Memory.Box; namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { @@ -149,7 +149,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone 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; if (tzRule.TimeCount > 0) // Find the current transition period @@ -174,10 +178,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone continue; } - var abbrStart = tzRule.Chars.AsSpan(ttInfo.AbbreviationListIndex); - int abbrEnd = abbrStart.IndexOf('\0'); + var abbrStart = tzRule.Chars[ttInfo.AbbreviationListIndex..]; + 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; } - internal ResultCode LoadTimeZoneRule(out TimeZoneRule outRules, string locationName) + internal ResultCode LoadTimeZoneRule(ref TimeZoneRule rules, string locationName) { - outRules = new TimeZoneRule - { - Ats = new long[TzMaxTimes], - Types = new byte[TzMaxTimes], - Ttis = new TimeTypeInfo[TzMaxTypes], - Chars = new char[TzCharsArraySize] - }; + rules = default; if (!HasTimeZoneBinaryTitle()) { @@ -295,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone if (result == ResultCode.Success) { - result = Manager.ParseTimeZoneRuleBinary(out outRules, timeZoneBinaryStream); + result = Manager.ParseTimeZoneRuleBinary(ref rules, timeZoneBinaryStream); ncaFile.Dispose(); } diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs index 1a80365ad9..736bc1025f 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs @@ -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 System.IO; -using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule; namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { class TimeZoneManager { private bool _isInitialized; - private TimeZoneRule _myRules; + private Box _myRules; private string _deviceLocationName; private UInt128 _timeZoneRuleVersion; private uint _totalLocationNameCount; @@ -21,15 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone _deviceLocationName = "UTC"; _timeZoneRuleVersion = new UInt128(); _lock = new object(); - - // Empty rules - _myRules = new TimeZoneRule - { - Ats = new long[TzMaxTimes], - Types = new byte[TzMaxTimes], - Ttis = new TimeTypeInfo[TzMaxTypes], - Chars = new char[TzCharsArraySize] - }; + _myRules = new Box(); _timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom(); } @@ -78,7 +70,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone lock (_lock) { - bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out TimeZoneRule rules, timeZoneBinaryStream); + Box rules = new Box(); + + bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref rules.Data, timeZoneBinaryStream); if (timeZoneConversionSuccess) { @@ -154,13 +148,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone return result; } - public ResultCode ParseTimeZoneRuleBinary(out TimeZoneRule outRules, Stream timeZoneBinaryStream) + public ResultCode ParseTimeZoneRuleBinary(ref TimeZoneRule outRules, Stream timeZoneBinaryStream) { ResultCode result = ResultCode.Success; lock (_lock) { - bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out outRules, timeZoneBinaryStream); + bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref outRules, timeZoneBinaryStream); if (!timeZoneConversionSuccess) { @@ -208,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { if (_isInitialized) { - result = ToCalendarTime(_myRules, time, out calendar); + result = ToCalendarTime(in _myRules.Data, time, out calendar); } else { @@ -220,13 +214,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone 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; lock (_lock) { - result = TimeZone.ToCalendarTime(rules, time, out calendar); + result = TimeZone.ToCalendarTime(in rules, time, out calendar); } return result; @@ -240,7 +234,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { if (_isInitialized) { - result = ToPosixTime(_myRules, calendarTime, out posixTime); + result = ToPosixTime(in _myRules.Data, calendarTime, out posixTime); } else { @@ -252,13 +246,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone 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; lock (_lock) { - result = TimeZone.ToPosixTime(rules, calendarTime, out posixTime); + result = TimeZone.ToPosixTime(in rules, calendarTime, out posixTime); } return result; diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs index ef9b87e794..a84a278592 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/CalendarAdditionalInfo.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { @@ -8,14 +9,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone public uint DayOfWeek; public uint DayOfYear; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public char[] TimezoneName; + public Array8 TimezoneName; [MarshalAs(UnmanagedType.I1)] public bool IsDaySavingTime; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public char[] Padding; + public Array3 Padding; public int GmtOffset; } diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs index 399e070077..6a490bafcd 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeTypeInfo.cs @@ -1,17 +1,19 @@ -using System.Runtime.InteropServices; +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { - [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)] + [StructLayout(LayoutKind.Sequential, Size = Size, Pack = 4)] struct TimeTypeInfo { + public const int Size = 0x10; + public int GmtOffset; [MarshalAs(UnmanagedType.I1)] public bool IsDaySavingTime; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public char[] Padding1; + public Array3 Padding1; public int AbbreviationListIndex; @@ -21,7 +23,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone [MarshalAs(UnmanagedType.I1)] public bool IsGMT; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public char[] Padding2; + public ushort Padding2; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs index 1af7a81acd..705ff02599 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/Types/TimeZoneRule.cs @@ -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 { [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x4000, CharSet = CharSet.Ansi)] struct TimeZoneRule { - public const int TzMaxTypes = 128; - public const int TzMaxChars = 50; - public const int TzMaxLeaps = 50; - public const int TzMaxTimes = 1000; - public const int TzNameMax = 255; - public const int TzCharsArraySize = 2 * (TzNameMax + 1); + public const int TzMaxTypes = 128; + public const int TzMaxChars = 50; + public const int TzMaxLeaps = 50; + public const int TzMaxTimes = 1000; + public const int TzNameMax = 255; + public const int TzCharsArraySize = 2 * (TzNameMax + 1); public int TimeCount; public int TypeCount; @@ -22,17 +24,32 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone [MarshalAs(UnmanagedType.I1)] public bool GoAhead; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTimes)] - public long[] Ats; + [StructLayout(LayoutKind.Sequential, Size = sizeof(long) * TzMaxTimes)] + private struct AtsStorageStruct {} - [MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTimes)] - public byte[] Types; + private AtsStorageStruct _ats; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTypes)] - public TimeTypeInfo[] Ttis; + public Span Ats => SpanHelpers.AsSpan(ref _ats); - [MarshalAs(UnmanagedType.ByValArray, SizeConst = TzCharsArraySize)] - public char[] Chars; + [StructLayout(LayoutKind.Sequential, Size = sizeof(byte) * TzMaxTimes)] + private struct TypesStorageStruct {} + + private TypesStorageStruct _types; + + public Span Types => SpanHelpers.AsByteSpan(ref _types); + + [StructLayout(LayoutKind.Sequential, Size = TimeTypeInfo.Size * TzMaxTimes)] + private struct TimeTypeInfoStorageStruct { } + + private TimeTypeInfoStorageStruct _ttis; + + public Span Ttis => SpanHelpers.AsSpan(ref _ttis); + + [StructLayout(LayoutKind.Sequential, Size = sizeof(byte) * TzCharsArraySize)] + private struct CharsStorageStruct {} + + private CharsStorageStruct _chars; + public Span Chars => SpanHelpers.AsByteSpan(ref _chars); public int DefaultType; } diff --git a/Ryujinx.HLE/Utilities/StringUtils.cs b/Ryujinx.HLE/Utilities/StringUtils.cs index 3027139b36..269f9b5588 100644 --- a/Ryujinx.HLE/Utilities/StringUtils.cs +++ b/Ryujinx.HLE/Utilities/StringUtils.cs @@ -128,7 +128,7 @@ namespace Ryujinx.HLE.Utilities } } - public static int CompareCStr(ReadOnlySpan s1, ReadOnlySpan s2) + public static int CompareCStr(ReadOnlySpan s1, ReadOnlySpan s2) { int s1Index = 0; int s2Index = 0; @@ -142,11 +142,11 @@ namespace Ryujinx.HLE.Utilities return s2[s2Index] - s1[s1Index]; } - public static int LengthCstr(ReadOnlySpan s) + public static int LengthCstr(ReadOnlySpan s) { int i = 0; - while (s[i] != '\0') + while (s[i] != 0) { i++; }