diff --git a/src/Speckle.Sdk/Helpers/Constants.cs b/src/Speckle.Sdk/Helpers/Constants.cs index e8045ec7..13f340c9 100644 --- a/src/Speckle.Sdk/Helpers/Constants.cs +++ b/src/Speckle.Sdk/Helpers/Constants.cs @@ -1,5 +1,3 @@ -using System.Text.RegularExpressions; - namespace Speckle.Sdk.Helpers; public static class Constants @@ -7,6 +5,4 @@ public static class Constants public const double EPS = 1e-5; public const double SMALL_EPS = 1e-8; public const double EPS_SQUARED = EPS * EPS; - - public static readonly Regex ChunkPropertyNameRegex = new(@"^@\((\d*)\)"); //TODO: Experiment with compiled flag } diff --git a/src/Speckle.Sdk/Helpers/PropNameValidator.cs b/src/Speckle.Sdk/Helpers/PropNameValidator.cs new file mode 100644 index 00000000..c97ee9c5 --- /dev/null +++ b/src/Speckle.Sdk/Helpers/PropNameValidator.cs @@ -0,0 +1,43 @@ +using System.Diagnostics.Contracts; +using System.Text.RegularExpressions; + +namespace Speckle.Sdk.Helpers; + +public static +#if NET7_0_OR_GREATER +partial +#endif +class PropNameValidator +{ + private const string CHUNK_PROPERTY_NAME_REGEX_STRING = @"^@\((\d*)\)"; + +#if NET7_0_OR_GREATER + [GeneratedRegex(CHUNK_PROPERTY_NAME_REGEX_STRING)] + private static partial Regex ChunkRegex(); + + private static readonly Regex ChunkPropertyNameRegex = ChunkRegex(); +#else + private static readonly Regex ChunkPropertyNameRegex = new(CHUNK_PROPERTY_NAME_REGEX_STRING); +#endif + + public static bool IsChunkable(string propName, out int chunkSize) + { + if (ChunkPropertyNameRegex.IsMatch(propName)) + { + var match = ChunkPropertyNameRegex.Match(propName); + var isChunkable = int.TryParse(match.Groups[^1].Value, out chunkSize); + return isChunkable; + } + + chunkSize = -1; + return false; + } + + [Pure] + public static bool IsDetached(string propName) => +#if NET5_0_OR_GREATER + propName.StartsWith('@'); +#else + propName.StartsWith("@"); +#endif +} diff --git a/src/Speckle.Sdk/Models/Base.cs b/src/Speckle.Sdk/Models/Base.cs index b6856b7d..d68e31e7 100644 --- a/src/Speckle.Sdk/Models/Base.cs +++ b/src/Speckle.Sdk/Models/Base.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Text.RegularExpressions; using Speckle.Newtonsoft.Json; using Speckle.Newtonsoft.Json.Linq; using Speckle.Sdk.Common; @@ -25,8 +24,6 @@ namespace Speckle.Sdk.Models; [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Serialized property names are camelCase by design")] public class Base : DynamicBase, ISpeckleObject { - private static readonly Regex s_chunkSyntax = Constants.ChunkPropertyNameRegex; - private string _type; /// @@ -133,21 +130,14 @@ public class Base : DynamicBase, ISpeckleObject var dynamicProps = @base.DynamicPropertyKeys; foreach (var propName in dynamicProps) { -#if NETSTANDARD2_0 - if (!propName.StartsWith("@")) -#else - if (!propName.StartsWith('@')) -#endif + if (!PropNameValidator.IsDetached(propName)) { continue; } // Simplfied dynamic prop chunking handling - if (s_chunkSyntax.IsMatch(propName)) + if (PropNameValidator.IsChunkable(propName, out int chunkSize)) { - var match = s_chunkSyntax.Match(propName); - _ = int.TryParse(match.Groups[^1].Value, out int chunkSize); - if (chunkSize != -1 && @base[propName] is IList asList) { count += asList.Count / chunkSize; diff --git a/src/Speckle.Sdk/Models/Extras.cs b/src/Speckle.Sdk/Models/Extras.cs index d87f1e4b..4ba31292 100644 --- a/src/Speckle.Sdk/Models/Extras.cs +++ b/src/Speckle.Sdk/Models/Extras.cs @@ -16,5 +16,5 @@ public sealed class ObjectReference : Base { public required string referencedId { get; init; } - public Dictionary closure { get; set; } + public Dictionary? closure { get; set; } } diff --git a/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs index 998ff450..0109a43e 100644 --- a/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs +++ b/src/Speckle.Sdk/Serialisation/SpeckleObjectSerializer.cs @@ -10,7 +10,6 @@ using Speckle.Sdk.Common; using Speckle.Sdk.Helpers; using Speckle.Sdk.Models; using Speckle.Sdk.Transports; -using Constants = Speckle.Sdk.Helpers.Constants; namespace Speckle.Sdk.Serialisation; @@ -131,7 +130,7 @@ public class SpeckleObjectSerializer // Note: this change was needed as we've made the ObjectReference type inherit from Base for // the purpose of the "do not convert unchanged previously converted objects" POC. case ObjectReference r: - Dictionary ret = + Dictionary ret = new() { ["speckle_type"] = r.speckle_type, @@ -322,19 +321,12 @@ public class SpeckleObjectSerializer } object? baseValue = baseObj[propName]; -#if NETSTANDARD2_0 - bool isDetachable = propName.StartsWith("@"); -#else - bool isDetachable = propName.StartsWith('@'); -#endif - bool isChunkable = false; - int chunkSize = 1000; - if (Constants.ChunkPropertyNameRegex.IsMatch(propName)) - { - var match = Constants.ChunkPropertyNameRegex.Match(propName); - isChunkable = int.TryParse(match.Groups[^1].Value, out chunkSize); - } + bool isDetachable = PropNameValidator.IsDetached(propName); + + int chunkSize = 1000; + bool isChunkable = isDetachable && PropNameValidator.IsChunkable(propName, out chunkSize); + allProperties[propName] = (baseValue, new PropertyAttributeInfo(isDetachable, isChunkable, chunkSize, null)); }