Compare commits
No commits in common. "master" and "empty" have entirely different histories.
259 changed files with 2 additions and 24067 deletions
901
.editorconfig
901
.editorconfig
|
@ -1,901 +0,0 @@
|
||||||
[*.cs]
|
|
||||||
|
|
||||||
# CA1001: Types that own disposable fields should be disposable
|
|
||||||
dotnet_diagnostic.CA1001.severity = warning
|
|
||||||
|
|
||||||
# CA1000: Do not declare static members on generic types
|
|
||||||
dotnet_diagnostic.CA1000.severity = warning
|
|
||||||
|
|
||||||
# CA1002: Do not expose generic lists
|
|
||||||
dotnet_diagnostic.CA1002.severity = warning
|
|
||||||
|
|
||||||
# CA1003: Use generic event handler instances
|
|
||||||
dotnet_diagnostic.CA1003.severity = warning
|
|
||||||
|
|
||||||
# CA1005: Avoid excessive parameters on generic types
|
|
||||||
dotnet_diagnostic.CA1005.severity = warning
|
|
||||||
|
|
||||||
# CA1008: Enums should have zero value
|
|
||||||
dotnet_diagnostic.CA1008.severity = warning
|
|
||||||
|
|
||||||
# CA1010: Generic interface should also be implemented
|
|
||||||
dotnet_diagnostic.CA1010.severity = warning
|
|
||||||
|
|
||||||
# CA1012: Abstract types should not have public constructors
|
|
||||||
dotnet_diagnostic.CA1012.severity = warning
|
|
||||||
|
|
||||||
# CA1014: Mark assemblies with CLSCompliant
|
|
||||||
dotnet_diagnostic.CA1014.severity = warning
|
|
||||||
|
|
||||||
# CA1016: Mark assemblies with assembly version
|
|
||||||
dotnet_diagnostic.CA1016.severity = warning
|
|
||||||
|
|
||||||
# CA1017: Mark assemblies with ComVisible
|
|
||||||
dotnet_diagnostic.CA1017.severity = warning
|
|
||||||
|
|
||||||
# CA1018: Mark attributes with AttributeUsageAttribute
|
|
||||||
dotnet_diagnostic.CA1018.severity = warning
|
|
||||||
|
|
||||||
# CA1019: Define accessors for attribute arguments
|
|
||||||
dotnet_diagnostic.CA1019.severity = warning
|
|
||||||
|
|
||||||
# CA1021: Avoid out parameters
|
|
||||||
dotnet_diagnostic.CA1021.severity = warning
|
|
||||||
|
|
||||||
# CA1024: Use properties where appropriate
|
|
||||||
dotnet_diagnostic.CA1024.severity = warning
|
|
||||||
|
|
||||||
# CA1027: Mark enums with FlagsAttribute
|
|
||||||
dotnet_diagnostic.CA1027.severity = warning
|
|
||||||
|
|
||||||
# CA1028: Enum Storage should be Int32
|
|
||||||
dotnet_diagnostic.CA1028.severity = warning
|
|
||||||
|
|
||||||
# CA1030: Use events where appropriate
|
|
||||||
dotnet_diagnostic.CA1030.severity = warning
|
|
||||||
|
|
||||||
# CA1031: Do not catch general exception types
|
|
||||||
dotnet_diagnostic.CA1031.severity = warning
|
|
||||||
|
|
||||||
# CA1033: Interface methods should be callable by child types
|
|
||||||
dotnet_diagnostic.CA1033.severity = warning
|
|
||||||
|
|
||||||
# CA1034: Nested types should not be visible
|
|
||||||
dotnet_diagnostic.CA1034.severity = warning
|
|
||||||
|
|
||||||
# CA1036: Override methods on comparable types
|
|
||||||
dotnet_diagnostic.CA1036.severity = warning
|
|
||||||
|
|
||||||
# CA1040: Avoid empty interfaces
|
|
||||||
dotnet_diagnostic.CA1040.severity = warning
|
|
||||||
|
|
||||||
# CA1041: Provide ObsoleteAttribute message
|
|
||||||
dotnet_diagnostic.CA1041.severity = warning
|
|
||||||
|
|
||||||
# CA1043: Use Integral Or String Argument For Indexers
|
|
||||||
dotnet_diagnostic.CA1043.severity = warning
|
|
||||||
|
|
||||||
# CA1044: Properties should not be write only
|
|
||||||
dotnet_diagnostic.CA1044.severity = warning
|
|
||||||
|
|
||||||
# CA1045: Do not pass types by reference
|
|
||||||
dotnet_diagnostic.CA1045.severity = warning
|
|
||||||
|
|
||||||
# CA1046: Do not overload equality operator on reference types
|
|
||||||
dotnet_diagnostic.CA1046.severity = warning
|
|
||||||
|
|
||||||
# CA1050: Declare types in namespaces
|
|
||||||
dotnet_diagnostic.CA1050.severity = warning
|
|
||||||
|
|
||||||
# CA1051: Do not declare visible instance fields
|
|
||||||
dotnet_diagnostic.CA1051.severity = warning
|
|
||||||
|
|
||||||
# CA1052: Static holder types should be Static or NotInheritable
|
|
||||||
dotnet_diagnostic.CA1052.severity = warning
|
|
||||||
|
|
||||||
# CA1054: URI-like parameters should not be strings
|
|
||||||
dotnet_diagnostic.CA1054.severity = warning
|
|
||||||
|
|
||||||
# CA1055: URI-like return values should not be strings
|
|
||||||
dotnet_diagnostic.CA1055.severity = warning
|
|
||||||
|
|
||||||
# CA1056: URI-like properties should not be strings
|
|
||||||
dotnet_diagnostic.CA1056.severity = warning
|
|
||||||
|
|
||||||
# CA1058: Types should not extend certain base types
|
|
||||||
dotnet_diagnostic.CA1058.severity = warning
|
|
||||||
|
|
||||||
# CA1060: Move pinvokes to native methods class
|
|
||||||
dotnet_diagnostic.CA1060.severity = warning
|
|
||||||
|
|
||||||
# CA1061: Do not hide base class methods
|
|
||||||
dotnet_diagnostic.CA1061.severity = warning
|
|
||||||
|
|
||||||
# CA1062: Validate arguments of public methods
|
|
||||||
dotnet_diagnostic.CA1062.severity = warning
|
|
||||||
|
|
||||||
# CA1063: Implement IDisposable Correctly
|
|
||||||
dotnet_diagnostic.CA1063.severity = warning
|
|
||||||
|
|
||||||
# CA1064: Exceptions should be public
|
|
||||||
dotnet_diagnostic.CA1064.severity = warning
|
|
||||||
|
|
||||||
# CA1065: Do not raise exceptions in unexpected locations
|
|
||||||
dotnet_diagnostic.CA1065.severity = warning
|
|
||||||
|
|
||||||
# CA1066: Implement IEquatable when overriding Object.Equals
|
|
||||||
dotnet_diagnostic.CA1066.severity = warning
|
|
||||||
|
|
||||||
# CA1067: Override Object.Equals(object) when implementing IEquatable<T>
|
|
||||||
dotnet_diagnostic.CA1067.severity = warning
|
|
||||||
|
|
||||||
# CA1068: CancellationToken parameters must come last
|
|
||||||
dotnet_diagnostic.CA1068.severity = warning
|
|
||||||
|
|
||||||
# CA1069: Enums values should not be duplicated
|
|
||||||
dotnet_diagnostic.CA1069.severity = warning
|
|
||||||
|
|
||||||
# CA1070: Do not declare event fields as virtual
|
|
||||||
dotnet_diagnostic.CA1070.severity = warning
|
|
||||||
|
|
||||||
# CA1303: Do not pass literals as localized parameters
|
|
||||||
dotnet_diagnostic.CA1303.severity = warning
|
|
||||||
|
|
||||||
# CA1304: Specify CultureInfo
|
|
||||||
dotnet_diagnostic.CA1304.severity = warning
|
|
||||||
|
|
||||||
# CA1305: Specify IFormatProvider
|
|
||||||
dotnet_diagnostic.CA1305.severity = warning
|
|
||||||
|
|
||||||
# CA1307: Specify StringComparison for clarity
|
|
||||||
dotnet_diagnostic.CA1307.severity = warning
|
|
||||||
|
|
||||||
# CA1308: Normalize strings to uppercase
|
|
||||||
dotnet_diagnostic.CA1308.severity = warning
|
|
||||||
|
|
||||||
# CA1310: Specify StringComparison for correctness
|
|
||||||
dotnet_diagnostic.CA1310.severity = warning
|
|
||||||
|
|
||||||
# CA1401: P/Invokes should not be visible
|
|
||||||
dotnet_diagnostic.CA1401.severity = warning
|
|
||||||
|
|
||||||
# CA1416: Validate platform compatibility
|
|
||||||
dotnet_diagnostic.CA1416.severity = warning
|
|
||||||
|
|
||||||
# CA1417: Do not use 'OutAttribute' on string parameters for P/Invokes
|
|
||||||
dotnet_diagnostic.CA1417.severity = warning
|
|
||||||
|
|
||||||
# CA1418: Use valid platform string
|
|
||||||
dotnet_diagnostic.CA1418.severity = warning
|
|
||||||
|
|
||||||
# CA1419: Provide a parameterless constructor that is as visible as the containing type for concrete types derived from 'System.Runtime.InteropServices.SafeHandle'
|
|
||||||
dotnet_diagnostic.CA1419.severity = warning
|
|
||||||
|
|
||||||
# CA1420: Property, type, or attribute requires runtime marshalling
|
|
||||||
dotnet_diagnostic.CA1420.severity = warning
|
|
||||||
|
|
||||||
# CA1421: This method uses runtime marshalling even when the 'DisableRuntimeMarshallingAttribute' is applied
|
|
||||||
dotnet_diagnostic.CA1421.severity = warning
|
|
||||||
|
|
||||||
# CA1422: Validate platform compatibility
|
|
||||||
dotnet_diagnostic.CA1422.severity = warning
|
|
||||||
|
|
||||||
# CA1501: Avoid excessive inheritance
|
|
||||||
dotnet_diagnostic.CA1501.severity = warning
|
|
||||||
|
|
||||||
# CA1502: Avoid excessive complexity
|
|
||||||
dotnet_diagnostic.CA1502.severity = warning
|
|
||||||
|
|
||||||
# CA1505: Avoid unmaintainable code
|
|
||||||
dotnet_diagnostic.CA1505.severity = warning
|
|
||||||
|
|
||||||
# CA1506: Avoid excessive class coupling
|
|
||||||
# dotnet_diagnostic.CA1506.severity = warning
|
|
||||||
|
|
||||||
# CA1509: Invalid entry in code metrics rule specification file
|
|
||||||
dotnet_diagnostic.CA1509.severity = warning
|
|
||||||
|
|
||||||
# CA1510: Use ArgumentNullException throw helper
|
|
||||||
dotnet_diagnostic.CA1510.severity = warning
|
|
||||||
|
|
||||||
# CA1511: Use ArgumentException throw helper
|
|
||||||
dotnet_diagnostic.CA1511.severity = warning
|
|
||||||
|
|
||||||
# CA1512: Use ArgumentOutOfRangeException throw helper
|
|
||||||
dotnet_diagnostic.CA1512.severity = warning
|
|
||||||
|
|
||||||
# CA1513: Use ObjectDisposedException throw helper
|
|
||||||
dotnet_diagnostic.CA1513.severity = warning
|
|
||||||
|
|
||||||
# CA1700: Do not name enum values 'Reserved'
|
|
||||||
dotnet_diagnostic.CA1700.severity = warning
|
|
||||||
|
|
||||||
# CA1707: Identifiers should not contain underscores
|
|
||||||
dotnet_diagnostic.CA1707.severity = warning
|
|
||||||
|
|
||||||
# CA1708: Identifiers should differ by more than case
|
|
||||||
dotnet_diagnostic.CA1708.severity = none
|
|
||||||
|
|
||||||
# CA1710: Identifiers should have correct suffix
|
|
||||||
dotnet_diagnostic.CA1710.severity = warning
|
|
||||||
|
|
||||||
# CA1711: Identifiers should not have incorrect suffix
|
|
||||||
dotnet_diagnostic.CA1711.severity = warning
|
|
||||||
|
|
||||||
# CA1712: Do not prefix enum values with type name
|
|
||||||
dotnet_diagnostic.CA1712.severity = warning
|
|
||||||
|
|
||||||
# CA1713: Events should not have 'Before' or 'After' prefix
|
|
||||||
dotnet_diagnostic.CA1713.severity = warning
|
|
||||||
|
|
||||||
# CA1715: Identifiers should have correct prefix
|
|
||||||
dotnet_diagnostic.CA1715.severity = warning
|
|
||||||
|
|
||||||
# CA1716: Identifiers should not match keywords
|
|
||||||
dotnet_diagnostic.CA1716.severity = none
|
|
||||||
|
|
||||||
# CA1720: Identifier contains type name
|
|
||||||
dotnet_diagnostic.CA1720.severity = warning
|
|
||||||
|
|
||||||
# CA1721: Property names should not match get methods
|
|
||||||
dotnet_diagnostic.CA1721.severity = warning
|
|
||||||
|
|
||||||
# CA1724: Type names should not match namespaces
|
|
||||||
dotnet_diagnostic.CA1724.severity = warning
|
|
||||||
|
|
||||||
# CA1725: Parameter names should match base declaration
|
|
||||||
dotnet_diagnostic.CA1725.severity = warning
|
|
||||||
|
|
||||||
# CA1727: Use PascalCase for named placeholders
|
|
||||||
dotnet_diagnostic.CA1727.severity = warning
|
|
||||||
|
|
||||||
# CA1806: Do not ignore method results
|
|
||||||
dotnet_diagnostic.CA1806.severity = warning
|
|
||||||
|
|
||||||
# CA1810: Initialize reference type static fields inline
|
|
||||||
dotnet_diagnostic.CA1810.severity = warning
|
|
||||||
|
|
||||||
# CA1813: Avoid unsealed attributes
|
|
||||||
dotnet_diagnostic.CA1813.severity = warning
|
|
||||||
|
|
||||||
# CA1814: Prefer jagged arrays over multidimensional
|
|
||||||
dotnet_diagnostic.CA1814.severity = warning
|
|
||||||
|
|
||||||
# CA1815: Override equals and operator equals on value types
|
|
||||||
dotnet_diagnostic.CA1815.severity = warning
|
|
||||||
|
|
||||||
# CA1816: Dispose methods should call SuppressFinalize
|
|
||||||
dotnet_diagnostic.CA1816.severity = warning
|
|
||||||
|
|
||||||
# CA1819: Properties should not return arrays
|
|
||||||
# dotnet_diagnostic.CA1819.severity = warning
|
|
||||||
|
|
||||||
# CA1820: Test for empty strings using string length
|
|
||||||
dotnet_diagnostic.CA1820.severity = warning
|
|
||||||
|
|
||||||
# CA1821: Remove empty Finalizers
|
|
||||||
dotnet_diagnostic.CA1821.severity = warning
|
|
||||||
|
|
||||||
# CA1822: Mark members as static
|
|
||||||
dotnet_diagnostic.CA1822.severity = warning
|
|
||||||
|
|
||||||
# CA1823: Avoid unused private fields
|
|
||||||
dotnet_diagnostic.CA1823.severity = warning
|
|
||||||
|
|
||||||
# CA1826: Do not use Enumerable methods on indexable collections
|
|
||||||
dotnet_diagnostic.CA1826.severity = warning
|
|
||||||
|
|
||||||
# CA1827: Do not use Count() or LongCount() when Any() can be used
|
|
||||||
dotnet_diagnostic.CA1827.severity = warning
|
|
||||||
|
|
||||||
# CA1828: Do not use CountAsync() or LongCountAsync() when AnyAsync() can be used
|
|
||||||
dotnet_diagnostic.CA1828.severity = warning
|
|
||||||
|
|
||||||
# CA1829: Use Length/Count property instead of Count() when available
|
|
||||||
dotnet_diagnostic.CA1829.severity = warning
|
|
||||||
|
|
||||||
# CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder
|
|
||||||
dotnet_diagnostic.CA1830.severity = warning
|
|
||||||
|
|
||||||
# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
|
|
||||||
dotnet_diagnostic.CA1831.severity = warning
|
|
||||||
|
|
||||||
# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
|
|
||||||
dotnet_diagnostic.CA1832.severity = warning
|
|
||||||
|
|
||||||
# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate
|
|
||||||
dotnet_diagnostic.CA1833.severity = warning
|
|
||||||
|
|
||||||
# CA1834: Consider using 'StringBuilder.Append(char)' when applicable
|
|
||||||
dotnet_diagnostic.CA1834.severity = warning
|
|
||||||
|
|
||||||
# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync'
|
|
||||||
dotnet_diagnostic.CA1835.severity = warning
|
|
||||||
|
|
||||||
# CA1836: Prefer IsEmpty over Count
|
|
||||||
dotnet_diagnostic.CA1836.severity = warning
|
|
||||||
|
|
||||||
# CA1837: Use 'Environment.ProcessId'
|
|
||||||
dotnet_diagnostic.CA1837.severity = warning
|
|
||||||
|
|
||||||
# CA1838: Avoid 'StringBuilder' parameters for P/Invokes
|
|
||||||
dotnet_diagnostic.CA1838.severity = warning
|
|
||||||
|
|
||||||
# CA1839: Use 'Environment.ProcessPath'
|
|
||||||
dotnet_diagnostic.CA1839.severity = warning
|
|
||||||
|
|
||||||
# CA1840: Use 'Environment.CurrentManagedThreadId'
|
|
||||||
dotnet_diagnostic.CA1840.severity = warning
|
|
||||||
|
|
||||||
# CA1842: Do not use 'WhenAll' with a single task
|
|
||||||
dotnet_diagnostic.CA1842.severity = warning
|
|
||||||
|
|
||||||
# CA1843: Do not use 'WaitAll' with a single task
|
|
||||||
dotnet_diagnostic.CA1843.severity = warning
|
|
||||||
|
|
||||||
# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream'
|
|
||||||
dotnet_diagnostic.CA1844.severity = warning
|
|
||||||
|
|
||||||
# CA1846: Prefer 'AsSpan' over 'Substring'
|
|
||||||
dotnet_diagnostic.CA1846.severity = warning
|
|
||||||
|
|
||||||
# CA1847: Use char literal for a single character lookup
|
|
||||||
dotnet_diagnostic.CA1847.severity = warning
|
|
||||||
|
|
||||||
# CA1848: Use the LoggerMessage delegates
|
|
||||||
dotnet_diagnostic.CA1848.severity = warning
|
|
||||||
|
|
||||||
# CA1849: Call async methods when in an async method
|
|
||||||
dotnet_diagnostic.CA1849.severity = warning
|
|
||||||
|
|
||||||
# CA1850: Prefer static 'HashData' method over 'ComputeHash'
|
|
||||||
dotnet_diagnostic.CA1850.severity = warning
|
|
||||||
|
|
||||||
# CA1852: Seal internal types
|
|
||||||
dotnet_diagnostic.CA1852.severity = warning
|
|
||||||
|
|
||||||
# CA1853: Unnecessary call to 'Dictionary.ContainsKey(key)'
|
|
||||||
dotnet_diagnostic.CA1853.severity = warning
|
|
||||||
|
|
||||||
# CA1854: Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method
|
|
||||||
dotnet_diagnostic.CA1854.severity = warning
|
|
||||||
|
|
||||||
# CA1858: Use 'StartsWith' instead of 'IndexOf'
|
|
||||||
dotnet_diagnostic.CA1858.severity = warning
|
|
||||||
|
|
||||||
# CA1859: Use concrete types when possible for improved performance
|
|
||||||
dotnet_diagnostic.CA1859.severity = warning
|
|
||||||
|
|
||||||
# CA1860: Avoid using 'Enumerable.Any()' extension method
|
|
||||||
dotnet_diagnostic.CA1860.severity = warning
|
|
||||||
|
|
||||||
# CA1861: Avoid constant arrays as arguments
|
|
||||||
dotnet_diagnostic.CA1861.severity = warning
|
|
||||||
|
|
||||||
# CA1862: Use the 'StringComparison' method overloads to perform case-insensitive string comparisons
|
|
||||||
dotnet_diagnostic.CA1862.severity = warning
|
|
||||||
|
|
||||||
# CA1863: Use 'CompositeFormat'
|
|
||||||
dotnet_diagnostic.CA1863.severity = warning
|
|
||||||
|
|
||||||
# CA1864: Prefer the 'IDictionary.TryAdd(TKey, TValue)' method
|
|
||||||
dotnet_diagnostic.CA1864.severity = warning
|
|
||||||
|
|
||||||
# CA1868: Unnecessary call to 'Contains(item)'
|
|
||||||
dotnet_diagnostic.CA1868.severity = warning
|
|
||||||
|
|
||||||
# CA1869: Cache and reuse 'JsonSerializerOptions' instances
|
|
||||||
dotnet_diagnostic.CA1869.severity = warning
|
|
||||||
|
|
||||||
# CA2000: Dispose objects before losing scope
|
|
||||||
dotnet_diagnostic.CA2000.severity = warning
|
|
||||||
|
|
||||||
# CA2002: Do not lock on objects with weak identity
|
|
||||||
dotnet_diagnostic.CA2002.severity = warning
|
|
||||||
|
|
||||||
# CA2007: Consider calling ConfigureAwait on the awaited task
|
|
||||||
dotnet_diagnostic.CA2007.severity = warning
|
|
||||||
|
|
||||||
# CA2008: Do not create tasks without passing a TaskScheduler
|
|
||||||
dotnet_diagnostic.CA2008.severity = warning
|
|
||||||
|
|
||||||
# CA2009: Do not call ToImmutableCollection on an ImmutableCollection value
|
|
||||||
dotnet_diagnostic.CA2009.severity = warning
|
|
||||||
|
|
||||||
# CA2011: Avoid infinite recursion
|
|
||||||
dotnet_diagnostic.CA2011.severity = warning
|
|
||||||
|
|
||||||
# CA2012: Use ValueTasks correctly
|
|
||||||
dotnet_diagnostic.CA2012.severity = warning
|
|
||||||
|
|
||||||
# CA2013: Do not use ReferenceEquals with value types
|
|
||||||
dotnet_diagnostic.CA2013.severity = warning
|
|
||||||
|
|
||||||
# CA2015: Do not define finalizers for types derived from MemoryManager<T>
|
|
||||||
dotnet_diagnostic.CA2015.severity = warning
|
|
||||||
|
|
||||||
# CA2017: Parameter count mismatch
|
|
||||||
dotnet_diagnostic.CA2017.severity = warning
|
|
||||||
|
|
||||||
# CA2018: 'Buffer.BlockCopy' expects the number of bytes to be copied for the 'count' argument
|
|
||||||
dotnet_diagnostic.CA2018.severity = warning
|
|
||||||
|
|
||||||
# CA2019: Improper 'ThreadStatic' field initialization
|
|
||||||
dotnet_diagnostic.CA2019.severity = warning
|
|
||||||
|
|
||||||
# CA2021: Do not call Enumerable.Cast<T> or Enumerable.OfType<T> with incompatible types
|
|
||||||
dotnet_diagnostic.CA2021.severity = warning
|
|
||||||
|
|
||||||
# CA2100: Review SQL queries for security vulnerabilities
|
|
||||||
dotnet_diagnostic.CA2100.severity = warning
|
|
||||||
|
|
||||||
# CA2101: Specify marshaling for P/Invoke string arguments
|
|
||||||
dotnet_diagnostic.CA2101.severity = warning
|
|
||||||
|
|
||||||
# CA2119: Seal methods that satisfy private interfaces
|
|
||||||
dotnet_diagnostic.CA2119.severity = warning
|
|
||||||
|
|
||||||
# CA2153: Do Not Catch Corrupted State Exceptions
|
|
||||||
dotnet_diagnostic.CA2153.severity = warning
|
|
||||||
|
|
||||||
# CA2200: Rethrow to preserve stack details
|
|
||||||
dotnet_diagnostic.CA2200.severity = warning
|
|
||||||
|
|
||||||
# CA2201: Do not raise reserved exception types
|
|
||||||
dotnet_diagnostic.CA2201.severity = warning
|
|
||||||
|
|
||||||
# CA2207: Initialize value type static fields inline
|
|
||||||
dotnet_diagnostic.CA2207.severity = warning
|
|
||||||
|
|
||||||
# CA2208: Instantiate argument exceptions correctly
|
|
||||||
dotnet_diagnostic.CA2208.severity = warning
|
|
||||||
|
|
||||||
# CA2211: Non-constant fields should not be visible
|
|
||||||
dotnet_diagnostic.CA2211.severity = warning
|
|
||||||
|
|
||||||
# CA2213: Disposable fields should be disposed
|
|
||||||
dotnet_diagnostic.CA2213.severity = warning
|
|
||||||
|
|
||||||
# CA2214: Do not call overridable methods in constructors
|
|
||||||
dotnet_diagnostic.CA2214.severity = warning
|
|
||||||
|
|
||||||
# CA2215: Dispose methods should call base class dispose
|
|
||||||
dotnet_diagnostic.CA2215.severity = warning
|
|
||||||
|
|
||||||
# CA2216: Disposable types should declare finalizer
|
|
||||||
dotnet_diagnostic.CA2216.severity = warning
|
|
||||||
|
|
||||||
# CA2217: Do not mark enums with FlagsAttribute
|
|
||||||
dotnet_diagnostic.CA2217.severity = warning
|
|
||||||
|
|
||||||
# CA2219: Do not raise exceptions in finally clauses
|
|
||||||
dotnet_diagnostic.CA2219.severity = warning
|
|
||||||
|
|
||||||
# CA2225: Operator overloads have named alternates
|
|
||||||
dotnet_diagnostic.CA2225.severity = warning
|
|
||||||
|
|
||||||
# CA2226: Operators should have symmetrical overloads
|
|
||||||
dotnet_diagnostic.CA2226.severity = warning
|
|
||||||
|
|
||||||
# CA2227: Collection properties should be read only
|
|
||||||
dotnet_diagnostic.CA2227.severity = warning
|
|
||||||
|
|
||||||
# CA2231: Overload operator equals on overriding value type Equals
|
|
||||||
dotnet_diagnostic.CA2231.severity = warning
|
|
||||||
|
|
||||||
# CA2235: Mark all non-serializable fields
|
|
||||||
dotnet_diagnostic.CA2235.severity = warning
|
|
||||||
|
|
||||||
# CA2237: Mark ISerializable types with serializable
|
|
||||||
dotnet_diagnostic.CA2237.severity = warning
|
|
||||||
|
|
||||||
# CA2241: Provide correct arguments to formatting methods
|
|
||||||
dotnet_diagnostic.CA2241.severity = warning
|
|
||||||
|
|
||||||
# CA2242: Test for NaN correctly
|
|
||||||
dotnet_diagnostic.CA2242.severity = warning
|
|
||||||
|
|
||||||
# CA2243: Attribute string literals should parse correctly
|
|
||||||
dotnet_diagnostic.CA2243.severity = warning
|
|
||||||
|
|
||||||
# CA2244: Do not duplicate indexed element initializations
|
|
||||||
dotnet_diagnostic.CA2244.severity = warning
|
|
||||||
|
|
||||||
# CA2245: Do not assign a property to itself
|
|
||||||
dotnet_diagnostic.CA2245.severity = warning
|
|
||||||
|
|
||||||
# CA2246: Assigning symbol and its member in the same statement
|
|
||||||
dotnet_diagnostic.CA2246.severity = warning
|
|
||||||
|
|
||||||
# CA2247: Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum
|
|
||||||
dotnet_diagnostic.CA2247.severity = warning
|
|
||||||
|
|
||||||
# CA2248: Provide correct 'enum' argument to 'Enum.HasFlag'
|
|
||||||
dotnet_diagnostic.CA2248.severity = warning
|
|
||||||
|
|
||||||
# CA2249: Consider using 'string.Contains' instead of 'string.IndexOf'
|
|
||||||
dotnet_diagnostic.CA2249.severity = warning
|
|
||||||
|
|
||||||
# CA2250: Use 'ThrowIfCancellationRequested'
|
|
||||||
dotnet_diagnostic.CA2250.severity = warning
|
|
||||||
|
|
||||||
# CA2251: Use 'string.Equals'
|
|
||||||
dotnet_diagnostic.CA2251.severity = warning
|
|
||||||
|
|
||||||
# CA2253: Named placeholders should not be numeric values
|
|
||||||
dotnet_diagnostic.CA2253.severity = warning
|
|
||||||
|
|
||||||
# CA2254: Template should be a static expression
|
|
||||||
dotnet_diagnostic.CA2254.severity = warning
|
|
||||||
|
|
||||||
# CA2255: The 'ModuleInitializer' attribute should not be used in libraries
|
|
||||||
dotnet_diagnostic.CA2255.severity = warning
|
|
||||||
|
|
||||||
# CA2256: All members declared in parent interfaces must have an implementation in a DynamicInterfaceCastableImplementation-attributed interface
|
|
||||||
dotnet_diagnostic.CA2256.severity = warning
|
|
||||||
|
|
||||||
# CA2257: Members defined on an interface with the 'DynamicInterfaceCastableImplementationAttribute' should be 'static'
|
|
||||||
dotnet_diagnostic.CA2257.severity = warning
|
|
||||||
|
|
||||||
# CA2258: Providing a 'DynamicInterfaceCastableImplementation' interface in Visual Basic is unsupported
|
|
||||||
dotnet_diagnostic.CA2258.severity = warning
|
|
||||||
|
|
||||||
# CA2259: 'ThreadStatic' only affects static fields
|
|
||||||
dotnet_diagnostic.CA2259.severity = warning
|
|
||||||
|
|
||||||
# CA2261: Do not use ConfigureAwaitOptions.SuppressThrowing with Task<TResult>
|
|
||||||
dotnet_diagnostic.CA2261.severity = warning
|
|
||||||
|
|
||||||
# CA2300: Do not use insecure deserializer BinaryFormatter
|
|
||||||
dotnet_diagnostic.CA2300.severity = warning
|
|
||||||
|
|
||||||
# CA2301: Do not call BinaryFormatter.Deserialize without first setting BinaryFormatter.Binder
|
|
||||||
dotnet_diagnostic.CA2301.severity = warning
|
|
||||||
|
|
||||||
# CA2302: Ensure BinaryFormatter.Binder is set before calling BinaryFormatter.Deserialize
|
|
||||||
dotnet_diagnostic.CA2302.severity = warning
|
|
||||||
|
|
||||||
# CA2305: Do not use insecure deserializer LosFormatter
|
|
||||||
dotnet_diagnostic.CA2305.severity = warning
|
|
||||||
|
|
||||||
# CA2310: Do not use insecure deserializer NetDataContractSerializer
|
|
||||||
dotnet_diagnostic.CA2310.severity = warning
|
|
||||||
|
|
||||||
# CA2311: Do not deserialize without first setting NetDataContractSerializer.Binder
|
|
||||||
dotnet_diagnostic.CA2311.severity = warning
|
|
||||||
|
|
||||||
# CA2312: Ensure NetDataContractSerializer.Binder is set before deserializing
|
|
||||||
dotnet_diagnostic.CA2312.severity = warning
|
|
||||||
|
|
||||||
# CA2315: Do not use insecure deserializer ObjectStateFormatter
|
|
||||||
dotnet_diagnostic.CA2315.severity = warning
|
|
||||||
|
|
||||||
# CA2321: Do not deserialize with JavaScriptSerializer using a SimpleTypeResolver
|
|
||||||
dotnet_diagnostic.CA2321.severity = warning
|
|
||||||
|
|
||||||
# CA2322: Ensure JavaScriptSerializer is not initialized with SimpleTypeResolver before deserializing
|
|
||||||
dotnet_diagnostic.CA2322.severity = warning
|
|
||||||
|
|
||||||
# CA2326: Do not use TypeNameHandling values other than None
|
|
||||||
dotnet_diagnostic.CA2326.severity = warning
|
|
||||||
|
|
||||||
# CA2327: Do not use insecure JsonSerializerSettings
|
|
||||||
dotnet_diagnostic.CA2327.severity = warning
|
|
||||||
|
|
||||||
# CA2328: Ensure that JsonSerializerSettings are secure
|
|
||||||
dotnet_diagnostic.CA2328.severity = warning
|
|
||||||
|
|
||||||
# CA2329: Do not deserialize with JsonSerializer using an insecure configuration
|
|
||||||
dotnet_diagnostic.CA2329.severity = warning
|
|
||||||
|
|
||||||
# CA2330: Ensure that JsonSerializer has a secure configuration when deserializing
|
|
||||||
dotnet_diagnostic.CA2330.severity = warning
|
|
||||||
|
|
||||||
# CA2350: Do not use DataTable.ReadXml() with untrusted data
|
|
||||||
dotnet_diagnostic.CA2350.severity = warning
|
|
||||||
|
|
||||||
# CA2351: Do not use DataSet.ReadXml() with untrusted data
|
|
||||||
dotnet_diagnostic.CA2351.severity = warning
|
|
||||||
|
|
||||||
# CA2361: Ensure auto-generated class containing DataSet.ReadXml() is not used with untrusted data
|
|
||||||
dotnet_diagnostic.CA2361.severity = warning
|
|
||||||
|
|
||||||
# CA3001: Review code for SQL injection vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3001.severity = warning
|
|
||||||
|
|
||||||
# CA3002: Review code for XSS vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3002.severity = warning
|
|
||||||
|
|
||||||
# CA3003: Review code for file path injection vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3003.severity = warning
|
|
||||||
|
|
||||||
# CA3004: Review code for information disclosure vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3004.severity = warning
|
|
||||||
|
|
||||||
# CA3005: Review code for LDAP injection vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3005.severity = warning
|
|
||||||
|
|
||||||
# CA3006: Review code for process command injection vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3006.severity = warning
|
|
||||||
|
|
||||||
# CA3007: Review code for open redirect vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3007.severity = warning
|
|
||||||
|
|
||||||
# CA3008: Review code for XPath injection vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3008.severity = warning
|
|
||||||
|
|
||||||
# CA3009: Review code for XML injection vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3009.severity = warning
|
|
||||||
|
|
||||||
# CA3010: Review code for XAML injection vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3010.severity = warning
|
|
||||||
|
|
||||||
# CA3011: Review code for DLL injection vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3011.severity = warning
|
|
||||||
|
|
||||||
# CA3012: Review code for regex injection vulnerabilities
|
|
||||||
dotnet_diagnostic.CA3012.severity = warning
|
|
||||||
|
|
||||||
# CA3061: Do Not Add Schema By URL
|
|
||||||
dotnet_diagnostic.CA3061.severity = warning
|
|
||||||
|
|
||||||
# CA3075: Insecure DTD processing in XML
|
|
||||||
dotnet_diagnostic.CA3075.severity = warning
|
|
||||||
|
|
||||||
# CA3076: Insecure XSLT script processing
|
|
||||||
dotnet_diagnostic.CA3076.severity = warning
|
|
||||||
|
|
||||||
# CA3077: Insecure Processing in API Design, XmlDocument and XmlTextReader
|
|
||||||
dotnet_diagnostic.CA3077.severity = warning
|
|
||||||
|
|
||||||
# CA3147: Mark Verb Handlers With Validate Antiforgery Token
|
|
||||||
dotnet_diagnostic.CA3147.severity = warning
|
|
||||||
|
|
||||||
# CA5350: Do Not Use Weak Cryptographic Algorithms
|
|
||||||
dotnet_diagnostic.CA5350.severity = warning
|
|
||||||
|
|
||||||
# CA5351: Do Not Use Broken Cryptographic Algorithms
|
|
||||||
dotnet_diagnostic.CA5351.severity = warning
|
|
||||||
|
|
||||||
# CA5358: Review cipher mode usage with cryptography experts
|
|
||||||
dotnet_diagnostic.CA5358.severity = warning
|
|
||||||
|
|
||||||
# CA5359: Do Not Disable Certificate Validation
|
|
||||||
dotnet_diagnostic.CA5359.severity = warning
|
|
||||||
|
|
||||||
# CA5360: Do Not Call Dangerous Methods In Deserialization
|
|
||||||
dotnet_diagnostic.CA5360.severity = warning
|
|
||||||
|
|
||||||
# CA5361: Do Not Disable SChannel Use of Strong Crypto
|
|
||||||
dotnet_diagnostic.CA5361.severity = warning
|
|
||||||
|
|
||||||
# CA5362: Potential reference cycle in deserialized object graph
|
|
||||||
dotnet_diagnostic.CA5362.severity = warning
|
|
||||||
|
|
||||||
# CA5363: Do Not Disable Request Validation
|
|
||||||
dotnet_diagnostic.CA5363.severity = warning
|
|
||||||
|
|
||||||
# CA5364: Do Not Use Deprecated Security Protocols
|
|
||||||
dotnet_diagnostic.CA5364.severity = warning
|
|
||||||
|
|
||||||
# CA5365: Do Not Disable HTTP Header Checking
|
|
||||||
dotnet_diagnostic.CA5365.severity = warning
|
|
||||||
|
|
||||||
# CA5366: Use XmlReader for 'DataSet.ReadXml()'
|
|
||||||
dotnet_diagnostic.CA5366.severity = warning
|
|
||||||
|
|
||||||
# CA5367: Do Not Serialize Types With Pointer Fields
|
|
||||||
dotnet_diagnostic.CA5367.severity = warning
|
|
||||||
|
|
||||||
# CA5368: Set ViewStateUserKey For Classes Derived From Page
|
|
||||||
dotnet_diagnostic.CA5368.severity = warning
|
|
||||||
|
|
||||||
# CA5369: Use XmlReader for 'XmlSerializer.Deserialize()'
|
|
||||||
dotnet_diagnostic.CA5369.severity = warning
|
|
||||||
|
|
||||||
# CA5370: Use XmlReader for XmlValidatingReader constructor
|
|
||||||
dotnet_diagnostic.CA5370.severity = warning
|
|
||||||
|
|
||||||
# CA5371: Use XmlReader for 'XmlSchema.Read()'
|
|
||||||
dotnet_diagnostic.CA5371.severity = warning
|
|
||||||
|
|
||||||
# CA5372: Use XmlReader for XPathDocument constructor
|
|
||||||
dotnet_diagnostic.CA5372.severity = warning
|
|
||||||
|
|
||||||
# CA5373: Do not use obsolete key derivation function
|
|
||||||
dotnet_diagnostic.CA5373.severity = warning
|
|
||||||
|
|
||||||
# CA5374: Do Not Use XslTransform
|
|
||||||
dotnet_diagnostic.CA5374.severity = warning
|
|
||||||
|
|
||||||
# CA5375: Do Not Use Account Shared Access Signature
|
|
||||||
dotnet_diagnostic.CA5375.severity = warning
|
|
||||||
|
|
||||||
# CA5376: Use SharedAccessProtocol HttpsOnly
|
|
||||||
dotnet_diagnostic.CA5376.severity = warning
|
|
||||||
|
|
||||||
# CA5377: Use Container Level Access Policy
|
|
||||||
dotnet_diagnostic.CA5377.severity = warning
|
|
||||||
|
|
||||||
# CA5378: Do not disable ServicePointManagerSecurityProtocols
|
|
||||||
dotnet_diagnostic.CA5378.severity = warning
|
|
||||||
|
|
||||||
# CA5379: Ensure Key Derivation Function algorithm is sufficiently strong
|
|
||||||
dotnet_diagnostic.CA5379.severity = warning
|
|
||||||
|
|
||||||
# CA5380: Do Not Add Certificates To Root Store
|
|
||||||
dotnet_diagnostic.CA5380.severity = warning
|
|
||||||
|
|
||||||
# CA5381: Ensure Certificates Are Not Added To Root Store
|
|
||||||
dotnet_diagnostic.CA5381.severity = warning
|
|
||||||
|
|
||||||
# CA5382: Use Secure Cookies In ASP.NET Core
|
|
||||||
dotnet_diagnostic.CA5382.severity = warning
|
|
||||||
|
|
||||||
# CA5383: Ensure Use Secure Cookies In ASP.NET Core
|
|
||||||
dotnet_diagnostic.CA5383.severity = warning
|
|
||||||
|
|
||||||
# CA5384: Do Not Use Digital Signature Algorithm (DSA)
|
|
||||||
dotnet_diagnostic.CA5384.severity = warning
|
|
||||||
|
|
||||||
# CA5385: Use Rivest-Shamir-Adleman (RSA) Algorithm With Sufficient Key Size
|
|
||||||
dotnet_diagnostic.CA5385.severity = warning
|
|
||||||
|
|
||||||
# CA5386: Avoid hardcoding SecurityProtocolType value
|
|
||||||
dotnet_diagnostic.CA5386.severity = warning
|
|
||||||
|
|
||||||
# CA5387: Do Not Use Weak Key Derivation Function With Insufficient Iteration Count
|
|
||||||
dotnet_diagnostic.CA5387.severity = warning
|
|
||||||
|
|
||||||
# CA5388: Ensure Sufficient Iteration Count When Using Weak Key Derivation Function
|
|
||||||
dotnet_diagnostic.CA5388.severity = warning
|
|
||||||
|
|
||||||
# CA5389: Do Not Add Archive Item's Path To The Target File System Path
|
|
||||||
dotnet_diagnostic.CA5389.severity = warning
|
|
||||||
|
|
||||||
# CA5390: Do not hard-code encryption key
|
|
||||||
dotnet_diagnostic.CA5390.severity = warning
|
|
||||||
|
|
||||||
# CA5391: Use antiforgery tokens in ASP.NET Core MVC controllers
|
|
||||||
dotnet_diagnostic.CA5391.severity = warning
|
|
||||||
|
|
||||||
# CA5392: Use DefaultDllImportSearchPaths attribute for P/Invokes
|
|
||||||
dotnet_diagnostic.CA5392.severity = warning
|
|
||||||
|
|
||||||
# CA5393: Do not use unsafe DllImportSearchPath value
|
|
||||||
dotnet_diagnostic.CA5393.severity = warning
|
|
||||||
|
|
||||||
# CA5394: Do not use insecure randomness
|
|
||||||
dotnet_diagnostic.CA5394.severity = warning
|
|
||||||
|
|
||||||
# CA5395: Miss HttpVerb attribute for action methods
|
|
||||||
dotnet_diagnostic.CA5395.severity = warning
|
|
||||||
|
|
||||||
# CA5396: Set HttpOnly to true for HttpCookie
|
|
||||||
dotnet_diagnostic.CA5396.severity = warning
|
|
||||||
|
|
||||||
# CA5397: Do not use deprecated SslProtocols values
|
|
||||||
dotnet_diagnostic.CA5397.severity = warning
|
|
||||||
|
|
||||||
# CA5398: Avoid hardcoded SslProtocols values
|
|
||||||
dotnet_diagnostic.CA5398.severity = warning
|
|
||||||
|
|
||||||
# CA5399: HttpClients should enable certificate revocation list checks
|
|
||||||
dotnet_diagnostic.CA5399.severity = warning
|
|
||||||
|
|
||||||
# CA5400: Ensure HttpClient certificate revocation list check is not disabled
|
|
||||||
dotnet_diagnostic.CA5400.severity = warning
|
|
||||||
|
|
||||||
# CA5401: Do not use CreateEncryptor with non-default IV
|
|
||||||
dotnet_diagnostic.CA5401.severity = warning
|
|
||||||
|
|
||||||
# CA5402: Use CreateEncryptor with the default IV
|
|
||||||
dotnet_diagnostic.CA5402.severity = warning
|
|
||||||
|
|
||||||
# CA5403: Do not hard-code certificate
|
|
||||||
dotnet_diagnostic.CA5403.severity = warning
|
|
||||||
|
|
||||||
# CA5404: Do not disable token validation checks
|
|
||||||
dotnet_diagnostic.CA5404.severity = warning
|
|
||||||
|
|
||||||
# CA5405: Do not always skip token validation in delegates
|
|
||||||
dotnet_diagnostic.CA5405.severity = warning
|
|
||||||
|
|
||||||
# CA1032: Implement standard exception constructors
|
|
||||||
dotnet_diagnostic.CA1032.severity = warning
|
|
||||||
|
|
||||||
# CA1200: Avoid using cref tags with a prefix
|
|
||||||
dotnet_diagnostic.CA1200.severity = warning
|
|
||||||
|
|
||||||
# CA1309: Use ordinal string comparison
|
|
||||||
dotnet_diagnostic.CA1309.severity = warning
|
|
||||||
|
|
||||||
# CA1311: Specify a culture or use an invariant version
|
|
||||||
dotnet_diagnostic.CA1311.severity = warning
|
|
||||||
|
|
||||||
# CA1507: Use nameof to express symbol names
|
|
||||||
dotnet_diagnostic.CA1507.severity = warning
|
|
||||||
|
|
||||||
# CA1508: Avoid dead conditional code
|
|
||||||
dotnet_diagnostic.CA1508.severity = warning
|
|
||||||
|
|
||||||
# CA1802: Use literals where appropriate
|
|
||||||
dotnet_diagnostic.CA1802.severity = warning
|
|
||||||
|
|
||||||
# CA1805: Do not initialize unnecessarily
|
|
||||||
dotnet_diagnostic.CA1805.severity = warning
|
|
||||||
|
|
||||||
# CA1812: Avoid uninstantiated internal classes
|
|
||||||
dotnet_diagnostic.CA1812.severity = warning
|
|
||||||
|
|
||||||
# CA1824: Mark assemblies with NeutralResourcesLanguageAttribute
|
|
||||||
dotnet_diagnostic.CA1824.severity = warning
|
|
||||||
|
|
||||||
# CA1825: Avoid zero-length array allocations
|
|
||||||
dotnet_diagnostic.CA1825.severity = warning
|
|
||||||
|
|
||||||
# CA1841: Prefer Dictionary.Contains methods
|
|
||||||
dotnet_diagnostic.CA1841.severity = warning
|
|
||||||
|
|
||||||
# CA1845: Use span-based 'string.Concat'
|
|
||||||
dotnet_diagnostic.CA1845.severity = warning
|
|
||||||
|
|
||||||
# CA1851: Possible multiple enumerations of 'IEnumerable' collection
|
|
||||||
dotnet_diagnostic.CA1851.severity = warning
|
|
||||||
|
|
||||||
# CA1855: Prefer 'Clear' over 'Fill'
|
|
||||||
dotnet_diagnostic.CA1855.severity = warning
|
|
||||||
|
|
||||||
# CA1856: Incorrect usage of ConstantExpected attribute
|
|
||||||
dotnet_diagnostic.CA1856.severity = warning
|
|
||||||
|
|
||||||
# CA1857: A constant is expected for the parameter
|
|
||||||
dotnet_diagnostic.CA1857.severity = warning
|
|
||||||
|
|
||||||
# CA1865: Use char overload
|
|
||||||
dotnet_diagnostic.CA1865.severity = warning
|
|
||||||
|
|
||||||
# CA1866: Use char overload
|
|
||||||
dotnet_diagnostic.CA1866.severity = warning
|
|
||||||
|
|
||||||
# CA1867: Use char overload
|
|
||||||
dotnet_diagnostic.CA1867.severity = warning
|
|
||||||
|
|
||||||
# CA1870: Use a cached 'SearchValues' instance
|
|
||||||
dotnet_diagnostic.CA1870.severity = warning
|
|
||||||
|
|
||||||
# CA2014: Do not use stackalloc in loops
|
|
||||||
dotnet_diagnostic.CA2014.severity = warning
|
|
||||||
|
|
||||||
# CA2016: Forward the 'CancellationToken' parameter to methods
|
|
||||||
dotnet_diagnostic.CA2016.severity = warning
|
|
||||||
|
|
||||||
# CA2020: Prevent behavioral change
|
|
||||||
dotnet_diagnostic.CA2020.severity = warning
|
|
||||||
|
|
||||||
# CA2234: Pass system uri objects instead of strings
|
|
||||||
dotnet_diagnostic.CA2234.severity = warning
|
|
||||||
|
|
||||||
# CA2252: This API requires opting into preview features
|
|
||||||
dotnet_diagnostic.CA2252.severity = warning
|
|
||||||
|
|
||||||
# CA2260: Use correct type parameter
|
|
||||||
dotnet_diagnostic.CA2260.severity = warning
|
|
||||||
|
|
||||||
# CA2352: Unsafe DataSet or DataTable in serializable type can be vulnerable to remote code execution attacks
|
|
||||||
dotnet_diagnostic.CA2352.severity = warning
|
|
||||||
|
|
||||||
# CA2353: Unsafe DataSet or DataTable in serializable type
|
|
||||||
dotnet_diagnostic.CA2353.severity = warning
|
|
||||||
|
|
||||||
# CA2354: Unsafe DataSet or DataTable in deserialized object graph can be vulnerable to remote code execution attacks
|
|
||||||
dotnet_diagnostic.CA2354.severity = warning
|
|
||||||
|
|
||||||
# CA2355: Unsafe DataSet or DataTable type found in deserializable object graph
|
|
||||||
dotnet_diagnostic.CA2355.severity = warning
|
|
||||||
|
|
||||||
# CA2356: Unsafe DataSet or DataTable type in web deserializable object graph
|
|
||||||
dotnet_diagnostic.CA2356.severity = warning
|
|
||||||
|
|
||||||
# CA2362: Unsafe DataSet or DataTable in auto-generated serializable type can be vulnerable to remote code execution attacks
|
|
||||||
dotnet_diagnostic.CA2362.severity = warning
|
|
|
@ -1,21 +0,0 @@
|
||||||
name: DCO
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
dco:
|
|
||||||
name: DCO
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.21'
|
|
||||||
|
|
||||||
- name: Run commit format checker
|
|
||||||
uses: https://git.frostfs.info/TrueCloudLab/dco-go@v3
|
|
||||||
with:
|
|
||||||
from: 'origin/${{ github.event.pull_request.base.ref }}'
|
|
|
@ -1,22 +0,0 @@
|
||||||
name: lint-build
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lint-build:
|
|
||||||
name: dotnet${{ matrix.dotnet }}
|
|
||||||
runs-on: docker
|
|
||||||
container: git.frostfs.info/truecloudlab/env:dotnet-${{ matrix.dotnet }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
dotnet:
|
|
||||||
- '8.0'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# Dotnet runs code analyzers on build (if configured):
|
|
||||||
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview?tabs=net-8#enable-on-build
|
|
||||||
- run: dotnet build
|
|
36
.gitignore
vendored
36
.gitignore
vendored
|
@ -1,36 +0,0 @@
|
||||||
# Binaries for programs and plugins
|
|
||||||
*.exe
|
|
||||||
*.exe~
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
*.pdb
|
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
|
||||||
*.test
|
|
||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
|
||||||
*.out
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
vendor/
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.idea
|
|
||||||
.vscode
|
|
||||||
.vs
|
|
||||||
|
|
||||||
# coverage
|
|
||||||
coverage.txt
|
|
||||||
coverage.html
|
|
||||||
|
|
||||||
# antlr tool jar
|
|
||||||
antlr-*.jar
|
|
||||||
|
|
||||||
# tempfiles
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# binary
|
|
||||||
bin/
|
|
||||||
release/
|
|
||||||
obj/
|
|
|
@ -1,3 +0,0 @@
|
||||||
.* @PavelGrossSpb
|
|
||||||
.forgejo/.* @potyarkin
|
|
||||||
Makefile @potyarkin
|
|
|
@ -1,45 +0,0 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.5.002.0
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Client", "src\FrostFS.SDK.Client\FrostFS.SDK.Client.csproj", "{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Cryptography", "src\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj", "{3D804F4A-B0B2-47A5-B006-BE447BE64B50}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Protos", "src\FrostFS.SDK.Protos\FrostFS.SDK.Protos.csproj", "{5012EF96-9C9E-4E77-BC78-B4111EE54107}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrostFS.SDK.Tests", "src\FrostFS.SDK.Tests\FrostFS.SDK.Tests.csproj", "{8FDA7E0D-9C75-4874-988E-6592CD28F76C}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2F030ACD-F87C-4E83-9A68-4CC5DF03AD90}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
|
||||||
.editorconfig = .editorconfig
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{50D8F61F-C302-4AC9-8D8A-AB0B8C0988C3}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{3D804F4A-B0B2-47A5-B006-BE447BE64B50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{3D804F4A-B0B2-47A5-B006-BE447BE64B50}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{3D804F4A-B0B2-47A5-B006-BE447BE64B50}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{3D804F4A-B0B2-47A5-B006-BE447BE64B50}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{5012EF96-9C9E-4E77-BC78-B4111EE54107}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{8FDA7E0D-9C75-4874-988E-6592CD28F76C}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
73
LICENSE
73
LICENSE
|
@ -1,73 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2024 TrueCloudLab
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
83
README.md
83
README.md
|
@ -1,82 +1,3 @@
|
||||||
# frostfs-sdk-csharp
|
# WIP area: this repo is just a fork!
|
||||||
|
|
||||||
C# implementation of FrostFS SDK.
|
Useful things may be published only in [other branches](../../../branches)
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
### Get the key for your wallet
|
|
||||||
|
|
||||||
1. Get the address
|
|
||||||
```bash
|
|
||||||
cat <path_to_your_wallet> | jq .accounts[0].address | tr -d '"'
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Get the key
|
|
||||||
```bash
|
|
||||||
neo-go wallet export -w <path_to_your_wallet> -d <address_from_p1>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example usage
|
|
||||||
|
|
||||||
### Container
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using FrostFS.SDK;
|
|
||||||
using FrostFS.SDK.ClientV2;
|
|
||||||
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
var Key = "KwHDAJ66o8FoLBjVbjP2sWBmgBMGjt7Vv4boA7xQrBoAYBE397Aq";
|
|
||||||
var Host = "http://172.22.33.44:8080";
|
|
||||||
|
|
||||||
var options = Options.Create(new SingleOwnerClientSettings
|
|
||||||
{
|
|
||||||
Key = Key,
|
|
||||||
Host = Host
|
|
||||||
});
|
|
||||||
|
|
||||||
using var client = Client.GetSingleOwnerInstance(options);
|
|
||||||
|
|
||||||
await foreach (var cid in client.ListContainersAsync())
|
|
||||||
{
|
|
||||||
await client.DeleteContainerAsync(new PrmContainerDelete(cid));
|
|
||||||
}
|
|
||||||
|
|
||||||
var placementPolicy = new FrostFsPlacementPolicy(true, new FrostFsReplica(1));
|
|
||||||
|
|
||||||
var createContainerParam = new PrmContainerCreate(
|
|
||||||
new FrostFsContainerInfo(BasicAcl.PublicRW, new FrostFsPlacementPolicy(true, new FrostFsReplica(1))));
|
|
||||||
|
|
||||||
var containerId = await client.PutContainerAsync(createContainerParam);
|
|
||||||
|
|
||||||
using var fileStream = File.OpenRead(@"C:\Users\Paul\Pictures\cat.jpeg");
|
|
||||||
|
|
||||||
var param = new PrmObjectPut
|
|
||||||
{
|
|
||||||
Header = new FrostFsObjectHeader(
|
|
||||||
containerId: containerId,
|
|
||||||
type: FrostFsObjectType.Regular,
|
|
||||||
[new FrostFsAttribute("fileName", "test")]),
|
|
||||||
Payload = fileStream
|
|
||||||
};
|
|
||||||
|
|
||||||
FrostFsObjectId objectId = await client.PutObjectAsync(param);
|
|
||||||
|
|
||||||
var filter = new FilterByAttribute(FrostFsMatchType.Equals, "fileName", "test");
|
|
||||||
|
|
||||||
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId) { Filters = [filter] }))
|
|
||||||
{
|
|
||||||
var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(containerId, objId));
|
|
||||||
}
|
|
||||||
|
|
||||||
var @object = await client.GetObjectAsync(new PrmObjectGet(containerId, objectId));
|
|
||||||
|
|
||||||
var downloadedBytes = new byte[@object.Header.PayloadLength];
|
|
||||||
MemoryStream ms = new(downloadedBytes);
|
|
||||||
|
|
||||||
ReadOnlyMemory<byte>? chunk = null;
|
|
||||||
while ((chunk = await @object.ObjectReader!.ReadChunk()) != null)
|
|
||||||
{
|
|
||||||
ms.Write(chunk.Value.Span);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public struct Actions(bool inverted, string[] names) : System.IEquatable<Actions>
|
|
||||||
{
|
|
||||||
public bool Inverted { get; set; } = inverted;
|
|
||||||
|
|
||||||
public string[] Names { get; set; } = names;
|
|
||||||
|
|
||||||
public override readonly bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj == null || obj is not Actions)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return Equals((Actions)obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly int GetHashCode()
|
|
||||||
{
|
|
||||||
return Inverted.GetHashCode() ^ string.Join(string.Empty, Names).GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(Actions left, Actions right)
|
|
||||||
{
|
|
||||||
return left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(Actions left, Actions right)
|
|
||||||
{
|
|
||||||
return !(left == right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly bool Equals(Actions other)
|
|
||||||
{
|
|
||||||
return this.GetHashCode().Equals(other.GetHashCode());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using Frostfs.V2.Ape;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public struct FrostFsChainTarget(FrostFsTargetType type, string name) : IEquatable<FrostFsChainTarget>
|
|
||||||
{
|
|
||||||
private ChainTarget? chainTarget;
|
|
||||||
|
|
||||||
public FrostFsTargetType Type { get; } = type;
|
|
||||||
|
|
||||||
public string Name { get; } = name;
|
|
||||||
|
|
||||||
internal ChainTarget GetChainTarget()
|
|
||||||
{
|
|
||||||
return chainTarget ??= new ChainTarget
|
|
||||||
{
|
|
||||||
Type = GetTargetType(Type),
|
|
||||||
Name = Name
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TargetType GetTargetType(FrostFsTargetType type)
|
|
||||||
{
|
|
||||||
return type switch
|
|
||||||
{
|
|
||||||
FrostFsTargetType.Undefined => TargetType.Undefined,
|
|
||||||
FrostFsTargetType.Namespace => TargetType.Namespace,
|
|
||||||
FrostFsTargetType.Container => TargetType.Container,
|
|
||||||
FrostFsTargetType.User => TargetType.User,
|
|
||||||
FrostFsTargetType.Group => TargetType.Group,
|
|
||||||
_ => throw new ArgumentException("Unexpected value for TargetType", nameof(type)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj == null || obj is not FrostFsChainTarget)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return Equals((FrostFsChainTarget)obj);
|
|
||||||
}
|
|
||||||
public readonly bool Equals(FrostFsChainTarget other)
|
|
||||||
{
|
|
||||||
return Type == other.Type && Name.Equals(other.Name, StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly int GetHashCode()
|
|
||||||
{
|
|
||||||
return Name.GetHashCode() ^ (int)Type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(FrostFsChainTarget left, FrostFsChainTarget right)
|
|
||||||
{
|
|
||||||
return left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(FrostFsChainTarget left, FrostFsChainTarget right)
|
|
||||||
{
|
|
||||||
return !(left == right);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public struct Condition : System.IEquatable<Condition>
|
|
||||||
{
|
|
||||||
public ConditionType Op { get; set; }
|
|
||||||
|
|
||||||
public ConditionKindType Kind { get; set; }
|
|
||||||
|
|
||||||
public string? Key { get; set; }
|
|
||||||
|
|
||||||
public string? Value { get; set; }
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj == null || obj is not Condition)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return Equals((Condition)obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly int GetHashCode()
|
|
||||||
{
|
|
||||||
return Op.GetHashCode()
|
|
||||||
^ Kind.GetHashCode()
|
|
||||||
^ (Key != null ? Key.GetHashCode() : 0)
|
|
||||||
^ (Value != null ? Value.GetHashCode() : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(Condition left, Condition right)
|
|
||||||
{
|
|
||||||
return left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(Condition left, Condition right)
|
|
||||||
{
|
|
||||||
return !(left == right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly bool Equals(Condition other)
|
|
||||||
{
|
|
||||||
return this.GetHashCode().Equals(other.GetHashCode());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public enum ConditionKindType
|
|
||||||
{
|
|
||||||
Resource,
|
|
||||||
Request
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public enum ConditionType
|
|
||||||
{
|
|
||||||
CondStringEquals,
|
|
||||||
|
|
||||||
CondStringNotEquals,
|
|
||||||
CondStringEqualsIgnoreCase,
|
|
||||||
|
|
||||||
CondStringNotEqualsIgnoreCase,
|
|
||||||
CondStringLike,
|
|
||||||
|
|
||||||
CondStringNotLike,
|
|
||||||
CondStringLessThan,
|
|
||||||
|
|
||||||
CondStringLessThanEquals,
|
|
||||||
CondStringGreaterThan,
|
|
||||||
|
|
||||||
CondStringGreaterThanEquals,
|
|
||||||
|
|
||||||
// Numeric condition operators.
|
|
||||||
CondNumericEquals,
|
|
||||||
|
|
||||||
CondNumericNotEquals,
|
|
||||||
CondNumericLessThan,
|
|
||||||
|
|
||||||
CondNumericLessThanEquals,
|
|
||||||
CondNumericGreaterThan,
|
|
||||||
|
|
||||||
CondNumericGreaterThanEquals,
|
|
||||||
|
|
||||||
CondSliceContains,
|
|
||||||
|
|
||||||
CondIPAddress,
|
|
||||||
CondNotIPAddress,
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public enum FrostFsTargetType
|
|
||||||
{
|
|
||||||
Undefined,
|
|
||||||
Namespace,
|
|
||||||
Container,
|
|
||||||
User,
|
|
||||||
Group
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public enum RuleStatus
|
|
||||||
{
|
|
||||||
Allow,
|
|
||||||
NoRuleFound,
|
|
||||||
AccessDenied,
|
|
||||||
QuotaLimitReached
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public class FrostFsChain
|
|
||||||
{
|
|
||||||
public byte[] ID { get; set; } = [];
|
|
||||||
|
|
||||||
public FrostFsRule[] Rules { get; set; } = [];
|
|
||||||
|
|
||||||
public RuleMatchType MatchType { get; set; }
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public class FrostFsRule
|
|
||||||
{
|
|
||||||
public RuleStatus Status { get; set; }
|
|
||||||
|
|
||||||
// Actions the operation is applied to.
|
|
||||||
public Actions Actions { get; set; }
|
|
||||||
|
|
||||||
// List of the resources the operation is applied to.
|
|
||||||
public Resources Resources { get; set; }
|
|
||||||
|
|
||||||
// True iff individual conditions must be combined with the logical OR.
|
|
||||||
// By default AND is used, so _each_ condition must pass.
|
|
||||||
public bool Any { get; set; }
|
|
||||||
|
|
||||||
public Condition[]? Conditions { get; set; }
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public enum RuleMatchType
|
|
||||||
{
|
|
||||||
// DenyPriority rejects the request if any `Deny` is specified.
|
|
||||||
DenyPriority,
|
|
||||||
|
|
||||||
// FirstMatch returns the first rule action matched to the request.
|
|
||||||
FirstMatch
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public struct Resources(bool inverted, string[] names) : System.IEquatable<Resources>
|
|
||||||
{
|
|
||||||
public bool Inverted { get; set; } = inverted;
|
|
||||||
|
|
||||||
public string[] Names { get; set; } = names;
|
|
||||||
|
|
||||||
public override readonly bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj == null || obj is not Resources)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return Equals((Resources)obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly int GetHashCode()
|
|
||||||
{
|
|
||||||
return Inverted.GetHashCode() ^ string.Join(string.Empty, Names).GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(Resources left, Resources right)
|
|
||||||
{
|
|
||||||
return left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(Resources left, Resources right)
|
|
||||||
{
|
|
||||||
return !(left == right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly bool Equals(Resources other)
|
|
||||||
{
|
|
||||||
return this.GetHashCode().Equals(other.GetHashCode());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,505 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("FrostFS.SDK.Tests")]
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
internal static class RuleSerializer
|
|
||||||
{
|
|
||||||
const byte Version = 0; // increase if breaking change
|
|
||||||
const int ByteSize = 1;
|
|
||||||
const int UInt8Size = ByteSize;
|
|
||||||
const int BoolSize = ByteSize;
|
|
||||||
const long NullSlice = -1;
|
|
||||||
const int NullSliceSize = 1;
|
|
||||||
const byte ByteTrue = 1;
|
|
||||||
const byte ByteFalse = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// maxSliceLen taken from https://github.com/neo-project/neo/blob/38218bbee5bbe8b33cd8f9453465a19381c9a547/src/Neo/IO/Helper.cs#L77
|
|
||||||
/// </summary>
|
|
||||||
const int MaxSliceLen = 0x1000000;
|
|
||||||
|
|
||||||
const int ChainMarshalVersion = 0;
|
|
||||||
|
|
||||||
internal static byte[] Serialize(FrostFsChain chain)
|
|
||||||
{
|
|
||||||
int s = UInt8Size // Marshaller version
|
|
||||||
+ UInt8Size // Chain version
|
|
||||||
+ SliceSize(chain.ID, b => ByteSize)
|
|
||||||
+ SliceSize(chain.Rules, RuleSize)
|
|
||||||
+ UInt8Size; // MatchType
|
|
||||||
|
|
||||||
byte[] buf = new byte[s];
|
|
||||||
|
|
||||||
int offset = UInt8Marshal(buf, 0, Version);
|
|
||||||
offset = UInt8Marshal(buf, offset, ChainMarshalVersion);
|
|
||||||
offset = SliceMarshal(buf, offset, chain.ID, ByteMarshal);
|
|
||||||
offset = SliceMarshal(buf, offset, chain.Rules, MarshalRule);
|
|
||||||
offset = UInt8Marshal(buf, offset, (byte)chain.MatchType);
|
|
||||||
|
|
||||||
VerifyMarshal(buf, offset);
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static FrostFsChain Deserialize(byte[] data)
|
|
||||||
{
|
|
||||||
if (data is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
FrostFsChain chain = new();
|
|
||||||
|
|
||||||
var (offset, version) = UInt8Unmarshal(data, 0);
|
|
||||||
|
|
||||||
if (version != Version)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"unsupported marshaller version {version}");
|
|
||||||
}
|
|
||||||
|
|
||||||
(offset, byte chainVersion) = UInt8Unmarshal(data, offset);
|
|
||||||
|
|
||||||
if (chainVersion != ChainMarshalVersion)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"unsupported chain version {chainVersion}");
|
|
||||||
}
|
|
||||||
|
|
||||||
(offset, chain.ID) = SliceUnmarshal(data, offset, UInt8Unmarshal);
|
|
||||||
|
|
||||||
(offset, chain.Rules) = SliceUnmarshal(data, offset, UnmarshalRule);
|
|
||||||
|
|
||||||
(offset, var matchTypeV) = UInt8Unmarshal(data, offset);
|
|
||||||
|
|
||||||
chain.MatchType = (RuleMatchType)matchTypeV;
|
|
||||||
|
|
||||||
VerifyUnmarshal(data, offset);
|
|
||||||
|
|
||||||
return chain;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int Int64Size(long value)
|
|
||||||
{
|
|
||||||
// https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=92;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c
|
|
||||||
// and
|
|
||||||
// https://cs.opensource.google/go/go/+/master:src/encoding/binary/varint.go;l=41;drc=dac9b9ddbd5160c5f4552410f5f8281bd5eed38c
|
|
||||||
ulong ux = (ulong)value << 1;
|
|
||||||
if (value < 0)
|
|
||||||
{
|
|
||||||
ux = ~ux;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = 0;
|
|
||||||
while (ux >= 0x80)
|
|
||||||
{
|
|
||||||
size++;
|
|
||||||
ux >>= 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
return size + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int SliceSize<T>(T[] slice, Func<T, int> sizeOf)
|
|
||||||
{
|
|
||||||
if (slice == null)
|
|
||||||
{
|
|
||||||
return NullSliceSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assuming Int64Size is the size of the slice
|
|
||||||
var size = Int64Size(slice.Length);
|
|
||||||
foreach (var v in slice)
|
|
||||||
{
|
|
||||||
size += sizeOf(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int StringSize(string? s)
|
|
||||||
{
|
|
||||||
var len = s != null ? s.Length : 0;
|
|
||||||
return Int64Size(len) + len;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ActionsSize(Actions action)
|
|
||||||
{
|
|
||||||
return BoolSize // Inverted
|
|
||||||
+ SliceSize(action.Names, StringSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ResourcesSize(Resources resource)
|
|
||||||
{
|
|
||||||
return BoolSize // Inverted
|
|
||||||
+ SliceSize(resource.Names, StringSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ConditionSize(Condition condition)
|
|
||||||
{
|
|
||||||
return ByteSize // Op
|
|
||||||
+ ByteSize // Object
|
|
||||||
+ StringSize(condition.Key)
|
|
||||||
+ StringSize(condition.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int RuleSize(FrostFsRule rule)
|
|
||||||
{
|
|
||||||
if (rule is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(rule));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ByteSize // Status
|
|
||||||
+ ActionsSize(rule.Actions)
|
|
||||||
+ ResourcesSize(rule.Resources)
|
|
||||||
+ BoolSize // Any
|
|
||||||
+ SliceSize(rule.Conditions!, ConditionSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int UInt8Marshal(byte[] buf, int offset, byte value)
|
|
||||||
{
|
|
||||||
if (buf.Length - offset < 1)
|
|
||||||
{
|
|
||||||
throw new FrostFsException("Not enough bytes left to serialize value of type byte");
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[offset] = value;
|
|
||||||
|
|
||||||
return offset + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ByteMarshal(byte[] buf, int offset, byte value)
|
|
||||||
{
|
|
||||||
return UInt8Marshal(buf, offset, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutVarint encodes an int64 into buf and returns the number of bytes written.
|
|
||||||
// If the buffer is too small, PutVarint will panic.
|
|
||||||
private static int PutVarint(byte[] buf, int offset, long x)
|
|
||||||
{
|
|
||||||
var ux = (ulong)x << 1;
|
|
||||||
|
|
||||||
if (x < 0)
|
|
||||||
{
|
|
||||||
ux = ~ux;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PutUvarint(buf, offset, ux);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int PutUvarint(byte[] buf, int offset, ulong x)
|
|
||||||
{
|
|
||||||
while (x >= 0x80)
|
|
||||||
{
|
|
||||||
buf[offset] = (byte)(x | 0x80);
|
|
||||||
x >>= 7;
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[offset] = (byte)x;
|
|
||||||
|
|
||||||
return offset + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int Int64Marshal(byte[] buf, int offset, long v)
|
|
||||||
{
|
|
||||||
if (buf.Length - offset < Int64Size(v))
|
|
||||||
{
|
|
||||||
throw new FrostFsException("Not enough bytes left to serialize value of type long");
|
|
||||||
}
|
|
||||||
|
|
||||||
return PutVarint(buf, offset, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int SliceMarshal<T>(byte[] buf, int offset, T[] slice, Func<byte[], int, T, int> marshalT)
|
|
||||||
{
|
|
||||||
if (slice == null)
|
|
||||||
{
|
|
||||||
return Int64Marshal(buf, offset, NullSlice);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slice.Length > MaxSliceLen)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"slice size if too big: {slice.Length}");
|
|
||||||
}
|
|
||||||
|
|
||||||
offset = Int64Marshal(buf, offset, slice.Length);
|
|
||||||
|
|
||||||
foreach (var v in slice)
|
|
||||||
{
|
|
||||||
offset = marshalT(buf, offset, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int BoolMarshal(byte[] buf, int offset, bool value)
|
|
||||||
{
|
|
||||||
return UInt8Marshal(buf, offset, value ? ByteTrue : ByteFalse);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int StringMarshal(byte[] buf, int offset, string value)
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"string value is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.Length > MaxSliceLen)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"string is too long: {value.Length}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf.Length - offset < Int64Size(value.Length) + value.Length)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"Not enough bytes left to serialize value of type string with length {value.Length}");
|
|
||||||
}
|
|
||||||
|
|
||||||
offset = Int64Marshal(buf, offset, value.Length);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer.BlockCopy(System.Text.Encoding.UTF8.GetBytes(value), 0, buf, offset, value.Length);
|
|
||||||
|
|
||||||
return offset + value.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int MarshalActions(byte[] buf, int offset, Actions action)
|
|
||||||
{
|
|
||||||
offset = BoolMarshal(buf, offset, action.Inverted);
|
|
||||||
|
|
||||||
return SliceMarshal(buf, offset, action.Names, StringMarshal);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int MarshalCondition(byte[] buf, int offset, Condition condition)
|
|
||||||
{
|
|
||||||
offset = ByteMarshal(buf, offset, (byte)condition.Op);
|
|
||||||
|
|
||||||
offset = ByteMarshal(buf, offset, (byte)condition.Kind);
|
|
||||||
|
|
||||||
offset = StringMarshal(buf, offset, condition.Key!);
|
|
||||||
|
|
||||||
return StringMarshal(buf, offset, condition.Value!);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int MarshalRule(byte[] buf, int offset, FrostFsRule rule)
|
|
||||||
{
|
|
||||||
if (rule is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(rule));
|
|
||||||
}
|
|
||||||
|
|
||||||
offset = ByteMarshal(buf, offset, (byte)rule.Status);
|
|
||||||
|
|
||||||
offset = MarshalActions(buf, offset, rule.Actions);
|
|
||||||
|
|
||||||
offset = MarshalResources(buf, offset, rule.Resources);
|
|
||||||
|
|
||||||
offset = BoolMarshal(buf, offset, rule.Any);
|
|
||||||
|
|
||||||
return SliceMarshal(buf, offset, rule.Conditions!, MarshalCondition);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int MarshalResources(byte[] buf, int offset, Resources resources)
|
|
||||||
{
|
|
||||||
offset = BoolMarshal(buf, offset, resources.Inverted);
|
|
||||||
|
|
||||||
return SliceMarshal(buf, offset, resources.Names, StringMarshal);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void VerifyMarshal(byte[] buf, int lastOffset)
|
|
||||||
{
|
|
||||||
if (buf.Length != lastOffset)
|
|
||||||
{
|
|
||||||
throw new FrostFsException("actual data size differs from expected");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (int, bool) BoolUnmarshal(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
(offset, byte val) = UInt8Unmarshal(buf, offset);
|
|
||||||
return (offset, val == ByteTrue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (int, string) StringUnmarshal(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
(offset, long size) = Int64Unmarshal(buf, offset);
|
|
||||||
|
|
||||||
if (size == 0)
|
|
||||||
{
|
|
||||||
return (offset, string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size > MaxSliceLen)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"string is too long: '{size}'");
|
|
||||||
}
|
|
||||||
if (size < 0)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"invalid string size: '{size}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf.Length - offset < size)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"not enough bytes left to string value");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (offset + (int)size, System.Text.Encoding.UTF8.GetString(buf, offset, (int)size));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (int, Actions) UnmarshalActions(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
Actions action = new();
|
|
||||||
(offset, action.Inverted) = BoolUnmarshal(buf, offset);
|
|
||||||
|
|
||||||
(offset, action.Names) = SliceUnmarshal(buf, offset, StringUnmarshal);
|
|
||||||
|
|
||||||
return (offset, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (int, Resources) UnmarshalResources(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
Resources res = new();
|
|
||||||
|
|
||||||
(offset, res.Inverted) = BoolUnmarshal(buf, offset);
|
|
||||||
(offset, res.Names) = SliceUnmarshal(buf, offset, StringUnmarshal);
|
|
||||||
|
|
||||||
return (offset, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (int, Condition) UnmarshalCondition(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
Condition cond = new();
|
|
||||||
(offset, var op) = UInt8Unmarshal(buf, offset);
|
|
||||||
|
|
||||||
cond.Op = (ConditionType)op;
|
|
||||||
|
|
||||||
(offset, var kind) = UInt8Unmarshal(buf, offset);
|
|
||||||
|
|
||||||
cond.Kind = (ConditionKindType)kind;
|
|
||||||
|
|
||||||
(offset, cond.Key) = StringUnmarshal(buf, offset);
|
|
||||||
|
|
||||||
(offset, cond.Value) = StringUnmarshal(buf, offset);
|
|
||||||
|
|
||||||
return (offset, cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (int, FrostFsRule) UnmarshalRule(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
FrostFsRule rule = new();
|
|
||||||
|
|
||||||
(offset, byte statusV) = UInt8Unmarshal(buf, offset);
|
|
||||||
rule.Status = (RuleStatus)statusV;
|
|
||||||
|
|
||||||
(offset, rule.Actions) = UnmarshalActions(buf, offset);
|
|
||||||
|
|
||||||
(offset, rule.Resources) = UnmarshalResources(buf, offset);
|
|
||||||
|
|
||||||
(offset, rule.Any) = BoolUnmarshal(buf, offset);
|
|
||||||
|
|
||||||
(offset, rule.Conditions) = SliceUnmarshal(buf, offset, UnmarshalCondition);
|
|
||||||
|
|
||||||
return (offset, rule);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (int, byte) UInt8Unmarshal(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
if (buf.Length - offset < 1)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"not enough bytes left to read a value of type 'byte' from offset {offset}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (offset + 1, buf[offset]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (int, long) Int64Unmarshal(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
if (buf.Length - offset < sizeof(long))
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"not enough bytes left to read a value of type 'long' from offset {offset}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Varint(buf, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (int, T[]) SliceUnmarshal<T>(byte[] buf, int offset, Func<byte[], int, (int, T)> unmarshalT)
|
|
||||||
{
|
|
||||||
var (newOffset, size) = Varint(buf, offset);
|
|
||||||
|
|
||||||
if (size == NullSlice)
|
|
||||||
{
|
|
||||||
return (newOffset, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size > MaxSliceLen)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"slice size is too big: '{size}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size < 0)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"invalid slice size: '{size}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = new T[size];
|
|
||||||
for (int i = 0; i < result.Length; i++)
|
|
||||||
{
|
|
||||||
(newOffset, result[i]) = unmarshalT(buf, newOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (newOffset, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void VerifyUnmarshal(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
if (buf.Length != offset)
|
|
||||||
{
|
|
||||||
throw new FrostFsException("unmarshalled bytes left");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int MaxVarIntLen64 = 10;
|
|
||||||
|
|
||||||
public static (int, long) Varint(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
var (ux, n) = Uvarint(buf, offset); // ok to continue in presence of error
|
|
||||||
long x = (long)ux >> 1;
|
|
||||||
if ((ux & 1) != 0)
|
|
||||||
{
|
|
||||||
x = ~x;
|
|
||||||
}
|
|
||||||
return (n, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static (ulong, int) Uvarint(byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
ulong x = 0;
|
|
||||||
int s = 0;
|
|
||||||
|
|
||||||
for (int i = offset; i < buf.Length; i++)
|
|
||||||
{
|
|
||||||
byte b = buf[i];
|
|
||||||
if (i == MaxVarIntLen64)
|
|
||||||
{
|
|
||||||
return (0, -(i + 1)); // overflow
|
|
||||||
}
|
|
||||||
if (b < 0x80)
|
|
||||||
{
|
|
||||||
if (i == MaxVarIntLen64 - 1 && b > 1)
|
|
||||||
{
|
|
||||||
return (0, -(i + 1)); // overflow
|
|
||||||
}
|
|
||||||
return (x | ((ulong)b << s), i + 1);
|
|
||||||
}
|
|
||||||
x |= (ulong)(b & 0x7f) << s;
|
|
||||||
s += 7;
|
|
||||||
}
|
|
||||||
return (0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
[assembly: AssemblyCompany("FrostFS.SDK.Client")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.2.0")]
|
|
||||||
[assembly: AssemblyInformationalVersion("1.0.0+d6fe0344538a223303c9295452f0ad73681ca376")]
|
|
||||||
[assembly: AssemblyProduct("FrostFS.SDK.Client")]
|
|
||||||
[assembly: AssemblyTitle("FrostFS.SDK.Client")]
|
|
||||||
[assembly: AssemblyVersion("1.0.2")]
|
|
|
@ -1,22 +0,0 @@
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
internal static class Caches
|
|
||||||
{
|
|
||||||
private static readonly IMemoryCache _ownersCache = new MemoryCache(new MemoryCacheOptions
|
|
||||||
{
|
|
||||||
// TODO: get from options?
|
|
||||||
SizeLimit = 256
|
|
||||||
});
|
|
||||||
|
|
||||||
private static readonly IMemoryCache _containersCache = new MemoryCache(new MemoryCacheOptions
|
|
||||||
{
|
|
||||||
// TODO: get from options?
|
|
||||||
SizeLimit = 1024
|
|
||||||
});
|
|
||||||
|
|
||||||
internal static IMemoryCache Owners => _ownersCache;
|
|
||||||
|
|
||||||
internal static IMemoryCache Containers => _containersCache;
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public class ClientKey(ECDsa key)
|
|
||||||
{
|
|
||||||
internal ECDsa ECDsaKey { get; } = key;
|
|
||||||
|
|
||||||
internal ByteString PublicKeyProto { get; } = ByteString.CopyFrom(key.PublicKey());
|
|
||||||
|
|
||||||
internal string PublicKey { get; } = Base58.Encode(key.PublicKey());
|
|
||||||
|
|
||||||
internal FrostFsOwner Owner { get; } = new FrostFsOwner(key.PublicKey().PublicKeyToAddress());
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public class FrostFsException : Exception
|
|
||||||
{
|
|
||||||
public FrostFsException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsException(string message, Exception innerException) : base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public class FrostFsInvalidObjectException : FrostFsException
|
|
||||||
{
|
|
||||||
public FrostFsInvalidObjectException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsInvalidObjectException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsInvalidObjectException(string message, Exception innerException) : base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public class FrostFsResponseException : FrostFsException
|
|
||||||
{
|
|
||||||
public FrostFsResponseStatus? Status { get; private set; }
|
|
||||||
|
|
||||||
public FrostFsResponseException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsResponseException(FrostFsResponseStatus status)
|
|
||||||
: base(status != null ? status.Message != null ? "" : "" : "")
|
|
||||||
{
|
|
||||||
Status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsResponseException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsResponseException(string message, Exception innerException) : base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public class FrostFsStreamException : FrostFsException
|
|
||||||
{
|
|
||||||
public FrostFsStreamException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsStreamException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsStreamException(string message, Exception innerException) : base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public class SessionExpiredException : FrostFsException
|
|
||||||
{
|
|
||||||
public SessionExpiredException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionExpiredException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionExpiredException(string message, Exception innerException) : base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public class SessionNotFoundException : FrostFsException
|
|
||||||
{
|
|
||||||
public SessionNotFoundException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionNotFoundException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionNotFoundException(string message, Exception innerException) : base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
public static class FrostFsExtensions
|
|
||||||
{
|
|
||||||
public static ByteString Sha256(this IMessage data)
|
|
||||||
{
|
|
||||||
return ByteString.CopyFrom(data.ToByteArray().Sha256());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Guid ToUuid(this ByteString id)
|
|
||||||
{
|
|
||||||
if (id == null)
|
|
||||||
throw new ArgumentNullException(nameof(id));
|
|
||||||
|
|
||||||
var orderedBytes = GetGuidBytesDirectOrder(id.Span);
|
|
||||||
|
|
||||||
return new Guid(orderedBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Serializes Guid to binary representation in direct order bytes format
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static byte[] ToBytes(this Guid id)
|
|
||||||
{
|
|
||||||
var bytes = id.ToByteArray();
|
|
||||||
|
|
||||||
var orderedBytes = GetGuidBytesDirectOrder(bytes);
|
|
||||||
|
|
||||||
return orderedBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] GetGuidBytesDirectOrder(ReadOnlySpan<byte> source)
|
|
||||||
{
|
|
||||||
if (source.Length != 16)
|
|
||||||
throw new ArgumentException("Wrong uuid binary format");
|
|
||||||
|
|
||||||
return [
|
|
||||||
source[3],
|
|
||||||
source[2],
|
|
||||||
source[1],
|
|
||||||
source[0],
|
|
||||||
source[5],
|
|
||||||
source[4],
|
|
||||||
source[7],
|
|
||||||
source[6],
|
|
||||||
source[8],
|
|
||||||
source[9],
|
|
||||||
source[10],
|
|
||||||
source[11],
|
|
||||||
source[12],
|
|
||||||
source[13],
|
|
||||||
source[14],
|
|
||||||
source[15]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
|
||||||
<LangVersion>12.0</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<EnableNETAnalyzers>true</EnableNETAnalyzers>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<_SkipUpgradeNetAnalyzersNuGetWarning>true</_SkipUpgradeNetAnalyzersNuGetWarning>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<GeneratedAssemblyInfoFile>Assemblyinfo.cs</GeneratedAssemblyInfoFile>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="CommunityToolkit.HighPerformance" Version="7.1.2" />
|
|
||||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="7.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="7.0.4">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
|
|
||||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="7.0.0" />
|
|
||||||
<PackageReference Include="System.Runtime.Caching" Version="7.0.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\FrostFS.SDK.Cryptography\FrostFS.SDK.Cryptography.csproj" />
|
|
||||||
<ProjectReference Include="..\FrostFS.SDK.Protos\FrostFS.SDK.Protos.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,464 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Client.Interfaces;
|
|
||||||
using FrostFS.SDK.Client.Services;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
using FrostFS.Session;
|
|
||||||
|
|
||||||
using Grpc.Core;
|
|
||||||
using Grpc.Core.Interceptors;
|
|
||||||
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
using static Frostfs.V2.Apemanager.APEManagerService;
|
|
||||||
using static FrostFS.Accounting.AccountingService;
|
|
||||||
using static FrostFS.Container.ContainerService;
|
|
||||||
using static FrostFS.Netmap.NetmapService;
|
|
||||||
using static FrostFS.Object.ObjectService;
|
|
||||||
using static FrostFS.Session.SessionService;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public class FrostFSClient : IFrostFSClient
|
|
||||||
{
|
|
||||||
internal ContainerServiceClient? ContainerServiceClient { get; set; }
|
|
||||||
internal ContainerServiceProvider? ContainerServiceProvider { get; set; }
|
|
||||||
|
|
||||||
internal NetmapServiceClient? NetmapServiceClient { get; set; }
|
|
||||||
internal NetmapServiceProvider? NetmapServiceProvider { get; set; }
|
|
||||||
|
|
||||||
internal APEManagerServiceClient? ApeManagerServiceClient { get; set; }
|
|
||||||
internal ApeManagerServiceProvider? ApeManagerServiceProvider { get; set; }
|
|
||||||
|
|
||||||
internal SessionServiceClient? SessionServiceClient { get; set; }
|
|
||||||
internal SessionServiceProvider? SessionServiceProvider { get; set; }
|
|
||||||
|
|
||||||
internal ObjectServiceClient? ObjectServiceClient { get; set; }
|
|
||||||
internal ObjectServiceProvider? ObjectServiceProvider { get; set; }
|
|
||||||
|
|
||||||
internal AccountingServiceClient? AccountingServiceClient { get; set; }
|
|
||||||
internal AccountingServiceProvider? AccountingServiceProvider { get; set; }
|
|
||||||
|
|
||||||
internal ClientContext ClientCtx { get; set; }
|
|
||||||
|
|
||||||
public static IFrostFSClient GetInstance(IOptions<ClientSettings> clientOptions, Func<string, ChannelBase> grpcChannelFactory)
|
|
||||||
{
|
|
||||||
if (clientOptions is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(clientOptions));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (grpcChannelFactory is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(grpcChannelFactory));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FrostFSClient(clientOptions, grpcChannelFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="clientOptions">Global setting for client</param>
|
|
||||||
/// <param name="channelOptions">Setting for gRPC channel</param>
|
|
||||||
/// <param name="containerService">ContainerService.ContainerServiceClient implementation</param>
|
|
||||||
/// <param name="netmapService">Netmap.NetmapService.NetmapServiceClient implementation</param>
|
|
||||||
/// <param name="sessionService">Session.SessionService.SessionServiceClient implementation</param>
|
|
||||||
/// <param name="objectService">Object.ObjectService.ObjectServiceClient implementation</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static IFrostFSClient GetTestInstance(
|
|
||||||
IOptions<ClientSettings> settings,
|
|
||||||
Func<string, ChannelBase> grpcChannelFactory,
|
|
||||||
NetmapServiceClient netmapService,
|
|
||||||
SessionServiceClient sessionService,
|
|
||||||
ContainerServiceClient containerService,
|
|
||||||
ObjectServiceClient objectService)
|
|
||||||
{
|
|
||||||
if (settings is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(settings));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (grpcChannelFactory is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(grpcChannelFactory));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (netmapService is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(netmapService));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionService is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(sessionService));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containerService is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(containerService));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectService is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(objectService));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FrostFSClient(
|
|
||||||
settings, channel: grpcChannelFactory(settings.Value.Host), containerService, netmapService, sessionService, objectService);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FrostFSClient(
|
|
||||||
IOptions<ClientSettings> settings,
|
|
||||||
ChannelBase channel,
|
|
||||||
ContainerServiceClient containerService,
|
|
||||||
NetmapServiceClient netmapService,
|
|
||||||
SessionServiceClient sessionService,
|
|
||||||
ObjectServiceClient objectService)
|
|
||||||
{
|
|
||||||
if (settings is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(settings));
|
|
||||||
}
|
|
||||||
|
|
||||||
var ecdsaKey = settings.Value.Key.LoadWif();
|
|
||||||
|
|
||||||
ClientCtx = new ClientContext(
|
|
||||||
client: this,
|
|
||||||
key: new ClientKey(ecdsaKey),
|
|
||||||
owner: FrostFsOwner.FromKey(ecdsaKey),
|
|
||||||
channel: channel,
|
|
||||||
version: new FrostFsVersion(2, 13))
|
|
||||||
{
|
|
||||||
SessionCache = new SessionCache(0),
|
|
||||||
Callback = settings.Value.Callback,
|
|
||||||
Interceptors = settings.Value.Interceptors
|
|
||||||
};
|
|
||||||
|
|
||||||
ContainerServiceClient = containerService ?? throw new ArgumentNullException(nameof(containerService));
|
|
||||||
NetmapServiceClient = netmapService ?? throw new ArgumentNullException(nameof(netmapService));
|
|
||||||
SessionServiceClient = sessionService ?? throw new ArgumentNullException(nameof(sessionService));
|
|
||||||
ObjectServiceClient = objectService ?? throw new ArgumentNullException(nameof(objectService));
|
|
||||||
}
|
|
||||||
|
|
||||||
private FrostFSClient(IOptions<ClientSettings> settings, Func<string, ChannelBase> grpcChannelFactory)
|
|
||||||
{
|
|
||||||
var clientSettings = (settings?.Value) ?? throw new ArgumentNullException(nameof(settings), "Options value must be initialized");
|
|
||||||
|
|
||||||
clientSettings.Validate();
|
|
||||||
|
|
||||||
var ecdsaKey = clientSettings.Key.LoadWif();
|
|
||||||
|
|
||||||
ClientCtx = new ClientContext(
|
|
||||||
this,
|
|
||||||
key: new ClientKey(ecdsaKey),
|
|
||||||
owner: FrostFsOwner.FromKey(ecdsaKey),
|
|
||||||
channel: grpcChannelFactory(settings.Value.Host),
|
|
||||||
version: new FrostFsVersion(2, 13))
|
|
||||||
{
|
|
||||||
SessionCache = new SessionCache(0),
|
|
||||||
Callback = settings.Value.Callback,
|
|
||||||
Interceptors = settings.Value.Interceptors
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: define timeout logic
|
|
||||||
// CheckFrostFsVersionSupport(new Context { Timeout = TimeSpan.FromSeconds(20) });
|
|
||||||
}
|
|
||||||
|
|
||||||
internal FrostFSClient(WrapperPrm prm, SessionCache cache)
|
|
||||||
{
|
|
||||||
ClientCtx = new ClientContext(
|
|
||||||
client: this,
|
|
||||||
key: new ClientKey(prm.Key),
|
|
||||||
owner: FrostFsOwner.FromKey(prm.Key!),
|
|
||||||
channel: prm.GrpcChannelFactory(prm.Address),
|
|
||||||
version: new FrostFsVersion(2, 13))
|
|
||||||
{
|
|
||||||
SessionCache = cache,
|
|
||||||
Interceptors = prm.Interceptors,
|
|
||||||
Callback = prm.Callback
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#region ApeManagerImplementation
|
|
||||||
public Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetApeManagerService().AddChainAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task RemoveChainAsync(PrmApeChainRemove args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetApeManagerService().RemoveChainAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<FrostFsChain[]> ListChainAsync(PrmApeChainList args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetApeManagerService().ListChainAsync(args, ctx);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region ContainerImplementation
|
|
||||||
public Task<FrostFsContainerInfo> GetContainerAsync(PrmContainerGet args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetContainerService().GetContainerAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetContainerService().ListContainersAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("Use PutContainerAsync method")]
|
|
||||||
public Task<FrostFsContainerId> CreateContainerAsync(PrmContainerCreate args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetContainerService().PutContainerAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<FrostFsContainerId> PutContainerAsync(PrmContainerCreate args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetContainerService().PutContainerAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DeleteContainerAsync(PrmContainerDelete args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetContainerService().DeleteContainerAsync(args, ctx);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region NetworkImplementation
|
|
||||||
public Task<FrostFsNetmapSnapshot> GetNetmapSnapshotAsync(CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetNetmapService().GetNetmapSnapshotAsync(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<FrostFsNodeInfo> GetNodeInfoAsync(CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetNetmapService().GetLocalNodeInfoAsync(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<NetworkSettings> GetNetworkSettingsAsync(CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetNetmapService().GetNetworkSettingsAsync(ctx);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region ObjectImplementation
|
|
||||||
public Task<FrostFsHeaderResult> GetObjectHeadAsync(PrmObjectHeadGet args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetObjectService().GetObjectHeadAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<FrostFsObject> GetObjectAsync(PrmObjectGet args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetObjectService().GetObjectAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<RangeReader> GetRangeAsync(PrmRangeGet args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetObjectService().GetRangeAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<ReadOnlyMemory<byte>[]> GetRangeHashAsync(PrmRangeHashGet args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetObjectService().GetRangeHashAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IObjectWriter> PutObjectAsync(PrmObjectPut args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetObjectService().PutStreamObjectAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<FrostFsObjectId> PutClientCutObjectAsync(PrmObjectClientCutPut args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetObjectService().PutClientCutObjectAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<FrostFsObjectId> PutSingleObjectAsync(PrmSingleObjectPut args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetObjectService().PutSingleObjectAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetObjectService().PatchObjectAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DeleteObjectAsync(PrmObjectDelete args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetObjectService().DeleteObjectAsync(args, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IAsyncEnumerable<FrostFsObjectId> SearchObjectsAsync(PrmObjectSearch args, CallContext ctx)
|
|
||||||
{
|
|
||||||
return GetObjectService().SearchObjectsAsync(args, ctx);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Session Implementation
|
|
||||||
public async Task<FrostFsSessionToken> CreateSessionAsync(PrmSessionCreate args, CallContext ctx)
|
|
||||||
{
|
|
||||||
var token = await CreateSessionInternalAsync(args, ctx).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return new FrostFsSessionToken(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Task<SessionToken> CreateSessionInternalAsync(PrmSessionCreate args, CallContext ctx)
|
|
||||||
{
|
|
||||||
var service = GetSessionService();
|
|
||||||
return service.CreateSessionAsync(args, ctx);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Accounting Implementation
|
|
||||||
public async Task<Accounting.Decimal> GetBalanceAsync(CallContext ctx)
|
|
||||||
{
|
|
||||||
return await GetAccouningService().GetBallance(ctx).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private CallInvoker? CreateInvoker()
|
|
||||||
{
|
|
||||||
CallInvoker? callInvoker = null;
|
|
||||||
|
|
||||||
if (ClientCtx.Interceptors != null)
|
|
||||||
{
|
|
||||||
foreach (var interceptor in ClientCtx.Interceptors)
|
|
||||||
callInvoker = AddInvoker(callInvoker, interceptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ClientCtx.Callback != null)
|
|
||||||
callInvoker = AddInvoker(callInvoker, new MetricsInterceptor(ClientCtx.Callback));
|
|
||||||
|
|
||||||
if (ClientCtx.PoolErrorHandler != null)
|
|
||||||
callInvoker = AddInvoker(callInvoker, new ErrorInterceptor(ClientCtx.PoolErrorHandler));
|
|
||||||
|
|
||||||
return callInvoker;
|
|
||||||
|
|
||||||
CallInvoker AddInvoker(CallInvoker? callInvoker, Interceptor interceptor)
|
|
||||||
{
|
|
||||||
if (callInvoker == null)
|
|
||||||
callInvoker = ClientCtx.Channel.Intercept(interceptor);
|
|
||||||
else
|
|
||||||
callInvoker = callInvoker.Intercept(interceptor);
|
|
||||||
|
|
||||||
return callInvoker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private NetmapServiceProvider GetNetmapService()
|
|
||||||
{
|
|
||||||
if (NetmapServiceProvider == null)
|
|
||||||
{
|
|
||||||
var invoker = CreateInvoker();
|
|
||||||
|
|
||||||
NetmapServiceClient ??= (
|
|
||||||
invoker != null
|
|
||||||
? new NetmapServiceClient(invoker)
|
|
||||||
: new NetmapServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
NetmapServiceProvider = new NetmapServiceProvider(NetmapServiceClient, ClientCtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NetmapServiceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SessionServiceProvider GetSessionService()
|
|
||||||
{
|
|
||||||
if (SessionServiceProvider == null)
|
|
||||||
{
|
|
||||||
var invoker = CreateInvoker();
|
|
||||||
|
|
||||||
SessionServiceClient ??= (
|
|
||||||
invoker != null
|
|
||||||
? new SessionServiceClient(invoker)
|
|
||||||
: new SessionServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
SessionServiceProvider = new SessionServiceProvider(SessionServiceClient, ClientCtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SessionServiceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ApeManagerServiceProvider GetApeManagerService()
|
|
||||||
{
|
|
||||||
if (ApeManagerServiceProvider == null)
|
|
||||||
{
|
|
||||||
var invoker = CreateInvoker();
|
|
||||||
|
|
||||||
ApeManagerServiceClient ??= (
|
|
||||||
invoker != null
|
|
||||||
? new APEManagerServiceClient(invoker)
|
|
||||||
: new APEManagerServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
ApeManagerServiceProvider = new ApeManagerServiceProvider(ApeManagerServiceClient, ClientCtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApeManagerServiceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AccountingServiceProvider GetAccouningService()
|
|
||||||
{
|
|
||||||
if (this.AccountingServiceProvider == null)
|
|
||||||
{
|
|
||||||
var invoker = CreateInvoker();
|
|
||||||
|
|
||||||
AccountingServiceClient ??= (
|
|
||||||
invoker != null
|
|
||||||
? new AccountingServiceClient(invoker)
|
|
||||||
: new AccountingServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
AccountingServiceProvider = new AccountingServiceProvider(AccountingServiceClient, ClientCtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return AccountingServiceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ContainerServiceProvider GetContainerService()
|
|
||||||
{
|
|
||||||
if (this.ContainerServiceProvider == null)
|
|
||||||
{
|
|
||||||
var invoker = CreateInvoker();
|
|
||||||
|
|
||||||
ContainerServiceClient ??= (
|
|
||||||
invoker != null
|
|
||||||
? new ContainerServiceClient(invoker)
|
|
||||||
: new ContainerServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
ContainerServiceProvider = new ContainerServiceProvider(ContainerServiceClient, ClientCtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ContainerServiceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectServiceProvider GetObjectService()
|
|
||||||
{
|
|
||||||
if (this.ObjectServiceProvider == null)
|
|
||||||
{
|
|
||||||
var invoker = CreateInvoker();
|
|
||||||
|
|
||||||
ObjectServiceClient ??= (
|
|
||||||
invoker != null
|
|
||||||
? new ObjectServiceClient(invoker)
|
|
||||||
: new ObjectServiceClient(ClientCtx.Channel));
|
|
||||||
|
|
||||||
ObjectServiceProvider = new ObjectServiceProvider(ObjectServiceClient, ClientCtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ObjectServiceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string?> Dial(CallContext ctx)
|
|
||||||
{
|
|
||||||
var service = GetAccouningService();
|
|
||||||
_ = await service.GetBallance(ctx).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RestartIfUnhealthy(CallContext ctx)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
// This file is used by Code Analysis to maintain SuppressMessage
|
|
||||||
// attributes that are applied to this project.
|
|
||||||
// Project-level suppressions either have no target or are given
|
|
||||||
// a specific target and scoped to a namespace, type, member, etc.
|
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
[assembly: SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "<Pending>", Scope = "member", Target = "~M:FrostFS.SDK.Client.Sampler.Next~System.Int32")]
|
|
|
@ -1,68 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Grpc.Core;
|
|
||||||
using Grpc.Core.Interceptors;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods",
|
|
||||||
Justification = "parameters are provided by GRPC infrastructure")]
|
|
||||||
public class ErrorInterceptor(Action<Exception> handler) : Interceptor
|
|
||||||
{
|
|
||||||
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
|
|
||||||
TRequest request,
|
|
||||||
ClientInterceptorContext<TRequest, TResponse> context,
|
|
||||||
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
|
|
||||||
{
|
|
||||||
var call = continuation(request, context);
|
|
||||||
|
|
||||||
return new AsyncUnaryCall<TResponse>(
|
|
||||||
HandleUnaryResponse(call),
|
|
||||||
call.ResponseHeadersAsync,
|
|
||||||
call.GetStatus,
|
|
||||||
call.GetTrailers,
|
|
||||||
call.Dispose);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(
|
|
||||||
ClientInterceptorContext<TRequest, TResponse> context,
|
|
||||||
AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
|
|
||||||
{
|
|
||||||
var call = continuation(context);
|
|
||||||
|
|
||||||
return new AsyncClientStreamingCall<TRequest, TResponse>(
|
|
||||||
call.RequestStream,
|
|
||||||
HandleStreamResponse(call),
|
|
||||||
call.ResponseHeadersAsync,
|
|
||||||
call.GetStatus,
|
|
||||||
call.GetTrailers,
|
|
||||||
call.Dispose);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<TResponse> HandleUnaryResponse<TResponse>(AsyncUnaryCall<TResponse> call)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await call;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
handler(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<TResponse> HandleStreamResponse<TRequest, TResponse>(AsyncClientStreamingCall<TRequest, TResponse> call)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await call;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
handler(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using Grpc.Core;
|
|
||||||
using Grpc.Core.Interceptors;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods",
|
|
||||||
Justification = "parameters are provided by GRPC infrastructure")]
|
|
||||||
public class MetricsInterceptor(Action<CallStatistics> callback) : Interceptor
|
|
||||||
{
|
|
||||||
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
|
|
||||||
TRequest request,
|
|
||||||
ClientInterceptorContext<TRequest, TResponse> context,
|
|
||||||
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
|
|
||||||
{
|
|
||||||
var call = continuation(request, context);
|
|
||||||
|
|
||||||
return new AsyncUnaryCall<TResponse>(
|
|
||||||
HandleUnaryResponse(call),
|
|
||||||
call.ResponseHeadersAsync,
|
|
||||||
call.GetStatus,
|
|
||||||
call.GetTrailers,
|
|
||||||
call.Dispose);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(
|
|
||||||
ClientInterceptorContext<TRequest, TResponse> context,
|
|
||||||
AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
|
|
||||||
{
|
|
||||||
var call = continuation(context);
|
|
||||||
|
|
||||||
return new AsyncClientStreamingCall<TRequest, TResponse>(
|
|
||||||
call.RequestStream,
|
|
||||||
HandleStreamResponse(call),
|
|
||||||
call.ResponseHeadersAsync,
|
|
||||||
call.GetStatus,
|
|
||||||
call.GetTrailers,
|
|
||||||
call.Dispose);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<TResponse> HandleUnaryResponse<TResponse>(AsyncUnaryCall<TResponse> call)
|
|
||||||
{
|
|
||||||
var watch = new Stopwatch();
|
|
||||||
watch.Start();
|
|
||||||
|
|
||||||
var response = await call;
|
|
||||||
|
|
||||||
watch.Stop();
|
|
||||||
|
|
||||||
var elapsed = watch.ElapsedTicks * 1_000_000 / Stopwatch.Frequency;
|
|
||||||
|
|
||||||
callback(new CallStatistics { MethodName = call.ToString(), ElapsedMicroSeconds = elapsed });
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<TResponse> HandleStreamResponse<TRequest, TResponse>(AsyncClientStreamingCall<TRequest, TResponse> call)
|
|
||||||
{
|
|
||||||
var watch = new Stopwatch();
|
|
||||||
watch.Start();
|
|
||||||
|
|
||||||
var response = await call;
|
|
||||||
|
|
||||||
watch.Stop();
|
|
||||||
|
|
||||||
var elapsed = watch.ElapsedTicks * 1_000_000 / Stopwatch.Frequency;
|
|
||||||
|
|
||||||
callback(new CallStatistics { MethodName = call.ToString(), ElapsedMicroSeconds = elapsed });
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Interfaces;
|
|
||||||
|
|
||||||
public interface IFrostFSClient
|
|
||||||
{
|
|
||||||
#region Network
|
|
||||||
Task<FrostFsNetmapSnapshot> GetNetmapSnapshotAsync(CallContext ctx);
|
|
||||||
|
|
||||||
Task<FrostFsNodeInfo> GetNodeInfoAsync(CallContext ctx);
|
|
||||||
|
|
||||||
Task<NetworkSettings> GetNetworkSettingsAsync(CallContext ctx);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Session
|
|
||||||
Task<FrostFsSessionToken> CreateSessionAsync(PrmSessionCreate args, CallContext ctx);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region ApeManager
|
|
||||||
Task<ReadOnlyMemory<byte>> AddChainAsync(PrmApeChainAdd args, CallContext ctx);
|
|
||||||
|
|
||||||
Task RemoveChainAsync(PrmApeChainRemove args, CallContext ctx);
|
|
||||||
|
|
||||||
Task<FrostFsChain[]> ListChainAsync(PrmApeChainList args, CallContext ctx);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Container
|
|
||||||
Task<FrostFsContainerInfo> GetContainerAsync(PrmContainerGet args, CallContext ctx);
|
|
||||||
|
|
||||||
IAsyncEnumerable<FrostFsContainerId> ListContainersAsync(PrmContainerGetAll args, CallContext ctx);
|
|
||||||
|
|
||||||
[Obsolete("Use PutContainerAsync method")]
|
|
||||||
Task<FrostFsContainerId> CreateContainerAsync(PrmContainerCreate args, CallContext ctx);
|
|
||||||
|
|
||||||
Task<FrostFsContainerId> PutContainerAsync(PrmContainerCreate args, CallContext ctx);
|
|
||||||
|
|
||||||
Task DeleteContainerAsync(PrmContainerDelete args, CallContext ctx);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Object
|
|
||||||
Task<FrostFsHeaderResult> GetObjectHeadAsync(PrmObjectHeadGet args, CallContext ctx);
|
|
||||||
|
|
||||||
Task<FrostFsObject> GetObjectAsync(PrmObjectGet args, CallContext ctx);
|
|
||||||
|
|
||||||
Task<RangeReader> GetRangeAsync(PrmRangeGet args, CallContext ctx);
|
|
||||||
|
|
||||||
Task<ReadOnlyMemory<byte>[]> GetRangeHashAsync(PrmRangeHashGet args, CallContext ctx);
|
|
||||||
|
|
||||||
Task<IObjectWriter> PutObjectAsync(PrmObjectPut args, CallContext ctx);
|
|
||||||
|
|
||||||
Task<FrostFsObjectId> PutClientCutObjectAsync(PrmObjectClientCutPut args, CallContext ctx);
|
|
||||||
|
|
||||||
Task<FrostFsObjectId> PutSingleObjectAsync(PrmSingleObjectPut args, CallContext ctx);
|
|
||||||
|
|
||||||
Task<FrostFsObjectId> PatchObjectAsync(PrmObjectPatch args, CallContext ctx);
|
|
||||||
|
|
||||||
Task DeleteObjectAsync(PrmObjectDelete args, CallContext ctx);
|
|
||||||
|
|
||||||
IAsyncEnumerable<FrostFsObjectId> SearchObjectsAsync(PrmObjectSearch args, CallContext ctx);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Account
|
|
||||||
Task<Accounting.Decimal> GetBalanceAsync(CallContext ctx);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public Task<string?> Dial(CallContext ctx);
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Interfaces
|
|
||||||
{
|
|
||||||
public interface IObjectWriter : IDisposable
|
|
||||||
{
|
|
||||||
Task WriteAsync(ReadOnlyMemory<byte> memory);
|
|
||||||
|
|
||||||
Task<FrostFsObjectId> CompleteAsync();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
internal static partial class FrostFsMessages
|
|
||||||
{
|
|
||||||
[LoggerMessage(100,
|
|
||||||
LogLevel.Warning,
|
|
||||||
"Failed to create frostfs session token for client. Address {address}, {error}",
|
|
||||||
EventName = nameof(SessionCreationError))]
|
|
||||||
internal static partial void SessionCreationError(ILogger logger, string address, string error);
|
|
||||||
|
|
||||||
[LoggerMessage(101,
|
|
||||||
LogLevel.Warning,
|
|
||||||
"Error threshold reached. Address {address}, threshold {threshold}",
|
|
||||||
EventName = nameof(ErrorЕhresholdReached))]
|
|
||||||
internal static partial void ErrorЕhresholdReached(ILogger logger, string address, uint threshold);
|
|
||||||
|
|
||||||
[LoggerMessage(102,
|
|
||||||
LogLevel.Warning,
|
|
||||||
"Health has changed: {address} healthy {healthy}, reason {error}",
|
|
||||||
EventName = nameof(HealthChanged))]
|
|
||||||
internal static partial void HealthChanged(ILogger logger, string address, bool healthy, string error);
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class ContainerMapper
|
|
||||||
{
|
|
||||||
public static FrostFsContainerInfo ToModel(this Container.Container container)
|
|
||||||
{
|
|
||||||
if (container == null)
|
|
||||||
throw new ArgumentNullException(nameof(container));
|
|
||||||
|
|
||||||
return new FrostFsContainerInfo(
|
|
||||||
container.PlacementPolicy.ToModel(),
|
|
||||||
container.Attributes?.Select(a => new FrostFsAttributePair(a.Key, a.Value)).ToArray(),
|
|
||||||
container.Version?.ToModel(),
|
|
||||||
container.OwnerId?.ToModel(),
|
|
||||||
container.Nonce?.ToUuid());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using FrostFS.Refs;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class ContainerIdMapper
|
|
||||||
{
|
|
||||||
private static readonly MemoryCacheEntryOptions _oneHourExpiration = new MemoryCacheEntryOptions()
|
|
||||||
.SetSlidingExpiration(TimeSpan.FromHours(1))
|
|
||||||
.SetSize(1);
|
|
||||||
|
|
||||||
public static ContainerID ToMessage(this FrostFsContainerId model)
|
|
||||||
{
|
|
||||||
if (model is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(model));
|
|
||||||
}
|
|
||||||
|
|
||||||
var containerId = model.GetValue() ?? throw new ArgumentNullException(nameof(model));
|
|
||||||
|
|
||||||
if (!Caches.Containers.TryGetValue(containerId, out ContainerID? message))
|
|
||||||
{
|
|
||||||
message = new ContainerID
|
|
||||||
{
|
|
||||||
Value = ByteString.CopyFrom(Base58.Decode(containerId))
|
|
||||||
};
|
|
||||||
|
|
||||||
Caches.Containers.Set(containerId, message, _oneHourExpiration);
|
|
||||||
}
|
|
||||||
return message!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FrostFsContainerId ToModel(this ContainerID message)
|
|
||||||
{
|
|
||||||
if (message is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FrostFsContainerId(Base58.Encode(message.Value.Span));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using FrostFS.Session;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class MetaHeaderMapper
|
|
||||||
{
|
|
||||||
public static RequestMetaHeader ToMessage(this MetaHeader metaHeader)
|
|
||||||
{
|
|
||||||
if (metaHeader is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(metaHeader));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RequestMetaHeader
|
|
||||||
{
|
|
||||||
Version = metaHeader.Version.ToMessage(),
|
|
||||||
Epoch = (uint)metaHeader.Epoch,
|
|
||||||
Ttl = (uint)metaHeader.Ttl
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public static class NetmapMapper
|
|
||||||
{
|
|
||||||
public static FrostFsNetmapSnapshot ToModel(this NetmapSnapshotResponse netmap)
|
|
||||||
{
|
|
||||||
if (netmap is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(netmap));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FrostFsNetmapSnapshot(
|
|
||||||
netmap.Body.Netmap.Epoch,
|
|
||||||
netmap.Body.Netmap.Nodes
|
|
||||||
.Select(n => n.ToModel(netmap.MetaHeader.Version))
|
|
||||||
.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public static class NodeInfoMapper
|
|
||||||
{
|
|
||||||
public static FrostFsNodeInfo ToModel(this LocalNodeInfoResponse.Types.Body node)
|
|
||||||
{
|
|
||||||
if (node is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
return node.NodeInfo.ToModel(node.Version);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FrostFsNodeInfo ToModel(this NodeInfo nodeInfo, Refs.Version version)
|
|
||||||
{
|
|
||||||
if (nodeInfo is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(nodeInfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeState state = nodeInfo.State switch
|
|
||||||
{
|
|
||||||
NodeInfo.Types.State.Unspecified => NodeState.Unspecified,
|
|
||||||
NodeInfo.Types.State.Online => NodeState.Online,
|
|
||||||
NodeInfo.Types.State.Offline => NodeState.Offline,
|
|
||||||
NodeInfo.Types.State.Maintenance => NodeState.Maintenance,
|
|
||||||
_ => throw new ArgumentException($"Unknown NodeState. Value: '{nodeInfo.State}'.")
|
|
||||||
};
|
|
||||||
|
|
||||||
return new FrostFsNodeInfo(
|
|
||||||
version: version.ToModel(),
|
|
||||||
state: state,
|
|
||||||
addresses: [.. nodeInfo.Addresses],
|
|
||||||
attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value),
|
|
||||||
publicKey: nodeInfo.PublicKey.Memory
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public static class PlacementPolicyMapper
|
|
||||||
{
|
|
||||||
public static FrostFsPlacementPolicy ToModel(this PlacementPolicy placementPolicy)
|
|
||||||
{
|
|
||||||
if (placementPolicy is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(placementPolicy));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FrostFsPlacementPolicy(
|
|
||||||
placementPolicy.Unique,
|
|
||||||
placementPolicy.ContainerBackupFactor,
|
|
||||||
[.. placementPolicy.Selectors.Select(s => s.ToModel())],
|
|
||||||
[.. placementPolicy.Filters.Select(f => f.ToModel())],
|
|
||||||
[.. placementPolicy.Replicas.Select(r => r.ToModel())]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,100 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public static class PolicyMapper
|
|
||||||
{
|
|
||||||
public static Replica ToMessage(this FrostFsReplica replica)
|
|
||||||
{
|
|
||||||
return new Replica
|
|
||||||
{
|
|
||||||
Count = (uint)replica.Count,
|
|
||||||
Selector = replica.Selector
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FrostFsReplica ToModel(this Replica replica)
|
|
||||||
{
|
|
||||||
if (replica is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(replica));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FrostFsReplica((int)replica.Count, replica.Selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Selector ToMessage(this FrostFsSelector selector)
|
|
||||||
{
|
|
||||||
if (selector is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(selector));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Selector
|
|
||||||
{
|
|
||||||
Name = selector.Name,
|
|
||||||
Count = selector.Count,
|
|
||||||
Clause = (Clause)selector.Clause,
|
|
||||||
Attribute = selector.Attribute,
|
|
||||||
Filter = selector.Filter
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FrostFsSelector ToModel(this Selector selector)
|
|
||||||
{
|
|
||||||
if (selector is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(selector));
|
|
||||||
}
|
|
||||||
|
|
||||||
var model = new FrostFsSelector(selector.Name)
|
|
||||||
{
|
|
||||||
Count = selector.Count,
|
|
||||||
Clause = (int)selector.Clause,
|
|
||||||
Attribute = selector.Attribute,
|
|
||||||
Filter = selector.Filter
|
|
||||||
};
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Filter ToMessage(this FrostFsFilter filter)
|
|
||||||
{
|
|
||||||
if (filter is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(filter));
|
|
||||||
}
|
|
||||||
|
|
||||||
var message = new Filter
|
|
||||||
{
|
|
||||||
Name = filter.Name,
|
|
||||||
Key = filter.Key,
|
|
||||||
Op = (Operation)filter.Operation,
|
|
||||||
Value = filter.Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
message.Filters.AddRange(filter.Filters.Select(f => f.ToMessage()));
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FrostFsFilter ToModel(this Filter filter)
|
|
||||||
{
|
|
||||||
if (filter is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(filter));
|
|
||||||
}
|
|
||||||
|
|
||||||
var model = new FrostFsFilter(
|
|
||||||
filter.Name,
|
|
||||||
filter.Key,
|
|
||||||
(int)filter.Op,
|
|
||||||
filter.Value,
|
|
||||||
[.. filter.Filters.Select(f => f.ToModel())]);
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
internal static class ObjectMapper
|
|
||||||
{
|
|
||||||
internal static FrostFsObject ToModel(this Object.Object obj)
|
|
||||||
{
|
|
||||||
return new FrostFsObject(obj.Header.ToModel())
|
|
||||||
{
|
|
||||||
ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.Span)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using FrostFS.Object;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class ObjectAttributeMapper
|
|
||||||
{
|
|
||||||
public static Header.Types.Attribute ToMessage(this FrostFsAttributePair attribute)
|
|
||||||
{
|
|
||||||
return new Header.Types.Attribute
|
|
||||||
{
|
|
||||||
Key = attribute.Key,
|
|
||||||
Value = attribute.Value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FrostFsAttributePair ToModel(this Header.Types.Attribute attribute)
|
|
||||||
{
|
|
||||||
if (attribute is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(attribute));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FrostFsAttributePair(attribute.Key, attribute.Value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using FrostFS.Object;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class ObjectFilterMapper
|
|
||||||
{
|
|
||||||
public static SearchRequest.Types.Body.Types.Filter ToMessage(this IObjectFilter filter)
|
|
||||||
{
|
|
||||||
if (filter is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(filter));
|
|
||||||
}
|
|
||||||
|
|
||||||
var objMatchTypeName = filter.MatchType switch
|
|
||||||
{
|
|
||||||
FrostFsMatchType.Unspecified => MatchType.Unspecified,
|
|
||||||
FrostFsMatchType.Equals => MatchType.StringEqual,
|
|
||||||
FrostFsMatchType.NotEquals => MatchType.StringNotEqual,
|
|
||||||
FrostFsMatchType.KeyAbsent => MatchType.NotPresent,
|
|
||||||
FrostFsMatchType.StartsWith => MatchType.CommonPrefix,
|
|
||||||
|
|
||||||
_ => throw new ArgumentException($"Unknown MatchType. Value: '{filter.MatchType}'.")
|
|
||||||
};
|
|
||||||
|
|
||||||
return new SearchRequest.Types.Body.Types.Filter
|
|
||||||
{
|
|
||||||
MatchType = objMatchTypeName,
|
|
||||||
Key = filter.Key,
|
|
||||||
Value = filter.GetSerializedValue()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using FrostFS.Object;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class ObjectHeaderMapper
|
|
||||||
{
|
|
||||||
public static FrostFsObjectHeader ToModel(this Header header)
|
|
||||||
{
|
|
||||||
if (header is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(header));
|
|
||||||
}
|
|
||||||
|
|
||||||
var objTypeName = header.ObjectType switch
|
|
||||||
{
|
|
||||||
ObjectType.Regular => FrostFsObjectType.Regular,
|
|
||||||
ObjectType.Lock => FrostFsObjectType.Lock,
|
|
||||||
ObjectType.Tombstone => FrostFsObjectType.Tombstone,
|
|
||||||
_ => throw new ArgumentException($"Unknown ObjectType. Value: '{header.ObjectType}'.")
|
|
||||||
};
|
|
||||||
|
|
||||||
FrostFsSplit? split = null;
|
|
||||||
|
|
||||||
if (header.Split != null)
|
|
||||||
{
|
|
||||||
var children = header.Split.Children.Count != 0 ? new ReadOnlyCollection<FrostFsObjectId>(
|
|
||||||
header.Split.Children.Select(x => x.ToModel()).ToList()) : null;
|
|
||||||
|
|
||||||
split = new FrostFsSplit(new SplitId(header.Split.SplitId.ToUuid()),
|
|
||||||
header.Split.Previous?.ToModel(),
|
|
||||||
header.Split.Parent?.ToModel(),
|
|
||||||
header.Split.ParentHeader?.ToModel(),
|
|
||||||
null,
|
|
||||||
children);
|
|
||||||
}
|
|
||||||
|
|
||||||
var model = new FrostFsObjectHeader(
|
|
||||||
new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.Span)),
|
|
||||||
objTypeName,
|
|
||||||
header.Attributes.Select(attribute => attribute.ToModel()).ToArray(),
|
|
||||||
split,
|
|
||||||
header.OwnerId.ToModel(),
|
|
||||||
header.Version.ToModel())
|
|
||||||
{
|
|
||||||
PayloadLength = header.PayloadLength,
|
|
||||||
};
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using FrostFS.Refs;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class ObjectIdMapper
|
|
||||||
{
|
|
||||||
public static ObjectID ToMessage(this FrostFsObjectId objectId)
|
|
||||||
{
|
|
||||||
if (objectId is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(objectId));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ObjectID
|
|
||||||
{
|
|
||||||
Value = ByteString.CopyFrom(objectId.ToHash())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FrostFsObjectId ToModel(this ObjectID objectId)
|
|
||||||
{
|
|
||||||
if (objectId is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(objectId));
|
|
||||||
}
|
|
||||||
|
|
||||||
return FrostFsObjectId.FromHash(objectId.Value.Span);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using FrostFS.Refs;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class OwnerIdMapper
|
|
||||||
{
|
|
||||||
private static readonly MemoryCacheEntryOptions _oneHourExpiration = new MemoryCacheEntryOptions()
|
|
||||||
.SetSlidingExpiration(TimeSpan.FromHours(1))
|
|
||||||
.SetSize(1);
|
|
||||||
|
|
||||||
public static OwnerID ToMessage(this FrostFsOwner model)
|
|
||||||
{
|
|
||||||
if (model is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(model));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Caches.Owners.TryGetValue(model, out OwnerID? message))
|
|
||||||
{
|
|
||||||
message = new OwnerID
|
|
||||||
{
|
|
||||||
Value = ByteString.CopyFrom(model.ToHash())
|
|
||||||
};
|
|
||||||
|
|
||||||
Caches.Owners.Set(model, message, _oneHourExpiration);
|
|
||||||
}
|
|
||||||
|
|
||||||
return message!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FrostFsOwner ToModel(this OwnerID message)
|
|
||||||
{
|
|
||||||
if (message is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Caches.Owners.TryGetValue(message, out FrostFsOwner? model))
|
|
||||||
{
|
|
||||||
model = new FrostFsOwner(Base58.Encode(message.Value.Span));
|
|
||||||
|
|
||||||
Caches.Owners.Set(message, model, _oneHourExpiration);
|
|
||||||
}
|
|
||||||
|
|
||||||
return model!;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
public static class SessionMapper
|
|
||||||
{
|
|
||||||
public static byte[] Serialize(this Session.SessionToken token)
|
|
||||||
{
|
|
||||||
if (token is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(token));
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bytes = new byte[token.CalculateSize()];
|
|
||||||
using CodedOutputStream stream = new(bytes);
|
|
||||||
token.WriteTo(stream);
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Session.SessionToken Deserialize(this Session.SessionToken token, byte[] bytes)
|
|
||||||
{
|
|
||||||
token.MergeFrom(bytes);
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class SignatureMapper
|
|
||||||
{
|
|
||||||
public static Refs.Signature ToMessage(this FrostFsSignature signature)
|
|
||||||
{
|
|
||||||
if (signature is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(signature));
|
|
||||||
}
|
|
||||||
|
|
||||||
var scheme = signature.Scheme switch
|
|
||||||
{
|
|
||||||
SignatureScheme.EcdsaRfc6979Sha256 => Refs.SignatureScheme.EcdsaRfc6979Sha256,
|
|
||||||
SignatureScheme.EcdsaRfc6979Sha256WalletConnect => Refs.SignatureScheme.EcdsaRfc6979Sha256WalletConnect,
|
|
||||||
SignatureScheme.EcdsaSha512 => Refs.SignatureScheme.EcdsaSha512,
|
|
||||||
_ => throw new ArgumentException(nameof(signature.Scheme), $"Unexpected enum value: {signature.Scheme}")
|
|
||||||
};
|
|
||||||
|
|
||||||
return new Refs.Signature
|
|
||||||
{
|
|
||||||
Key = ByteString.CopyFrom(signature.Key),
|
|
||||||
Scheme = scheme,
|
|
||||||
Sign = ByteString.CopyFrom(signature.Sign)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class StatusMapper
|
|
||||||
{
|
|
||||||
public static FrostFsResponseStatus ToModel(this Status.Status status)
|
|
||||||
{
|
|
||||||
if (status is null)
|
|
||||||
return new FrostFsResponseStatus(FrostFsStatusCode.Success);
|
|
||||||
|
|
||||||
var codeName = Enum.GetName(typeof(FrostFsStatusCode), status.Code);
|
|
||||||
|
|
||||||
return codeName is null
|
|
||||||
? throw new ArgumentException($"Unknown StatusCode. Value: '{status.Code}'.")
|
|
||||||
: new FrostFsResponseStatus(
|
|
||||||
(FrostFsStatusCode)status.Code,
|
|
||||||
status.Message,
|
|
||||||
string.Join(", ", status.Details.Select(d => System.Text.Encoding.UTF8.GetString([.. d.Value]))));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
using System.Collections;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
using FrostFS.Refs;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
public static class VersionMapper
|
|
||||||
{
|
|
||||||
private static readonly Hashtable _cacheMessages = [];
|
|
||||||
private static readonly Hashtable _cacheModels = [];
|
|
||||||
private static SpinLock _spinlock;
|
|
||||||
|
|
||||||
public static Version ToMessage(this FrostFsVersion model)
|
|
||||||
{
|
|
||||||
if (model is null)
|
|
||||||
{
|
|
||||||
throw new System.ArgumentNullException(nameof(model));
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = model.Major << 16 + model.Minor;
|
|
||||||
|
|
||||||
if (!_cacheMessages.ContainsKey(key))
|
|
||||||
{
|
|
||||||
bool lockTaken = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_spinlock.Enter(ref lockTaken);
|
|
||||||
var message = new Version
|
|
||||||
{
|
|
||||||
Major = (uint)model.Major,
|
|
||||||
Minor = (uint)model.Minor
|
|
||||||
};
|
|
||||||
|
|
||||||
_cacheMessages.Add(key, message);
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
catch (System.ArgumentException)
|
|
||||||
{
|
|
||||||
// ignore attempt to add duplicate
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (lockTaken)
|
|
||||||
_spinlock.Exit(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Version)_cacheMessages[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FrostFsVersion ToModel(this Version message)
|
|
||||||
{
|
|
||||||
if (message is null)
|
|
||||||
{
|
|
||||||
throw new System.ArgumentNullException(nameof(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = (int)message.Major << 16 + (int)message.Minor;
|
|
||||||
|
|
||||||
if (!_cacheModels.ContainsKey(key))
|
|
||||||
{
|
|
||||||
bool lockTaken = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_spinlock.Enter(ref lockTaken);
|
|
||||||
var model = new FrostFsVersion((int)message.Major, (int)message.Minor);
|
|
||||||
|
|
||||||
_cacheModels.Add(key, model);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
catch (System.ArgumentException)
|
|
||||||
{
|
|
||||||
// ignore attempt to add duplicate
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (lockTaken)
|
|
||||||
_spinlock.Exit(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (FrostFsVersion)_cacheModels[key];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using Grpc.Core.Interceptors;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class ClientSettings
|
|
||||||
{
|
|
||||||
protected static readonly string errorTemplate = "{0} is required parameter";
|
|
||||||
|
|
||||||
public string Host { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
public string Key { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
public Action<CallStatistics>? Callback { get; set; }
|
|
||||||
|
|
||||||
public Collection<Interceptor> Interceptors { get; } = [];
|
|
||||||
|
|
||||||
public void Validate()
|
|
||||||
{
|
|
||||||
StringBuilder? errors = null;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Host))
|
|
||||||
(errors = new StringBuilder(128)).AppendLine(string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Host)));
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(Key))
|
|
||||||
(errors ??= new StringBuilder(128)).AppendLine(string.Format(CultureInfo.InvariantCulture, errorTemplate, nameof(Key)));
|
|
||||||
|
|
||||||
if (errors != null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException(errors.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
using FrostFS.Refs;
|
|
||||||
using FrostFS.SDK.Client;
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsContainerId
|
|
||||||
{
|
|
||||||
private string? modelId;
|
|
||||||
private ContainerID? containerID;
|
|
||||||
|
|
||||||
public FrostFsContainerId(string id)
|
|
||||||
{
|
|
||||||
this.modelId = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal FrostFsContainerId(ContainerID id)
|
|
||||||
{
|
|
||||||
this.containerID = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetValue()
|
|
||||||
{
|
|
||||||
if (this.modelId != null)
|
|
||||||
return this.modelId;
|
|
||||||
|
|
||||||
if (containerID != null)
|
|
||||||
{
|
|
||||||
this.modelId = Base58.Encode(containerID.Value.Span);
|
|
||||||
return this.modelId;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new FrostFsInvalidObjectException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContainerID GetContainerID()
|
|
||||||
{
|
|
||||||
if (this.containerID != null)
|
|
||||||
return this.containerID;
|
|
||||||
|
|
||||||
if (modelId != null)
|
|
||||||
{
|
|
||||||
this.containerID = this.ToMessage();
|
|
||||||
return this.containerID;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new FrostFsInvalidObjectException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return GetValue();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Client;
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsContainerInfo
|
|
||||||
{
|
|
||||||
private Container.Container.Types.Attribute[]? grpsAttributes;
|
|
||||||
private ReadOnlyCollection<FrostFsAttributePair>? attributes;
|
|
||||||
private FrostFsPlacementPolicy? placementPolicy;
|
|
||||||
private Guid? nonce;
|
|
||||||
|
|
||||||
private Container.Container? container;
|
|
||||||
|
|
||||||
public FrostFsContainerInfo(
|
|
||||||
FrostFsPlacementPolicy placementPolicy,
|
|
||||||
FrostFsAttributePair[]? attributes = null,
|
|
||||||
FrostFsVersion? version = null,
|
|
||||||
FrostFsOwner? owner = null,
|
|
||||||
Guid? nonce = null)
|
|
||||||
{
|
|
||||||
this.placementPolicy = placementPolicy;
|
|
||||||
Version = version;
|
|
||||||
Owner = owner;
|
|
||||||
this.nonce = nonce;
|
|
||||||
|
|
||||||
if (attributes != null)
|
|
||||||
this.attributes = new ReadOnlyCollection<FrostFsAttributePair>(attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal FrostFsContainerInfo(Container.Container container)
|
|
||||||
{
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid Nonce
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
nonce ??= container?.Nonce != null ? container.Nonce.ToUuid() : Guid.NewGuid();
|
|
||||||
return nonce.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsPlacementPolicy? PlacementPolicy
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
placementPolicy ??= container?.PlacementPolicy?.ToModel();
|
|
||||||
return placementPolicy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyCollection<FrostFsAttributePair>? Attributes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (attributes == null && grpsAttributes != null)
|
|
||||||
attributes = new ReadOnlyCollection<FrostFsAttributePair>(grpsAttributes.Select(a => new FrostFsAttributePair(a.Key, a.Value)).ToList());
|
|
||||||
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsVersion? Version { get; private set; }
|
|
||||||
|
|
||||||
public FrostFsOwner? Owner { get; private set; }
|
|
||||||
|
|
||||||
internal Container.Container.Types.Attribute[]? GetGrpsAttributes()
|
|
||||||
{
|
|
||||||
grpsAttributes ??= Attributes?
|
|
||||||
.Select(a => new Container.Container.Types.Attribute { Key = a.Key, Value = a.Value })
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return grpsAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Container.Container GetContainer()
|
|
||||||
{
|
|
||||||
if (this.container == null)
|
|
||||||
{
|
|
||||||
if (PlacementPolicy == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("PlacementPolicy is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.container = new Container.Container()
|
|
||||||
{
|
|
||||||
PlacementPolicy = PlacementPolicy.Value.GetPolicy(),
|
|
||||||
Nonce = ByteString.CopyFrom(Nonce.ToBytes()),
|
|
||||||
OwnerId = Owner?.OwnerID,
|
|
||||||
Version = Version?.VersionID
|
|
||||||
};
|
|
||||||
|
|
||||||
var attribs = GetGrpsAttributes();
|
|
||||||
if (attribs != null)
|
|
||||||
this.container.Attributes.AddRange(attribs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.container;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public enum FrostFsMatchType
|
|
||||||
{
|
|
||||||
Unspecified = 0,
|
|
||||||
Equals = 1,
|
|
||||||
NotEquals = 2,
|
|
||||||
KeyAbsent = 3,
|
|
||||||
StartsWith = 4
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public enum FrostFsObjectType
|
|
||||||
{
|
|
||||||
Regular = 0,
|
|
||||||
Tombstone = 1,
|
|
||||||
Lock = 3
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public enum FrostFsStatusCode
|
|
||||||
{
|
|
||||||
Success = 0,
|
|
||||||
Internal = 1024,
|
|
||||||
WrongMagicNumber = 1025,
|
|
||||||
SignatureVerificationFailure = 1026,
|
|
||||||
NodeUnderMaintenance = 1027,
|
|
||||||
ObjectAccessDenied = 2048,
|
|
||||||
ObjectNotFound = 2049,
|
|
||||||
ObjectLocked = 2050,
|
|
||||||
LockNotRegularObject = 2051,
|
|
||||||
ObjectAlreadyRemoved = 2052,
|
|
||||||
OutOfRange = 2053,
|
|
||||||
ContainerNotFound = 3072,
|
|
||||||
EAclNotFound = 3073,
|
|
||||||
ContainerAccessDenied = 3074,
|
|
||||||
TokenNotFound = 4096,
|
|
||||||
TokenExpired = 4097,
|
|
||||||
ApeManagerAccessDenied = 5120
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public enum NodeState
|
|
||||||
{
|
|
||||||
Unspecified = 0,
|
|
||||||
Online = 1,
|
|
||||||
Offline = 2,
|
|
||||||
Maintenance = 3
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public enum SignatureScheme
|
|
||||||
{
|
|
||||||
EcdsaSha512,
|
|
||||||
EcdsaRfc6979Sha256,
|
|
||||||
EcdsaRfc6979Sha256WalletConnect
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class CallStatistics
|
|
||||||
{
|
|
||||||
public string? MethodName { get; set; }
|
|
||||||
public long ElapsedMicroSeconds { get; set; }
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class CheckSum
|
|
||||||
{
|
|
||||||
private byte[]? hash;
|
|
||||||
private string? text;
|
|
||||||
|
|
||||||
public static CheckSum CreateCheckSum(byte[] content)
|
|
||||||
{
|
|
||||||
return new CheckSum { hash = content.Sha256() };
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return text ??= BitConverter.ToString(hash).Replace("-", "");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public static class Constants
|
|
||||||
{
|
|
||||||
public const int ObjectChunkSize = 3 * (1 << 20);
|
|
||||||
public const int Sha256HashLength = 32;
|
|
||||||
|
|
||||||
// HeaderPrefix is a prefix of key to object header value or property.
|
|
||||||
public const string HeaderPrefix = "$Object:";
|
|
||||||
|
|
||||||
// FilterHeaderVersion is a filter key to "version" field of the object header.
|
|
||||||
public const string FilterHeaderVersion = HeaderPrefix + "version";
|
|
||||||
|
|
||||||
// FilterHeaderObjectID is a filter key to "object_id" field of the object.
|
|
||||||
public const string FilterHeaderObjectID = HeaderPrefix + "objectID";
|
|
||||||
|
|
||||||
// FilterHeaderContainerID is a filter key to "container_id" field of the object header.
|
|
||||||
public const string FilterHeaderContainerID = HeaderPrefix + "containerID";
|
|
||||||
|
|
||||||
// FilterHeaderOwnerID is a filter key to "owner_id" field of the object header.
|
|
||||||
public const string FilterHeaderOwnerID = HeaderPrefix + "ownerID";
|
|
||||||
|
|
||||||
// FilterHeaderCreationEpoch is a filter key to "creation_epoch" field of the object header.
|
|
||||||
public const string FilterHeaderCreationEpoch = HeaderPrefix + "creationEpoch";
|
|
||||||
|
|
||||||
// FilterHeaderPayloadLength is a filter key to "payload_length" field of the object header.
|
|
||||||
public const string FilterHeaderPayloadLength = HeaderPrefix + "payloadLength";
|
|
||||||
|
|
||||||
// FilterHeaderPayloadHash is a filter key to "payload_hash" field of the object header.
|
|
||||||
public const string FilterHeaderPayloadHash = HeaderPrefix + "payloadHash";
|
|
||||||
|
|
||||||
// FilterHeaderObjectType is a filter key to "object_type" field of the object header.
|
|
||||||
public const string FilterHeaderObjectType = HeaderPrefix + "objectType";
|
|
||||||
|
|
||||||
// FilterHeaderHomomorphicHash is a filter key to "homomorphic_hash" field of the object header.
|
|
||||||
public const string FilterHeaderHomomorphicHash = HeaderPrefix + "homomorphicHash";
|
|
||||||
|
|
||||||
// FilterHeaderParent is a filter key to "split.parent" field of the object header.
|
|
||||||
public const string FilterHeaderParent = HeaderPrefix + "split.parent";
|
|
||||||
|
|
||||||
// FilterHeaderSplitID is a filter key to "split.splitID" field of the object header.
|
|
||||||
public const string FilterHeaderSplitID = HeaderPrefix + "split.splitID";
|
|
||||||
|
|
||||||
// FilterHeaderECParent is a filter key to "ec.parent" field of the object header.
|
|
||||||
public const string FilterHeaderECParent = HeaderPrefix + "ec.parent";
|
|
||||||
|
|
||||||
// FilterPropertyRoot is a filter key to check if regular object is on top of split hierarchy.
|
|
||||||
public const string FilterHeaderRoot = HeaderPrefix + "ROOT";
|
|
||||||
|
|
||||||
// FilterPropertyPhy is a filter key to check if an object physically stored on a node.
|
|
||||||
public const string FilterHeaderPhy = HeaderPrefix + "PHY";
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
using System.Linq;
|
|
||||||
using FrostFS.Netmap;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsFilter(string name, string key, int operation, string value, FrostFsFilter[] filters) : IFrostFsFilter
|
|
||||||
{
|
|
||||||
public string Name { get; } = name;
|
|
||||||
public string Key { get; } = key;
|
|
||||||
public int Operation { get; } = operation;
|
|
||||||
public string Value { get; } = value;
|
|
||||||
public FrostFsFilter[] Filters { get; } = filters;
|
|
||||||
|
|
||||||
internal Filter GetMessage()
|
|
||||||
{
|
|
||||||
var filter = new Filter()
|
|
||||||
{
|
|
||||||
Name = Name,
|
|
||||||
Key = Key,
|
|
||||||
Op = (Operation)Operation,
|
|
||||||
Value = Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
filter.Filters.AddRange(Filters.Select(f => f.GetMessage()));
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,246 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Client;
|
|
||||||
using FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsNetmapSnapshot(ulong epoch, IReadOnlyList<FrostFsNodeInfo> nodeInfoCollection)
|
|
||||||
{
|
|
||||||
public ulong Epoch { get; private set; } = epoch;
|
|
||||||
|
|
||||||
public IReadOnlyList<FrostFsNodeInfo> NodeInfoCollection { get; private set; } = nodeInfoCollection;
|
|
||||||
|
|
||||||
internal static INormalizer NewReverseMinNorm(double minV)
|
|
||||||
{
|
|
||||||
return new ReverseMinNorm { min = minV };
|
|
||||||
}
|
|
||||||
|
|
||||||
// newSigmoidNorm returns a normalizer which
|
|
||||||
// normalize values in range of 0.0 to 1.0 to a scaled sigmoid.
|
|
||||||
internal static INormalizer NewSigmoidNorm(double scale)
|
|
||||||
{
|
|
||||||
return new SigmoidNorm(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// PlacementVectors sorts container nodes returned by ContainerNodes method
|
|
||||||
// and returns placement vectors for the entity identified by the given pivot.
|
|
||||||
// For example, in order to build node list to store the object, binary-encoded
|
|
||||||
// object identifier can be used as pivot. Result is deterministic for
|
|
||||||
// the fixed NetMap and parameters.
|
|
||||||
public FrostFsNodeInfo[][] PlacementVectors(FrostFsNodeInfo[][] vectors, byte[] pivot)
|
|
||||||
{
|
|
||||||
if (vectors is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(vectors));
|
|
||||||
}
|
|
||||||
|
|
||||||
using var murmur3 = new Murmur3(0);
|
|
||||||
var hash = murmur3.GetCheckSum64(pivot);
|
|
||||||
|
|
||||||
var wf = Tools.DefaultWeightFunc(NodeInfoCollection.ToArray());
|
|
||||||
|
|
||||||
var result = new FrostFsNodeInfo[vectors.Length][];
|
|
||||||
var maxSize = vectors.Max(x => x.Length);
|
|
||||||
|
|
||||||
var spanWeigths = new double[maxSize];
|
|
||||||
|
|
||||||
for (int i = 0; i < vectors.Length; i++)
|
|
||||||
{
|
|
||||||
result[i] = new FrostFsNodeInfo[vectors[i].Length];
|
|
||||||
|
|
||||||
for (int j = 0; j < vectors[i].Length; j++)
|
|
||||||
{
|
|
||||||
result[i][j] = vectors[i][j];
|
|
||||||
}
|
|
||||||
|
|
||||||
Tools.AppendWeightsTo(result[i], wf, ref spanWeigths);
|
|
||||||
|
|
||||||
result[i] = Tools.SortHasherSliceByWeightValue(result[i].ToList<FrostFsNodeInfo>(), spanWeigths, hash).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectFilterNodes returns a two-dimensional list of nodes as a result of applying the
|
|
||||||
// given SelectFilterExpr to the NetMap.
|
|
||||||
// If the SelectFilterExpr contains only filters, the result contains a single row with the
|
|
||||||
// result of the last filter application.
|
|
||||||
// If the SelectFilterExpr contains only selectors, the result contains the selection rows
|
|
||||||
// of the last select application.
|
|
||||||
List<List<FrostFsNodeInfo>> SelectFilterNodes(SelectFilterExpr expr)
|
|
||||||
{
|
|
||||||
var policy = new FrostFsPlacementPolicy(false, expr.Cbf, [expr.Selector], expr.Filters);
|
|
||||||
|
|
||||||
var ctx = new Context(this)
|
|
||||||
{
|
|
||||||
Cbf = expr.Cbf
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.ProcessFilters(policy);
|
|
||||||
ctx.ProcessSelectors(policy);
|
|
||||||
|
|
||||||
var ret = new List<List<FrostFsNodeInfo>>();
|
|
||||||
|
|
||||||
if (expr.Selector == null)
|
|
||||||
{
|
|
||||||
var lastFilter = expr.Filters[^1];
|
|
||||||
|
|
||||||
var subCollestion = new List<FrostFsNodeInfo>();
|
|
||||||
ret.Add(subCollestion);
|
|
||||||
|
|
||||||
foreach (var nodeInfo in NodeInfoCollection)
|
|
||||||
{
|
|
||||||
if (ctx.Match(ctx.ProcessedFilters[lastFilter.Name], nodeInfo))
|
|
||||||
{
|
|
||||||
subCollestion.Add(nodeInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (expr.Selector.Name != null)
|
|
||||||
{
|
|
||||||
var sel = ctx.GetSelection(ctx.ProcessedSelectors[expr.Selector.Name]);
|
|
||||||
|
|
||||||
foreach (var ns in sel)
|
|
||||||
{
|
|
||||||
var subCollestion = new List<FrostFsNodeInfo>();
|
|
||||||
ret.Add(subCollestion);
|
|
||||||
foreach (var n in ns)
|
|
||||||
{
|
|
||||||
subCollestion.Add(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Func<FrostFsNodeInfo, double> NewWeightFunc(INormalizer capNorm, INormalizer priceNorm)
|
|
||||||
{
|
|
||||||
return new Func<FrostFsNodeInfo, double>((FrostFsNodeInfo nodeInfo) =>
|
|
||||||
{
|
|
||||||
return capNorm.Normalize(nodeInfo.GetCapacity()) * priceNorm.Normalize(nodeInfo.Price);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FrostFsNodeInfo[] FlattenNodes(List<List<FrostFsNodeInfo>> nodes)
|
|
||||||
{
|
|
||||||
int sz = 0;
|
|
||||||
foreach (var ns in nodes)
|
|
||||||
{
|
|
||||||
sz += ns.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = new FrostFsNodeInfo[sz];
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
foreach (var ns in nodes)
|
|
||||||
{
|
|
||||||
foreach (var n in ns)
|
|
||||||
{
|
|
||||||
result[i++] = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerNodes returns two-dimensional list of nodes as a result of applying
|
|
||||||
// given PlacementPolicy to the NetMap. Each line of the list corresponds to a
|
|
||||||
// replica descriptor. Line order corresponds to order of ReplicaDescriptor list
|
|
||||||
// in the policy. Nodes are pre-filtered according to the Filter list from
|
|
||||||
// the policy, and then selected by Selector list. Result is deterministic for
|
|
||||||
// the fixed NetMap and parameters.
|
|
||||||
//
|
|
||||||
// Result can be used in PlacementVectors.
|
|
||||||
public FrostFsNodeInfo[][] ContainerNodes(FrostFsPlacementPolicy p, byte[]? pivot)
|
|
||||||
{
|
|
||||||
var c = new Context(this)
|
|
||||||
{
|
|
||||||
Cbf = p.BackupFactor == 0 ? 3 : p.BackupFactor
|
|
||||||
};
|
|
||||||
|
|
||||||
if (pivot != null && pivot.Length > 0)
|
|
||||||
{
|
|
||||||
c.HrwSeed = pivot;
|
|
||||||
|
|
||||||
using var murmur = new Murmur3(0);
|
|
||||||
c.HrwSeedHash = murmur.GetCheckSum64(pivot);
|
|
||||||
}
|
|
||||||
|
|
||||||
c.ProcessFilters(p);
|
|
||||||
c.ProcessSelectors(p);
|
|
||||||
|
|
||||||
var unique = p.IsUnique();
|
|
||||||
|
|
||||||
var result = new List<List<FrostFsNodeInfo>>(p.Replicas.Length);
|
|
||||||
for (int i = 0; i < p.Replicas.Length; i++)
|
|
||||||
{
|
|
||||||
result.Add([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that the cached selectors are not used when the policy contains the UNIQUE flag.
|
|
||||||
// This is necessary because each selection vector affects potentially the subsequent vectors
|
|
||||||
// and thus we call getSelection in such case, in order to take into account nodes previously
|
|
||||||
// marked as used by earlier replicas.
|
|
||||||
for (int i = 0; i < p.Replicas.Length; i++)
|
|
||||||
{
|
|
||||||
var sName = p.Replicas[i].Selector;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(sName) && !(p.Replicas.Length == 1 && p.Selectors.Count == 1))
|
|
||||||
{
|
|
||||||
var s = new FrostFsSelector(string.Empty)
|
|
||||||
{
|
|
||||||
Count = p.Replicas[i].CountNodes(),
|
|
||||||
Filter = Context.mainFilterName
|
|
||||||
};
|
|
||||||
|
|
||||||
var nodes = c.GetSelection(s);
|
|
||||||
result[i].AddRange(FlattenNodes(nodes));
|
|
||||||
|
|
||||||
if (unique)
|
|
||||||
{
|
|
||||||
foreach (var n in result[i])
|
|
||||||
{
|
|
||||||
c.UsedNodes[n.Hash()] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unique)
|
|
||||||
{
|
|
||||||
if (!c.ProcessedSelectors.TryGetValue(sName, out var s) || s == null)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"selector not found: {sName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var nodes = c.GetSelection(c.ProcessedSelectors[sName]);
|
|
||||||
|
|
||||||
result[i].AddRange(FlattenNodes(nodes));
|
|
||||||
|
|
||||||
foreach (var n in result[i])
|
|
||||||
{
|
|
||||||
c.UsedNodes[n.Hash()] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var nodes = c.Selections[sName];
|
|
||||||
result[i].AddRange(FlattenNodes(nodes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var collection = new FrostFsNodeInfo[result.Count][];
|
|
||||||
for (int i = 0; i < result.Count; i++)
|
|
||||||
{
|
|
||||||
collection[i] = [.. result[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return collection;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsNodeInfo(
|
|
||||||
FrostFsVersion version,
|
|
||||||
NodeState state,
|
|
||||||
IReadOnlyCollection<string> addresses,
|
|
||||||
IReadOnlyDictionary<string, string> attributes,
|
|
||||||
ReadOnlyMemory<byte> publicKey) : IHasher
|
|
||||||
{
|
|
||||||
private ulong _hash;
|
|
||||||
|
|
||||||
// attrPrice is a key to the node attribute that indicates the
|
|
||||||
// price in GAS tokens for storing one GB of data during one Epoch.
|
|
||||||
internal const string AttrPrice = "Price";
|
|
||||||
|
|
||||||
// attrCapacity is a key to the node attribute that indicates the
|
|
||||||
// total available disk space in Gigabytes.
|
|
||||||
internal const string AttrCapacity = "Capacity";
|
|
||||||
|
|
||||||
// attrExternalAddr is a key for the attribute storing node external addresses.
|
|
||||||
internal const string AttrExternalAddr = "ExternalAddr";
|
|
||||||
|
|
||||||
// sepExternalAddr is a separator for multi-value ExternalAddr attribute.
|
|
||||||
internal const string SepExternalAddr = ",";
|
|
||||||
|
|
||||||
private ulong price = ulong.MaxValue;
|
|
||||||
|
|
||||||
public NodeState State { get; } = state;
|
|
||||||
|
|
||||||
public FrostFsVersion Version { get; } = version;
|
|
||||||
|
|
||||||
public IReadOnlyCollection<string> Addresses { get; } = addresses;
|
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, string> Attributes { get; } = attributes;
|
|
||||||
|
|
||||||
public ReadOnlyMemory<byte> PublicKey { get; } = publicKey;
|
|
||||||
|
|
||||||
public ulong Hash()
|
|
||||||
{
|
|
||||||
if (_hash == 0)
|
|
||||||
{
|
|
||||||
using var murmur3 = new Murmur3(0);
|
|
||||||
murmur3.Initialize();
|
|
||||||
_hash = murmur3.GetCheckSum64(PublicKey.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
return _hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ulong GetCapacity()
|
|
||||||
{
|
|
||||||
if (!Attributes.TryGetValue(AttrCapacity, out var val))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return ulong.Parse(val, CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ulong Price
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (price == ulong.MaxValue)
|
|
||||||
{
|
|
||||||
if (!Attributes.TryGetValue(AttrPrice, out var val))
|
|
||||||
price = 0;
|
|
||||||
else
|
|
||||||
price = uint.Parse(val, CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
return price;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using FrostFS.Netmap;
|
|
||||||
using FrostFS.SDK.Client;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public struct FrostFsPlacementPolicy(bool unique,
|
|
||||||
uint backupFactor,
|
|
||||||
Collection<FrostFsSelector> selectors,
|
|
||||||
Collection<FrostFsFilter> filters,
|
|
||||||
params FrostFsReplica[] replicas)
|
|
||||||
: IEquatable<FrostFsPlacementPolicy>
|
|
||||||
{
|
|
||||||
private PlacementPolicy? policy;
|
|
||||||
|
|
||||||
public FrostFsReplica[] Replicas { get; } = replicas;
|
|
||||||
|
|
||||||
public Collection<FrostFsSelector> Selectors { get; } = selectors;
|
|
||||||
|
|
||||||
public Collection<FrostFsFilter> Filters { get; } = filters;
|
|
||||||
|
|
||||||
public bool Unique { get; } = unique;
|
|
||||||
|
|
||||||
public uint BackupFactor { get; } = backupFactor;
|
|
||||||
|
|
||||||
public override readonly bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var other = (FrostFsPlacementPolicy)obj;
|
|
||||||
|
|
||||||
return Equals(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlacementPolicy GetPolicy()
|
|
||||||
{
|
|
||||||
if (policy == null)
|
|
||||||
{
|
|
||||||
policy = new PlacementPolicy
|
|
||||||
{
|
|
||||||
Filters = { },
|
|
||||||
Selectors = { },
|
|
||||||
Replicas = { },
|
|
||||||
Unique = Unique,
|
|
||||||
ContainerBackupFactor = BackupFactor
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Selectors != null && Selectors.Count > 0)
|
|
||||||
{
|
|
||||||
policy.Selectors.AddRange(Selectors.Select(s => s.GetMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Filters != null && Filters.Count > 0)
|
|
||||||
{
|
|
||||||
policy.Filters.AddRange(Filters.Select(s => s.ToMessage()));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var replica in Replicas)
|
|
||||||
{
|
|
||||||
policy.Replicas.Add(replica.ToMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return policy;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal readonly bool IsUnique()
|
|
||||||
{
|
|
||||||
return Unique || Replicas.Any(r => r.EcDataCount != 0 || r.EcParityCount != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly int GetHashCode()
|
|
||||||
{
|
|
||||||
return Unique ? 17 : 0 + Replicas.GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(FrostFsPlacementPolicy left, FrostFsPlacementPolicy right)
|
|
||||||
{
|
|
||||||
return left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(FrostFsPlacementPolicy left, FrostFsPlacementPolicy right)
|
|
||||||
{
|
|
||||||
return !(left == right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly bool Equals(FrostFsPlacementPolicy other)
|
|
||||||
{
|
|
||||||
var notEqual = Unique != other.Unique
|
|
||||||
|| Replicas.Length != other.Replicas.Length;
|
|
||||||
|
|
||||||
if (notEqual)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
foreach (var replica in Replicas)
|
|
||||||
{
|
|
||||||
if (!other.Replicas.Any(r => r.Equals(replica)))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public struct FrostFsReplica : IEquatable<FrostFsReplica>
|
|
||||||
{
|
|
||||||
public int Count { get; set; }
|
|
||||||
public string Selector { get; set; }
|
|
||||||
public uint EcDataCount { get; set; }
|
|
||||||
public uint EcParityCount { get; set; }
|
|
||||||
|
|
||||||
public FrostFsReplica(int count, string? selector = null)
|
|
||||||
{
|
|
||||||
selector ??= string.Empty;
|
|
||||||
|
|
||||||
Count = count;
|
|
||||||
Selector = selector;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var other = (FrostFsReplica)obj;
|
|
||||||
|
|
||||||
return Count == other.Count && Selector == other.Selector;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly uint CountNodes()
|
|
||||||
{
|
|
||||||
return Count != 0 ? (uint)Count : EcDataCount + EcParityCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly int GetHashCode()
|
|
||||||
{
|
|
||||||
return (Count + Selector.GetHashCode()) ^ (int)EcDataCount ^ (int)EcParityCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(FrostFsReplica left, FrostFsReplica right)
|
|
||||||
{
|
|
||||||
return left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(FrostFsReplica left, FrostFsReplica right)
|
|
||||||
{
|
|
||||||
return !(left == right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly bool Equals(FrostFsReplica other)
|
|
||||||
{
|
|
||||||
return Count == other.Count
|
|
||||||
&& Selector == other.Selector
|
|
||||||
&& EcDataCount == other.EcDataCount
|
|
||||||
&& EcParityCount == other.EcParityCount;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
using FrostFS.Netmap;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsSelector(string name)
|
|
||||||
{
|
|
||||||
public string Name { get; } = name;
|
|
||||||
public uint Count { get; set; }
|
|
||||||
public int Clause { get; set; }
|
|
||||||
public string? Attribute { get; set; }
|
|
||||||
public string? Filter { get; set; }
|
|
||||||
|
|
||||||
internal Selector GetMessage()
|
|
||||||
{
|
|
||||||
return new Selector()
|
|
||||||
{
|
|
||||||
Name = Name,
|
|
||||||
Clause = (Clause)Clause,
|
|
||||||
Count = Count,
|
|
||||||
Filter = Filter ?? string.Empty,
|
|
||||||
Attribute = Attribute ?? string.Empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
using FrostFS.Refs;
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsVersion(int major, int minor)
|
|
||||||
{
|
|
||||||
private Version? version;
|
|
||||||
|
|
||||||
public int Major { get; set; } = major;
|
|
||||||
public int Minor { get; set; } = minor;
|
|
||||||
|
|
||||||
internal Version VersionID
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
this.version ??= this.ToMessage();
|
|
||||||
return this.version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSupported(FrostFsVersion version)
|
|
||||||
{
|
|
||||||
if (version is null)
|
|
||||||
{
|
|
||||||
throw new System.ArgumentNullException(nameof(version));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Major == version.Major;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"v{Major}.{Minor}";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
namespace FrostFS.SDK
|
|
||||||
{
|
|
||||||
public interface IFrostFsFilter
|
|
||||||
{
|
|
||||||
FrostFsFilter[] Filters { get; }
|
|
||||||
string Key { get; }
|
|
||||||
string Name { get; }
|
|
||||||
int Operation { get; }
|
|
||||||
string Value { get; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
struct NodeAttrPair
|
|
||||||
{
|
|
||||||
internal string attr;
|
|
||||||
internal FrostFsNodeInfo[] nodes;
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
public enum FrostFsClause
|
|
||||||
{
|
|
||||||
Unspecified = 0,
|
|
||||||
Same,
|
|
||||||
Distinct
|
|
||||||
}
|
|
|
@ -1,456 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
internal struct Context
|
|
||||||
{
|
|
||||||
private const string errInvalidFilterName = "filter name is invalid";
|
|
||||||
private const string errInvalidFilterOp = "invalid filter operation";
|
|
||||||
private const string errFilterNotFound = "filter not found";
|
|
||||||
private const string errNonEmptyFilters = "simple filter contains sub-filters";
|
|
||||||
private const string errNotEnoughNodes = "not enough nodes to SELECT from";
|
|
||||||
private const string errUnnamedTopFilter = "unnamed top-level filter";
|
|
||||||
|
|
||||||
internal const string mainFilterName = "*";
|
|
||||||
internal const string likeWildcard = "*";
|
|
||||||
|
|
||||||
// network map to operate on
|
|
||||||
internal FrostFsNetmapSnapshot NetMap { get; }
|
|
||||||
|
|
||||||
// cache of processed filters
|
|
||||||
internal Dictionary<string, FrostFsFilter> ProcessedFilters { get; } = [];
|
|
||||||
|
|
||||||
// cache of processed selectors
|
|
||||||
internal Dictionary<string, FrostFsSelector> ProcessedSelectors { get; } = [];
|
|
||||||
|
|
||||||
// stores results of selector processing
|
|
||||||
internal Dictionary<string, List<List<FrostFsNodeInfo>>> Selections { get; } = [];
|
|
||||||
|
|
||||||
// cache of parsed numeric values
|
|
||||||
internal Dictionary<string, ulong> NumCache { get; } = [];
|
|
||||||
|
|
||||||
internal byte[]? HrwSeed { get; set; }
|
|
||||||
|
|
||||||
// hrw.Hash of hrwSeed
|
|
||||||
internal ulong HrwSeedHash { get; set; }
|
|
||||||
|
|
||||||
// container backup factor
|
|
||||||
internal uint Cbf { get; set; }
|
|
||||||
|
|
||||||
// nodes already used in previous selections, which is needed when the placement
|
|
||||||
// policy uses the UNIQUE flag. Nodes marked as used are not used in subsequent
|
|
||||||
// base selections.
|
|
||||||
internal Dictionary<ulong, bool> UsedNodes { get; } = [];
|
|
||||||
|
|
||||||
// If true, returns an error when netmap does not contain enough nodes for selection.
|
|
||||||
// By default best effort is taken.
|
|
||||||
internal bool Strict { get; set; }
|
|
||||||
|
|
||||||
// weightFunc is a weighting function for determining node priority
|
|
||||||
// which combines low price and high performance
|
|
||||||
private readonly Func<FrostFsNodeInfo, double> weightFunc;
|
|
||||||
|
|
||||||
public Context(FrostFsNetmapSnapshot netMap)
|
|
||||||
{
|
|
||||||
NetMap = netMap;
|
|
||||||
weightFunc = Tools.DefaultWeightFunc(NetMap.NodeInfoCollection);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ProcessFilters(FrostFsPlacementPolicy policy)
|
|
||||||
{
|
|
||||||
foreach (var filter in policy.Filters)
|
|
||||||
{
|
|
||||||
ProcessFilter(filter, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly void ProcessFilter(FrostFsFilter filter, bool top)
|
|
||||||
{
|
|
||||||
var filterName = filter.Name;
|
|
||||||
if (filterName == mainFilterName)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"{errInvalidFilterName}: '{errInvalidFilterName}' is reserved");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (top && string.IsNullOrEmpty(filterName))
|
|
||||||
{
|
|
||||||
throw new FrostFsException(errUnnamedTopFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!top && !string.IsNullOrEmpty(filterName) && !ProcessedFilters.ContainsKey(filterName))
|
|
||||||
{
|
|
||||||
throw new FrostFsException(errFilterNotFound);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter.Operation == (int)Operation.AND ||
|
|
||||||
filter.Operation == (int)Operation.OR ||
|
|
||||||
filter.Operation == (int)Operation.NOT)
|
|
||||||
{
|
|
||||||
foreach (var f in filter.Filters)
|
|
||||||
ProcessFilter(f, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (filter.Filters.Length != 0)
|
|
||||||
{
|
|
||||||
throw new FrostFsException(errNonEmptyFilters);
|
|
||||||
}
|
|
||||||
else if (!top && !string.IsNullOrEmpty(filterName))
|
|
||||||
{
|
|
||||||
// named reference
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (filter.Operation)
|
|
||||||
{
|
|
||||||
case (int)Operation.EQ:
|
|
||||||
case (int)Operation.NE:
|
|
||||||
case (int)Operation.LIKE:
|
|
||||||
break;
|
|
||||||
case (int)Operation.GT:
|
|
||||||
case (int)Operation.GE:
|
|
||||||
case (int)Operation.LT:
|
|
||||||
case (int)Operation.LE:
|
|
||||||
{
|
|
||||||
var n = uint.Parse(filter.Value, CultureInfo.InvariantCulture);
|
|
||||||
NumCache[filter.Value] = n;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new FrostFsException($"{errInvalidFilterOp}: {filter.Operation}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (top)
|
|
||||||
{
|
|
||||||
ProcessedFilters[filterName] = filter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// processSelectors processes selectors and returns error is any of them is invalid.
|
|
||||||
internal void ProcessSelectors(FrostFsPlacementPolicy policy)
|
|
||||||
{
|
|
||||||
foreach (var selector in policy.Selectors)
|
|
||||||
{
|
|
||||||
var filterName = selector.Filter;
|
|
||||||
if (filterName != mainFilterName)
|
|
||||||
{
|
|
||||||
if (selector.Filter == null || !ProcessedFilters.ContainsKey(selector.Filter))
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"{errFilterNotFound}: SELECT FROM '{filterName}'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessedSelectors[selector.Name] = selector;
|
|
||||||
|
|
||||||
var selection = GetSelection(selector);
|
|
||||||
|
|
||||||
Selections[selector.Name] = selection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calcNodesCount returns number of buckets and minimum number of nodes in every bucket
|
|
||||||
// for the given selector.
|
|
||||||
static (int bucketCount, int nodesInBucket) CalcNodesCount(FrostFsSelector selector)
|
|
||||||
{
|
|
||||||
return selector.Clause == (int)FrostFsClause.Same
|
|
||||||
? (1, (int)selector.Count)
|
|
||||||
: ((int)selector.Count, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// getSelectionBase returns nodes grouped by selector attribute.
|
|
||||||
// It it guaranteed that each pair will contain at least one node.
|
|
||||||
internal NodeAttrPair[] GetSelectionBase(FrostFsSelector selector)
|
|
||||||
{
|
|
||||||
var fName = selector.Filter ?? throw new FrostFsException("Filter name for selector is empty");
|
|
||||||
|
|
||||||
_ = ProcessedFilters.TryGetValue(fName, out var f);
|
|
||||||
|
|
||||||
var isMain = fName == mainFilterName;
|
|
||||||
var result = new List<NodeAttrPair>();
|
|
||||||
|
|
||||||
var nodeMap = new Dictionary<string, List<FrostFsNodeInfo>>();
|
|
||||||
var attr = selector.Attribute;
|
|
||||||
|
|
||||||
foreach (var node in NetMap.NodeInfoCollection)
|
|
||||||
{
|
|
||||||
if (UsedNodes.ContainsKey(node.Hash()))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMain || Match(f, node))
|
|
||||||
{
|
|
||||||
if (attr == null)
|
|
||||||
{
|
|
||||||
// Default attribute is transparent identifier which is different for every node.
|
|
||||||
result.Add(new NodeAttrPair { attr = "", nodes = [node] });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var v = node.Attributes[attr];
|
|
||||||
if (!nodeMap.TryGetValue(v, out var nodes) || nodes == null)
|
|
||||||
{
|
|
||||||
nodeMap[v] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeMap[v].Add(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(attr))
|
|
||||||
{
|
|
||||||
foreach (var v in nodeMap)
|
|
||||||
{
|
|
||||||
result.Add(new NodeAttrPair() { attr = v.Key, nodes = [.. v.Value] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HrwSeed != null && HrwSeed.Length != 0)
|
|
||||||
{
|
|
||||||
double[] ws = [];
|
|
||||||
|
|
||||||
var sortedNodes = new NodeAttrPair[result.Count];
|
|
||||||
|
|
||||||
for (int i = 0; i < result.Count; i++)
|
|
||||||
{
|
|
||||||
var res = result[i];
|
|
||||||
Tools.AppendWeightsTo(res.nodes, weightFunc, ref ws);
|
|
||||||
sortedNodes[i].nodes = Tools.SortHasherSliceByWeightValue(res.nodes.ToList(), ws, HrwSeedHash).ToArray();
|
|
||||||
sortedNodes[i].attr = result[i].attr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sortedNodes;
|
|
||||||
}
|
|
||||||
return [.. result];
|
|
||||||
}
|
|
||||||
|
|
||||||
static double CalcBucketWeight(List<FrostFsNodeInfo> ns, MeanIQRAgg a, Func<FrostFsNodeInfo, double> wf)
|
|
||||||
{
|
|
||||||
foreach (var node in ns)
|
|
||||||
{
|
|
||||||
a.Add(wf(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.Compute();
|
|
||||||
}
|
|
||||||
|
|
||||||
// getSelection returns nodes grouped by s.attribute.
|
|
||||||
// Last argument specifies if more buckets can be used to fulfill CBF.
|
|
||||||
internal List<List<FrostFsNodeInfo>> GetSelection(FrostFsSelector s)
|
|
||||||
{
|
|
||||||
var (bucketCount, nodesInBucket) = CalcNodesCount(s);
|
|
||||||
|
|
||||||
var buckets = GetSelectionBase(s);
|
|
||||||
|
|
||||||
if (Strict && buckets.Length < bucketCount)
|
|
||||||
throw new FrostFsException($"errNotEnoughNodes: '{s.Name}'");
|
|
||||||
|
|
||||||
// We need deterministic output in case there is no pivot.
|
|
||||||
// If pivot is set, buckets are sorted by HRW.
|
|
||||||
// However, because initial order influences HRW order for buckets with equal weights,
|
|
||||||
// we also need to have deterministic input to HRW sorting routine.
|
|
||||||
if (HrwSeed == null || HrwSeed.Length == 0)
|
|
||||||
{
|
|
||||||
buckets = string.IsNullOrEmpty(s.Attribute)
|
|
||||||
? [.. buckets.OrderBy(b => b.nodes[0].Hash())]
|
|
||||||
: [.. buckets.OrderBy(b => b.attr)];
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxNodesInBucket = nodesInBucket * (int)Cbf;
|
|
||||||
|
|
||||||
var res = new List<List<FrostFsNodeInfo>>(buckets.Length);
|
|
||||||
var fallback = new List<List<FrostFsNodeInfo>>(buckets.Length);
|
|
||||||
|
|
||||||
for (int i = 0; i < buckets.Length; i++)
|
|
||||||
{
|
|
||||||
var ns = buckets[i].nodes;
|
|
||||||
if (ns.Length >= maxNodesInBucket)
|
|
||||||
{
|
|
||||||
res.Add(new List<FrostFsNodeInfo>(ns[..maxNodesInBucket]));
|
|
||||||
}
|
|
||||||
else if (ns.Length >= nodesInBucket)
|
|
||||||
{
|
|
||||||
fallback.Add(new List<FrostFsNodeInfo>(ns));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.Count < bucketCount)
|
|
||||||
{
|
|
||||||
// Fallback to using minimum allowed backup factor (1).
|
|
||||||
res.AddRange(fallback);
|
|
||||||
|
|
||||||
if (Strict && res.Count < bucketCount)
|
|
||||||
{
|
|
||||||
throw new FrostFsException($"{errNotEnoughNodes}: {s}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HrwSeed != null && HrwSeed.Length != 0)
|
|
||||||
{
|
|
||||||
var weights = new double[res.Count];
|
|
||||||
var a = new MeanIQRAgg();
|
|
||||||
|
|
||||||
for (int i = 0; i < res.Count; i++)
|
|
||||||
{
|
|
||||||
a.Clear();
|
|
||||||
weights[i] = CalcBucketWeight(res[i], a, weightFunc);
|
|
||||||
}
|
|
||||||
|
|
||||||
var hashers = res.Select(r => new HasherList(r)).ToList();
|
|
||||||
hashers = Tools.SortHasherSliceByWeightValue(hashers, weights, HrwSeedHash);
|
|
||||||
|
|
||||||
for (int i = 0; i < res.Count; i++)
|
|
||||||
{
|
|
||||||
res[i] = hashers[i].Nodes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.Count < bucketCount)
|
|
||||||
{
|
|
||||||
if (Strict && res.Count == 0)
|
|
||||||
{
|
|
||||||
throw new FrostFsException(errNotEnoughNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
bucketCount = res.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(s.Attribute))
|
|
||||||
{
|
|
||||||
fallback = res.Skip(bucketCount).ToList();
|
|
||||||
res = res.Take(bucketCount).ToList();
|
|
||||||
|
|
||||||
for (int i = 0; i < fallback.Count; i++)
|
|
||||||
{
|
|
||||||
var index = i % bucketCount;
|
|
||||||
if (res[index].Count >= maxNodesInBucket)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
res[index].AddRange(fallback[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.Take(bucketCount).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool MatchKeyValue(FrostFsFilter f, FrostFsNodeInfo nodeInfo)
|
|
||||||
{
|
|
||||||
switch (f.Operation)
|
|
||||||
{
|
|
||||||
case (int)Operation.EQ:
|
|
||||||
return nodeInfo.Attributes.TryGetValue(f.Key, out var val) && val == f.Value;
|
|
||||||
case (int)Operation.LIKE:
|
|
||||||
{
|
|
||||||
var hasPrefix = f.Value.StartsWith(likeWildcard, StringComparison.Ordinal);
|
|
||||||
var hasSuffix = f.Value.EndsWith(likeWildcard, StringComparison.Ordinal);
|
|
||||||
|
|
||||||
var start = hasPrefix ? likeWildcard.Length : 0;
|
|
||||||
var end = hasSuffix ? f.Value.Length - likeWildcard.Length : f.Value.Length;
|
|
||||||
var str = f.Value[start..end];
|
|
||||||
|
|
||||||
if (hasPrefix && hasSuffix)
|
|
||||||
return nodeInfo.Attributes[f.Key].Contains(str);
|
|
||||||
|
|
||||||
if (hasPrefix && !hasSuffix)
|
|
||||||
return nodeInfo.Attributes[f.Key].EndsWith(str, StringComparison.Ordinal);
|
|
||||||
|
|
||||||
if (!hasPrefix && hasSuffix)
|
|
||||||
return nodeInfo.Attributes[f.Key].StartsWith(str, StringComparison.Ordinal);
|
|
||||||
|
|
||||||
|
|
||||||
return nodeInfo.Attributes[f.Key] == f.Value;
|
|
||||||
}
|
|
||||||
case (int)Operation.NE:
|
|
||||||
return nodeInfo.Attributes[f.Key] != f.Value;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
ulong attr;
|
|
||||||
switch (f.Key)
|
|
||||||
{
|
|
||||||
case FrostFsNodeInfo.AttrPrice:
|
|
||||||
attr = nodeInfo.Price;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FrostFsNodeInfo.AttrCapacity:
|
|
||||||
attr = nodeInfo.GetCapacity();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!ulong.TryParse(nodeInfo.Attributes[f.Key], NumberStyles.Integer, CultureInfo.InvariantCulture, out attr))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (f.Operation)
|
|
||||||
{
|
|
||||||
case (int)Operation.GT:
|
|
||||||
return attr > NumCache[f.Value];
|
|
||||||
case (int)Operation.GE:
|
|
||||||
return attr >= NumCache[f.Value];
|
|
||||||
case (int)Operation.LT:
|
|
||||||
return attr < NumCache[f.Value];
|
|
||||||
case (int)Operation.LE:
|
|
||||||
return attr <= NumCache[f.Value];
|
|
||||||
default:
|
|
||||||
// do nothing and return false
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// will not happen if context was created from f (maybe panic?)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// match matches f against b. It returns no errors because
|
|
||||||
// filter should have been parsed during context creation
|
|
||||||
// and missing node properties are considered as a regular fail.
|
|
||||||
internal bool Match(FrostFsFilter f, FrostFsNodeInfo nodeInfo)
|
|
||||||
{
|
|
||||||
switch (f.Operation)
|
|
||||||
{
|
|
||||||
case (int)Operation.NOT:
|
|
||||||
{
|
|
||||||
var inner = f.Filters;
|
|
||||||
var fSub = inner[0];
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(inner[0].Name))
|
|
||||||
{
|
|
||||||
fSub = ProcessedFilters[inner[0].Name];
|
|
||||||
}
|
|
||||||
return !Match(fSub, nodeInfo);
|
|
||||||
}
|
|
||||||
case (int)Operation.AND:
|
|
||||||
case (int)Operation.OR:
|
|
||||||
{
|
|
||||||
for (int i = 0; i < f.Filters.Length; i++)
|
|
||||||
{
|
|
||||||
var fSub = f.Filters[i];
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(f.Filters[i].Name))
|
|
||||||
{
|
|
||||||
fSub = ProcessedFilters[f.Filters[i].Name];
|
|
||||||
}
|
|
||||||
|
|
||||||
var ok = Match(fSub, nodeInfo);
|
|
||||||
|
|
||||||
if (ok == (f.Operation == (int)Operation.OR))
|
|
||||||
{
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.Operation == (int)Operation.AND;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return MatchKeyValue(f, nodeInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
internal sealed class HasherList : IHasher
|
|
||||||
{
|
|
||||||
private readonly List<FrostFsNodeInfo> _nodes;
|
|
||||||
|
|
||||||
internal HasherList(List<FrostFsNodeInfo> nodes)
|
|
||||||
{
|
|
||||||
_nodes = nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal List<FrostFsNodeInfo> Nodes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _nodes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ulong Hash()
|
|
||||||
{
|
|
||||||
return _nodes.Count > 0 ? _nodes[0].Hash() : 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
internal interface IAggregator
|
|
||||||
{
|
|
||||||
void Add(double d);
|
|
||||||
|
|
||||||
double Compute();
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
internal interface IHasher
|
|
||||||
{
|
|
||||||
ulong Hash();
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
interface INormalizer
|
|
||||||
{
|
|
||||||
double Normalize(double w);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
internal struct MeanAgg
|
|
||||||
{
|
|
||||||
private double mean;
|
|
||||||
private int count;
|
|
||||||
|
|
||||||
internal void Add(double n)
|
|
||||||
{
|
|
||||||
int c = count + 1;
|
|
||||||
mean = mean * count / c + n / c;
|
|
||||||
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal readonly double Compute()
|
|
||||||
{
|
|
||||||
return mean;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
internal struct MeanIQRAgg : IAggregator
|
|
||||||
{
|
|
||||||
private const int minLn = 4;
|
|
||||||
internal Collection<double> arr = [];
|
|
||||||
|
|
||||||
public MeanIQRAgg()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly void Add(double d)
|
|
||||||
{
|
|
||||||
arr.Add(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly double Compute()
|
|
||||||
{
|
|
||||||
var length = arr.Count;
|
|
||||||
if (length == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var sorted = arr.OrderBy(p => p).ToArray();
|
|
||||||
|
|
||||||
double minV, maxV;
|
|
||||||
|
|
||||||
if (arr.Count < minLn)
|
|
||||||
{
|
|
||||||
minV = sorted[0];
|
|
||||||
maxV = sorted[length - 1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var start = length / minLn;
|
|
||||||
var end = length * 3 / minLn - 1;
|
|
||||||
|
|
||||||
minV = sorted[start];
|
|
||||||
maxV = sorted[end];
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = 0;
|
|
||||||
double sum = 0;
|
|
||||||
|
|
||||||
foreach (var e in sorted)
|
|
||||||
{
|
|
||||||
if (e >= minV && e <= maxV)
|
|
||||||
{
|
|
||||||
sum += e;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum / count;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal readonly void Clear()
|
|
||||||
{
|
|
||||||
arr.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
internal struct MinAgg
|
|
||||||
{
|
|
||||||
private double min;
|
|
||||||
private bool minFound;
|
|
||||||
|
|
||||||
internal void Add(double n)
|
|
||||||
{
|
|
||||||
if (!minFound)
|
|
||||||
{
|
|
||||||
min = n;
|
|
||||||
minFound = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n < min)
|
|
||||||
{
|
|
||||||
min = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal readonly double Compute()
|
|
||||||
{
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
public enum Operation
|
|
||||||
{
|
|
||||||
Unspecified = 0,
|
|
||||||
EQ,
|
|
||||||
NE,
|
|
||||||
GT,
|
|
||||||
GE,
|
|
||||||
LT,
|
|
||||||
LE,
|
|
||||||
OR,
|
|
||||||
AND,
|
|
||||||
NOT,
|
|
||||||
LIKE
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
internal struct ReverseMinNorm : INormalizer
|
|
||||||
{
|
|
||||||
internal double min;
|
|
||||||
|
|
||||||
public readonly double Normalize(double w) => (min + 1) / (w + 1);
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
internal struct SelectFilterExpr(uint cbf, FrostFsSelector selector, Collection<FrostFsFilter> filters)
|
|
||||||
{
|
|
||||||
internal uint Cbf { get; } = cbf;
|
|
||||||
internal FrostFsSelector Selector { get; } = selector;
|
|
||||||
internal Collection<FrostFsFilter> Filters { get; } = filters;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
internal readonly struct SigmoidNorm : INormalizer
|
|
||||||
{
|
|
||||||
private readonly double _scale;
|
|
||||||
|
|
||||||
internal SigmoidNorm(double scale)
|
|
||||||
{
|
|
||||||
_scale = scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly double Normalize(double w)
|
|
||||||
{
|
|
||||||
if (_scale == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = w / _scale;
|
|
||||||
|
|
||||||
return x / (1 + x);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,138 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using static FrostFS.SDK.FrostFsNetmapSnapshot;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
|
|
||||||
|
|
||||||
public static class Tools
|
|
||||||
{
|
|
||||||
internal static ulong Distance(ulong x, ulong y)
|
|
||||||
{
|
|
||||||
var acc = x ^ y;
|
|
||||||
acc ^= acc >> 33;
|
|
||||||
acc *= 0xff51afd7ed558ccd;
|
|
||||||
acc ^= acc >> 33;
|
|
||||||
acc *= 0xc4ceb9fe1a85ec53;
|
|
||||||
acc ^= acc >> 33;
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static double ReverceNormalize(double r, double w)
|
|
||||||
{
|
|
||||||
return (r + 1) / (w + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static double Normalize(double r, double w)
|
|
||||||
{
|
|
||||||
if (r == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = w / r;
|
|
||||||
return x / (1 + x);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void AppendWeightsTo(FrostFsNodeInfo[] nodes, Func<FrostFsNodeInfo, double> wf, ref double[] weights)
|
|
||||||
{
|
|
||||||
if (weights.Length < nodes.Length)
|
|
||||||
{
|
|
||||||
weights = new double[nodes.Length];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < nodes.Length; i++)
|
|
||||||
{
|
|
||||||
weights[i] = wf(nodes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static List<T> SortHasherSliceByWeightValue<T>(List<T> nodes, Span<double> weights, ulong hash) where T : IHasher
|
|
||||||
{
|
|
||||||
if (nodes.Count == 0)
|
|
||||||
{
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
var allEquals = true;
|
|
||||||
|
|
||||||
if (weights.Length > 1)
|
|
||||||
{
|
|
||||||
for (int i = 1; i < weights.Length; i++)
|
|
||||||
{
|
|
||||||
if (weights[i] != weights[0])
|
|
||||||
{
|
|
||||||
allEquals = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var dist = new double[nodes.Count];
|
|
||||||
|
|
||||||
if (allEquals)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < dist.Length; i++)
|
|
||||||
{
|
|
||||||
var x = nodes[i].Hash();
|
|
||||||
dist[i] = Distance(x, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SortHasherByDistance(nodes, dist, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < dist.Length; i++)
|
|
||||||
{
|
|
||||||
var d = Distance(nodes[i].Hash(), hash);
|
|
||||||
dist[i] = (ulong.MaxValue - d) * weights[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return SortHasherByDistance(nodes, dist, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static List<T> SortHasherByDistance<T, N>(List<T> nodes, N[] dist, bool asc)
|
|
||||||
{
|
|
||||||
IndexedValue<T, N>[] indexes = new IndexedValue<T, N>[nodes.Count];
|
|
||||||
for (int i = 0; i < dist.Length; i++)
|
|
||||||
{
|
|
||||||
indexes[i] = new IndexedValue<T, N>() { nodeInfo = nodes[i], dist = dist[i] };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asc)
|
|
||||||
{
|
|
||||||
return new List<T>(indexes
|
|
||||||
.OrderBy(x => x.dist)
|
|
||||||
.Select(x => x.nodeInfo).ToArray());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new List<T>(indexes
|
|
||||||
.OrderByDescending(x => x.dist)
|
|
||||||
.Select(x => x.nodeInfo));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Func<FrostFsNodeInfo, double> DefaultWeightFunc(IReadOnlyList<FrostFsNodeInfo> nodes)
|
|
||||||
{
|
|
||||||
MeanAgg mean = new();
|
|
||||||
MinAgg minV = new();
|
|
||||||
|
|
||||||
foreach (var node in nodes)
|
|
||||||
{
|
|
||||||
mean.Add(node.GetCapacity());
|
|
||||||
minV.Add(node.Price);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewWeightFunc(
|
|
||||||
NewSigmoidNorm(mean.Compute()),
|
|
||||||
NewReverseMinNorm(minV.Compute()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct IndexedValue<T, N>
|
|
||||||
{
|
|
||||||
internal T nodeInfo;
|
|
||||||
internal N dist;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
using FrostFS.Refs;
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsAddress
|
|
||||||
{
|
|
||||||
private FrostFsObjectId? frostFsObjectId;
|
|
||||||
private FrostFsContainerId? frostFsContainerId;
|
|
||||||
private ObjectID? objectId;
|
|
||||||
private ContainerID? containerId;
|
|
||||||
|
|
||||||
public FrostFsAddress(FrostFsContainerId frostFsContainerId, FrostFsObjectId frostFsObjectId)
|
|
||||||
{
|
|
||||||
FrostFsObjectId = frostFsObjectId ?? throw new System.ArgumentNullException(nameof(frostFsObjectId));
|
|
||||||
FrostFsContainerId = frostFsContainerId ?? throw new System.ArgumentNullException(nameof(frostFsContainerId));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal FrostFsAddress(ObjectID objectId, ContainerID containerId)
|
|
||||||
{
|
|
||||||
ObjectId = objectId ?? throw new System.ArgumentNullException(nameof(objectId));
|
|
||||||
ContainerId = containerId ?? throw new System.ArgumentNullException(nameof(containerId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsObjectId FrostFsObjectId
|
|
||||||
{
|
|
||||||
get => frostFsObjectId ??= objectId!.ToModel();
|
|
||||||
set => frostFsObjectId = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FrostFsContainerId FrostFsContainerId
|
|
||||||
{
|
|
||||||
get => frostFsContainerId ??= containerId!.ToModel();
|
|
||||||
set => frostFsContainerId = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectID ObjectId
|
|
||||||
{
|
|
||||||
get => objectId ??= frostFsObjectId!.ToMessage();
|
|
||||||
set => objectId = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContainerID ContainerId
|
|
||||||
{
|
|
||||||
get => containerId ??= frostFsContainerId!.ToMessage();
|
|
||||||
set => containerId = value;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public struct FrostFsAttributePair(string key, string value) : System.IEquatable<FrostFsAttributePair>
|
|
||||||
{
|
|
||||||
public string Key { get; set; } = key;
|
|
||||||
|
|
||||||
public string Value { get; set; } = value;
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
if (obj == null || obj is not FrostFsAttributePair)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return Equals((FrostFsAttributePair)obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return Key.GetHashCode() ^ Value.GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(FrostFsAttributePair left, FrostFsAttributePair right)
|
|
||||||
{
|
|
||||||
return left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(FrostFsAttributePair left, FrostFsAttributePair right)
|
|
||||||
{
|
|
||||||
return !(left == right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(FrostFsAttributePair other)
|
|
||||||
{
|
|
||||||
return GetHashCode().Equals(other.GetHashCode());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsHeaderResult
|
|
||||||
{
|
|
||||||
public FrostFsObjectHeader? HeaderInfo { get; internal set; }
|
|
||||||
|
|
||||||
public FrostFsSplitInfo? SplitInfo { get; internal set; }
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsLargeObject(FrostFsContainerId container) : FrostFsObject(container)
|
|
||||||
{
|
|
||||||
public ulong PayloadLength
|
|
||||||
{
|
|
||||||
get { return Header!.PayloadLength; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsLinkObject : FrostFsObject
|
|
||||||
{
|
|
||||||
public FrostFsLinkObject(FrostFsContainerId containerId,
|
|
||||||
SplitId splitId,
|
|
||||||
FrostFsObjectHeader largeObjectHeader,
|
|
||||||
IList<FrostFsObjectId> children)
|
|
||||||
: base(containerId)
|
|
||||||
{
|
|
||||||
Header!.Split = new FrostFsSplit(splitId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
largeObjectHeader,
|
|
||||||
null,
|
|
||||||
new ReadOnlyCollection<FrostFsObjectId>(children));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsObject
|
|
||||||
{
|
|
||||||
// private byte[]? _payloadBytes;
|
|
||||||
// private ReadOnlyMemory<byte> _payloadMemory;
|
|
||||||
// private bool _isInitPayloadMemory;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates new instance from <c>ObjectHeader</c>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="header"></param> <summary>
|
|
||||||
public FrostFsObject(FrostFsObjectHeader header)
|
|
||||||
{
|
|
||||||
Header = header;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates new instance with specified parameters
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container"></param>
|
|
||||||
/// <param name="objectType"></param>
|
|
||||||
public FrostFsObject(FrostFsContainerId container, FrostFsObjectType objectType = FrostFsObjectType.Regular)
|
|
||||||
{
|
|
||||||
Header = new FrostFsObjectHeader(containerId: container, type: objectType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Header contains metadata for the object
|
|
||||||
/// </summary>
|
|
||||||
/// <value></value>
|
|
||||||
public FrostFsObjectHeader Header { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The value is calculated internally as a hash of ObjectHeader. Do not use pre-calculated value is the object has been changed.
|
|
||||||
/// </summary>
|
|
||||||
public FrostFsObjectId? ObjectId
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A payload is obtained via stream reader
|
|
||||||
/// </summary>
|
|
||||||
/// <value>Reader for received data</value>
|
|
||||||
public IObjectReader? ObjectReader { get; set; }
|
|
||||||
|
|
||||||
public ReadOnlyMemory<byte> SingleObjectPayload { get; set; }
|
|
||||||
|
|
||||||
//public ReadOnlyMemory<byte> SingleObjectPayloadMemory
|
|
||||||
//{
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// if (!_isInitPayloadMemory)
|
|
||||||
// {
|
|
||||||
// _payloadMemory = _payloadBytes.AsMemory();
|
|
||||||
// _isInitPayloadMemory = true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return _payloadMemory;
|
|
||||||
// }
|
|
||||||
// set
|
|
||||||
// {
|
|
||||||
// _payloadMemory = value;
|
|
||||||
// _isInitPayloadMemory = true;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provide SHA256 hash of the payload. If null, the hash is calculated by internal logic
|
|
||||||
/// </summary>
|
|
||||||
public byte[]? PayloadHash { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applied only for the last Object in chain in case of manual multipart uploading
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="largeObject">Parent for multipart object</param>
|
|
||||||
public void SetParent(FrostFsObjectHeader largeObjectHeader)
|
|
||||||
{
|
|
||||||
if (Header?.Split == null)
|
|
||||||
throw new ArgumentNullException(nameof(largeObjectHeader), "Split value must not be null");
|
|
||||||
|
|
||||||
Header.Split.ParentHeader = largeObjectHeader;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public interface IObjectFilter
|
|
||||||
{
|
|
||||||
public FrostFsMatchType MatchType { get; set; }
|
|
||||||
public string Key { get; set; }
|
|
||||||
|
|
||||||
string? GetSerializedValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class FrostFsObjectFilter<T>(FrostFsMatchType matchType, string key, T value) : IObjectFilter
|
|
||||||
{
|
|
||||||
public FrostFsMatchType MatchType { get; set; } = matchType;
|
|
||||||
public string Key { get; set; } = key;
|
|
||||||
|
|
||||||
public T Value { get; set; } = value;
|
|
||||||
|
|
||||||
public string? GetSerializedValue()
|
|
||||||
{
|
|
||||||
return Value?.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by Attribute
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="key">Attribute key</param>
|
|
||||||
/// <param name="value">Attribute value</param>
|
|
||||||
public class FilterByAttributePair(FrostFsMatchType matchType, string key, string value) : FrostFsObjectFilter<string>(matchType, key, value) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by ObjectId
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="objectId">ObjectId</param>
|
|
||||||
public class FilterByObjectId(FrostFsMatchType matchType, FrostFsObjectId objectId) : FrostFsObjectFilter<FrostFsObjectId>(matchType, Constants.FilterHeaderObjectID, objectId) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by OwnerId
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="ownerId">ObjectId</param>
|
|
||||||
public class FilterByOwnerId(FrostFsMatchType matchType, FrostFsOwner ownerId) : FrostFsObjectFilter<FrostFsOwner>(matchType, Constants.FilterHeaderOwnerID, ownerId) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by Version
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="version">Version</param>
|
|
||||||
public class FilterByVersion(FrostFsMatchType matchType, FrostFsVersion version) : FrostFsObjectFilter<FrostFsVersion>(matchType, Constants.FilterHeaderVersion, version) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by ContainerId
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="containerId">ContainerId</param>
|
|
||||||
public class FilterByContainerId(FrostFsMatchType matchType, FrostFsContainerId containerId) : FrostFsObjectFilter<FrostFsContainerId>(matchType, Constants.FilterHeaderContainerID, containerId) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by creation Epoch
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="epoch">Creation Epoch</param>
|
|
||||||
public class FilterByEpoch(FrostFsMatchType matchType, ulong epoch) : FrostFsObjectFilter<ulong>(matchType, Constants.FilterHeaderCreationEpoch, epoch) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by Payload Length
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="payloadLength">Payload Length</param>
|
|
||||||
public class FilterByPayloadLength(FrostFsMatchType matchType, ulong payloadLength) : FrostFsObjectFilter<ulong>(matchType, Constants.FilterHeaderPayloadLength, payloadLength) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by Payload Hash
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="payloadHash">Payload Hash</param>
|
|
||||||
public class FilterByPayloadHash(FrostFsMatchType matchType, CheckSum payloadHash) : FrostFsObjectFilter<CheckSum>(matchType, Constants.FilterHeaderPayloadHash, payloadHash) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by Parent
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="parentId">Parent</param>
|
|
||||||
public class FilterByParent(FrostFsMatchType matchType, FrostFsObjectId parentId) : FrostFsObjectFilter<FrostFsObjectId>(matchType, Constants.FilterHeaderParent, parentId) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by SplitId
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="splitId">SplitId</param>
|
|
||||||
public class FilterBySplitId(FrostFsMatchType matchType, SplitId splitId) : FrostFsObjectFilter<SplitId>(matchType, Constants.FilterHeaderSplitID, splitId) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search by Payload Hash
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchType">Match type</param>
|
|
||||||
/// <param name="ecParentId">Payload Hash</param>
|
|
||||||
public class FilterByECParent(FrostFsMatchType matchType, FrostFsObjectId ecParentId) : FrostFsObjectFilter<FrostFsObjectId>(matchType, Constants.FilterHeaderECParent, ecParentId) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search Root objects
|
|
||||||
/// </summary>
|
|
||||||
public class FilterByRootObject() : FrostFsObjectFilter<string>(FrostFsMatchType.Unspecified, Constants.FilterHeaderRoot, string.Empty) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates filter to search objects that are physically stored on the server
|
|
||||||
/// </summary
|
|
||||||
public class FilterByPhysicallyStored() : FrostFsObjectFilter<string>(FrostFsMatchType.Unspecified, Constants.FilterHeaderPhy, string.Empty) { }
|
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using FrostFS.Object;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsObjectHeader(
|
|
||||||
FrostFsContainerId containerId,
|
|
||||||
FrostFsObjectType type = FrostFsObjectType.Regular,
|
|
||||||
FrostFsAttributePair[]? attributes = null,
|
|
||||||
FrostFsSplit? split = null,
|
|
||||||
FrostFsOwner? owner = null,
|
|
||||||
FrostFsVersion? version = null)
|
|
||||||
{
|
|
||||||
private Header? header;
|
|
||||||
private Container.Container.Types.Attribute[]? grpsAttributes;
|
|
||||||
|
|
||||||
public ReadOnlyCollection<FrostFsAttributePair>? Attributes { get; internal set; } =
|
|
||||||
attributes == null ? null :
|
|
||||||
new ReadOnlyCollection<FrostFsAttributePair>(attributes);
|
|
||||||
|
|
||||||
public FrostFsContainerId ContainerId { get; } = containerId;
|
|
||||||
|
|
||||||
public ulong PayloadLength { get; set; }
|
|
||||||
|
|
||||||
public byte[]? PayloadCheckSum { get; set; }
|
|
||||||
|
|
||||||
public FrostFsObjectType ObjectType { get; } = type;
|
|
||||||
|
|
||||||
public FrostFsOwner? OwnerId { get; internal set; } = owner;
|
|
||||||
|
|
||||||
public FrostFsVersion? Version { get; internal set; } = version;
|
|
||||||
|
|
||||||
public FrostFsSplit? Split { get; internal set; } = split;
|
|
||||||
|
|
||||||
internal Container.Container.Types.Attribute[]? GetGrpsAttributes()
|
|
||||||
{
|
|
||||||
grpsAttributes ??= Attributes?
|
|
||||||
.Select(a => new Container.Container.Types.Attribute { Key = a.Key, Value = a.Value })
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return grpsAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Header GetHeader()
|
|
||||||
{
|
|
||||||
if (header == null)
|
|
||||||
{
|
|
||||||
var objTypeName = ObjectType switch
|
|
||||||
{
|
|
||||||
FrostFsObjectType.Regular => Object.ObjectType.Regular,
|
|
||||||
FrostFsObjectType.Lock => Object.ObjectType.Lock,
|
|
||||||
FrostFsObjectType.Tombstone => Object.ObjectType.Tombstone,
|
|
||||||
_ => throw new ArgumentException($"Unknown ObjectType. Value: '{ObjectType}'.")
|
|
||||||
};
|
|
||||||
|
|
||||||
this.header = new Header
|
|
||||||
{
|
|
||||||
OwnerId = OwnerId?.ToMessage(),
|
|
||||||
Version = Version?.ToMessage(),
|
|
||||||
ContainerId = ContainerId.ToMessage(),
|
|
||||||
ObjectType = objTypeName,
|
|
||||||
PayloadLength = PayloadLength
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Attributes != null)
|
|
||||||
{
|
|
||||||
foreach (var attribute in Attributes)
|
|
||||||
{
|
|
||||||
this.header.Attributes.Add(attribute.ToMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var split = Split;
|
|
||||||
if (split != null)
|
|
||||||
{
|
|
||||||
this.header.Split = new Header.Types.Split
|
|
||||||
{
|
|
||||||
SplitId = split!.SplitId != null ? split.SplitId.GetSplitId() : null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.header;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsObjectId(string id)
|
|
||||||
{
|
|
||||||
public string Value { get; } = id;
|
|
||||||
|
|
||||||
public static FrostFsObjectId FromHash(ReadOnlySpan<byte> hash)
|
|
||||||
{
|
|
||||||
if (hash.Length != Constants.Sha256HashLength)
|
|
||||||
throw new FormatException("ObjectID must be a sha256 hash.");
|
|
||||||
|
|
||||||
return new FrostFsObjectId(Base58.Encode(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] ToHash()
|
|
||||||
{
|
|
||||||
return Base58.Decode(Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
using FrostFS.Refs;
|
|
||||||
using FrostFS.SDK.Client.Mappers.GRPC;
|
|
||||||
using FrostFS.SDK.Cryptography;
|
|
||||||
|
|
||||||
|
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public class FrostFsOwner(string id)
|
|
||||||
{
|
|
||||||
private OwnerID? ownerID;
|
|
||||||
|
|
||||||
public string Value { get; } = id;
|
|
||||||
|
|
||||||
public static FrostFsOwner FromKey(ECDsa key)
|
|
||||||
{
|
|
||||||
return new FrostFsOwner(key.PublicKey().PublicKeyToAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
internal OwnerID OwnerID
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
ownerID ??= this.ToMessage();
|
|
||||||
return ownerID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] ToHash()
|
|
||||||
{
|
|
||||||
return Base58.Decode(Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Value;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
namespace FrostFS.SDK;
|
|
||||||
|
|
||||||
public readonly struct FrostFsRange(ulong offset, ulong length) : System.IEquatable<FrostFsRange>
|
|
||||||
{
|
|
||||||
public ulong Offset { get; } = offset;
|
|
||||||
|
|
||||||
public ulong Length { get; } = length;
|
|
||||||
|
|
||||||
public override readonly bool Equals(object obj) => this == (FrostFsRange)obj;
|
|
||||||
|
|
||||||
public override readonly int GetHashCode() => $"{Offset}{Length}".GetHashCode();
|
|
||||||
|
|
||||||
public static bool operator ==(FrostFsRange left, FrostFsRange right)
|
|
||||||
{
|
|
||||||
return left.Equals(right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(FrostFsRange left, FrostFsRange right)
|
|
||||||
{
|
|
||||||
return !(left == right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly bool Equals(FrostFsRange other)
|
|
||||||
{
|
|
||||||
return this == other;
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue