Compare commits

...

No commits in common. "master" and "empty" have entirely different histories.

259 changed files with 2 additions and 24067 deletions

View file

@ -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

View file

@ -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 }}'

View file

@ -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
View file

@ -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/

View file

@ -1,3 +0,0 @@
.* @PavelGrossSpb
.forgejo/.* @potyarkin
Makefile @potyarkin

View file

@ -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
View file

@ -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.

View file

@ -1,82 +1,3 @@
# frostfs-sdk-csharp
# WIP area: this repo is just a fork!
C# implementation of FrostFS SDK.
## 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);
}
```
Useful things may be published only in [other branches](../../../branches)

View file

@ -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());
}
}

View file

@ -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);
}
}

View file

@ -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());
}
}

View file

@ -1,7 +0,0 @@
namespace FrostFS.SDK.Client;
public enum ConditionKindType
{
Resource,
Request
}

View file

@ -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,
}

View file

@ -1,10 +0,0 @@
namespace FrostFS.SDK.Client;
public enum FrostFsTargetType
{
Undefined,
Namespace,
Container,
User,
Group
}

View file

@ -1,9 +0,0 @@
namespace FrostFS.SDK.Client;
public enum RuleStatus
{
Allow,
NoRuleFound,
AccessDenied,
QuotaLimitReached
}

View file

@ -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; }
}

View file

@ -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; }
}

View file

@ -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
}

View file

@ -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());
}
}

View file

@ -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);
}
}

View file

@ -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")]

View file

@ -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;
}

View file

@ -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());
}

View file

@ -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)
{
}
}

View file

@ -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)
{
}
}

View file

@ -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)
{
}
}

View file

@ -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)
{
}
}

View file

@ -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)
{
}
}

View file

@ -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)
{
}
}

View file

@ -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]
];
}
}

View file

@ -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>

View file

@ -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();
}
}

View file

@ -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")]

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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);
}

View file

@ -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());
}
}

View file

@ -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));
}
}

View file

@ -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
};
}
}

View file

@ -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());
}
}

View file

@ -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
);
}
}

View file

@ -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())]
);
}
}

View file

@ -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;
}
}

View file

@ -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)
};
}
}

View file

@ -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);
}
}

View file

@ -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()
};
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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!;
}
}

View file

@ -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;
}
}

View file

@ -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)
};
}
}

View file

@ -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]))));
}
}

View file

@ -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];
}
}

View file

@ -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());
}
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -1,10 +0,0 @@
namespace FrostFS.SDK;
public enum FrostFsMatchType
{
Unspecified = 0,
Equals = 1,
NotEquals = 2,
KeyAbsent = 3,
StartsWith = 4
}

View file

@ -1,8 +0,0 @@
namespace FrostFS.SDK;
public enum FrostFsObjectType
{
Regular = 0,
Tombstone = 1,
Lock = 3
}

View file

@ -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
}

View file

@ -1,9 +0,0 @@
namespace FrostFS.SDK;
public enum NodeState
{
Unspecified = 0,
Online = 1,
Offline = 2,
Maintenance = 3
}

View file

@ -1,8 +0,0 @@
namespace FrostFS.SDK;
public enum SignatureScheme
{
EcdsaSha512,
EcdsaRfc6979Sha256,
EcdsaRfc6979Sha256WalletConnect
}

View file

@ -1,7 +0,0 @@
namespace FrostFS.SDK;
public class CallStatistics
{
public string? MethodName { get; set; }
public long ElapsedMicroSeconds { get; set; }
}

View file

@ -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("-", "");
}
}

View file

@ -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";
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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,
};
}
}

View file

@ -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}";
}
}

View file

@ -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; }
}
}

View file

@ -1,7 +0,0 @@
namespace FrostFS.SDK;
struct NodeAttrPair
{
internal string attr;
internal FrostFsNodeInfo[] nodes;
}

View file

@ -1,8 +0,0 @@
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
public enum FrostFsClause
{
Unspecified = 0,
Same,
Distinct
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -1,8 +0,0 @@
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
internal interface IAggregator
{
void Add(double d);
double Compute();
}

View file

@ -1,6 +0,0 @@
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
internal interface IHasher
{
ulong Hash();
}

View file

@ -1,6 +0,0 @@
namespace FrostFS.SDK.Client.Models.Netmap.Placement;
interface INormalizer
{
double Normalize(double w);
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -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
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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());
}
}

View file

@ -1,8 +0,0 @@
namespace FrostFS.SDK;
public class FrostFsHeaderResult
{
public FrostFsObjectHeader? HeaderInfo { get; internal set; }
public FrostFsSplitInfo? SplitInfo { get; internal set; }
}

View file

@ -1,9 +0,0 @@
namespace FrostFS.SDK;
public class FrostFsLargeObject(FrostFsContainerId container) : FrostFsObject(container)
{
public ulong PayloadLength
{
get { return Header!.PayloadLength; }
}
}

View file

@ -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));
}
}

View file

@ -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;
}
}

View file

@ -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) { }

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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