Example #1
0
            public Marvin Import(byte[] input)
            {
                JavaScriptSerializer _JSS = new JavaScriptSerializer();
                Marvin WIc = _JSS.Deserialize <Marvin>(Encoding.UTF8.GetString(input));

                return(WIc);
            }
Example #2
0
 private static unsafe int GetHashCodeCompat(string content)
 {
     fixed(char *c = content)
     {
         return(Marvin.ComputeHash32OrdinalIgnoreCase(ref *c, content.Length, 0xDEAD, 0xBEEF));
     }
 }
Example #3
0
        public void ComputeHash_Success(ulong seed, string testDataString, ulong expectedHash)
        {
            var  testDataSpan = new Span <byte>(testDataString.HexToByteArray());
            long hash         = Marvin.ComputeHash(ref testDataSpan.DangerousGetPinnableReference(), testDataSpan.Length, seed);

            Assert.Equal((long)expectedHash, hash);
        }
Example #4
0
        public void ComputeHash_Success(ulong seed, string testDataString, ulong expectedHash)
        {
            var  testDataSpan = new Span <byte>(testDataString.HexToByteArray());
            long hash         = Marvin.ComputeHash(testDataSpan, seed);

            Assert.Equal((long)expectedHash, hash);
        }
        private static StringSetInfo GetCollisions(string stringsFile)
        {
            var info = new StringSetInfo();

            info.Name = Path.GetFileNameWithoutExtension(stringsFile);

            var strings = ReadStrings(stringsFile);

            info.StringCount = strings.Count;

            info.Results.Add(GetCollisions(strings, "GetHashCode", s => (ulong)s.GetHashCode()));
            info.Results.Add(GetCollisions(strings, "Fnv1a32Fast", s => Tests.FnvHash32.GetHashCodeFast(s)));
            info.Results.Add(GetCollisions(strings, "Fnv1a64Fast", s => Tests.FnvHash64.GetHashCodeFast(s)));
            info.Results.Add(GetCollisions(strings, "Fnv1a32", s => Tests.FnvHash32.GetHashCode(s)));
            info.Results.Add(GetCollisions(strings, "Fnv1a64", s => Tests.FnvHash64.GetHashCode(s)));
            info.Results.Add(GetCollisions(strings, "djb2", s => (ulong)Tests.Djb2.GetHashCode(s)));
            info.Results.Add(GetCollisions(strings, "xxHash32", s => xxHash32(s)));
            info.Results.Add(GetCollisions(strings, "xxHash64", s => xxHash64(s)));
            info.Results.Add(GetCollisions(strings, "Marvin", s => (ulong)Marvin.ComputeHash32(s)));
            info.Results.Add(GetCollisions(strings, "Murmur3-32", s => (ulong)Tests.MurmurHash3_32.Create(s).Hash));
            info.Results.Add(GetCollisions(strings, "Murmur3-128", s => (ulong)Tests.MurmurHash3.Create(s).Low));
            info.Results.Add(GetCollisions(strings, "Sha256-64", s => (ulong)Tests.StringHash.GetSha256First64(s)));
            info.Results.Add(GetCollisions(strings, "Sha256-32", s => (ulong)Tests.StringHash.GetSha256First32(s)));

            return(info);
        }
Example #6
0
        private unsafe int GetHashCodeOfStringCore(ReadOnlySpan <char> source, CompareOptions options)
        {
            Debug.Assert(!GlobalizationMode.Invariant);
            Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);

            if (source.Length == 0)
            {
                return(0);
            }

            uint flags = LCMAP_SORTKEY | (uint)GetNativeCompareFlags(options);

            fixed(char *pSource = source)
            {
                int sortKeyLength = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName,
                                                                   flags,
                                                                   pSource, source.Length /* in chars */,
                                                                   null, 0,
                                                                   null, null, _sortHandle);

                if (sortKeyLength == 0)
                {
                    throw new ArgumentException(SR.Arg_ExternalException);
                }

                // Note in calls to LCMapStringEx below, the input buffer is specified in wchars (and wchar count),
                // but the output buffer is specified in bytes (and byte count). This is because when generating
                // sort keys, LCMapStringEx treats the output buffer as containing opaque binary data.
                // See https://docs.microsoft.com/en-us/windows/desktop/api/winnls/nf-winnls-lcmapstringex.

                byte[]? borrowedArr = null;
                Span <byte> span = sortKeyLength <= 512 ?
                                   stackalloc byte[512] :
                                   (borrowedArr = ArrayPool <byte> .Shared.Rent(sortKeyLength));

                fixed(byte *pSortKey = &MemoryMarshal.GetReference(span))
                {
                    if (Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName,
                                                       flags,
                                                       pSource, source.Length /* in chars */,
                                                       pSortKey, sortKeyLength,
                                                       null, null, _sortHandle) != sortKeyLength)
                    {
                        throw new ArgumentException(SR.Arg_ExternalException);
                    }
                }

                int hash = Marvin.ComputeHash32(span.Slice(0, sortKeyLength), Marvin.DefaultSeed);

                // Return the borrowed array if necessary.
                if (borrowedArr != null)
                {
                    ArrayPool <byte> .Shared.Return(borrowedArr);
                }

                return(hash);
            }
        }
Example #7
0
        // -----------------------------
        // ---- PAL layer ends here ----
        // -----------------------------

        internal unsafe int GetHashCodeOfStringCore(ReadOnlySpan <char> source, CompareOptions options)
        {
            Debug.Assert(!GlobalizationMode.Invariant);
            Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);

            if (source.Length == 0)
            {
                return(0);
            }

            // according to ICU User Guide the performance of ucol_getSortKey is worse when it is called with null output buffer
            // the solution is to try to fill the sort key in a temporary buffer of size equal 4 x string length
            // 1MB is the biggest array that can be rented from ArrayPool.Shared without memory allocation
            int sortKeyLength = (source.Length > 1024 * 1024 / 4) ? 0 : 4 * source.Length;

            byte[]? borrowedArray = null;
            Span <byte> sortKey = sortKeyLength <= 1024
                ? stackalloc byte[1024]
                : (borrowedArray = ArrayPool <byte> .Shared.Rent(sortKeyLength));

            fixed(char *pSource = &MemoryMarshal.GetReference(source))
            {
                fixed(byte *pSortKey = &MemoryMarshal.GetReference(sortKey))
                {
                    sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKey.Length, options);
                }

                if (sortKeyLength > sortKey.Length) // slow path for big strings
                {
                    if (borrowedArray != null)
                    {
                        ArrayPool <byte> .Shared.Return(borrowedArray);
                    }

                    sortKey = (borrowedArray = ArrayPool <byte> .Shared.Rent(sortKeyLength));

                    fixed(byte *pSortKey = &MemoryMarshal.GetReference(sortKey))
                    {
                        sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKey.Length, options);
                    }
                }
            }

            if (sortKeyLength == 0 || sortKeyLength > sortKey.Length) // internal error (0) or a bug (2nd call failed) in ucol_getSortKey
            {
                throw new ArgumentException(SR.Arg_ExternalException);
            }

            int hash = Marvin.ComputeHash32(sortKey.Slice(0, sortKeyLength), Marvin.DefaultSeed);

            if (borrowedArray != null)
            {
                ArrayPool <byte> .Shared.Return(borrowedArray);
            }

            return(hash);
        }
Example #8
0
 public void Marvin32()
 {
     for (int i = 0; i < data.Count; i++)
     {
         var kvp       = data[i];
         var keyHash   = Marvin.ComputeHash32(kvp.Key);
         var valueHash = Marvin.ComputeHash32(kvp.Value);
     }
 }
Example #9
0
        private ulong CalculateHash2()
        {
            var b = new byte[32];

            Buffer.BlockCopy(_rawBytes, 0, b, 0, 32);

            var aaa = Marvin.ComputeHash(ref b[0], b.Length, 0x82EF4D887A4E55C5);

            return((ulong)aaa);
        }
Example #10
0
        private unsafe int IcuGetHashCodeOfString(ReadOnlySpan <char> source, CompareOptions options)
        {
            Debug.Assert(!GlobalizationMode.Invariant);
            Debug.Assert(!GlobalizationMode.UseNls);
            Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);

            // according to ICU User Guide the performance of ucol_getSortKey is worse when it is called with null output buffer
            // the solution is to try to fill the sort key in a temporary buffer of size equal 4 x string length
            // (The ArrayPool used to have a limit on the length of buffers it would cache; this code was avoiding
            // exceeding that limit to avoid a per-operation allocation, and the performance implications here
            // were not re-evaluated when the limit was lifted.)
            int sortKeyLength = (source.Length > 1024 * 1024 / 4) ? 0 : 4 * source.Length;

            byte[]? borrowedArray = null;
            Span <byte> sortKey = sortKeyLength <= 1024
                ? stackalloc byte[1024]
                : (borrowedArray = ArrayPool <byte> .Shared.Rent(sortKeyLength));

            fixed(char *pSource = &MemoryMarshal.GetNonNullPinnableReference(source))
            {
                fixed(byte *pSortKey = &MemoryMarshal.GetReference(sortKey))
                {
                    sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKey.Length, options);
                }

                if (sortKeyLength > sortKey.Length) // slow path for big strings
                {
                    if (borrowedArray != null)
                    {
                        ArrayPool <byte> .Shared.Return(borrowedArray);
                    }

                    sortKey = (borrowedArray = ArrayPool <byte> .Shared.Rent(sortKeyLength));

                    fixed(byte *pSortKey = &MemoryMarshal.GetReference(sortKey))
                    {
                        sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKey.Length, options);
                    }
                }
            }

            if (sortKeyLength == 0 || sortKeyLength > sortKey.Length) // internal error (0) or a bug (2nd call failed) in ucol_getSortKey
            {
                throw new ArgumentException(SR.Arg_ExternalException);
            }

            int hash = Marvin.ComputeHash32(sortKey.Slice(0, sortKeyLength), Marvin.DefaultSeed);

            if (borrowedArray != null)
            {
                ArrayPool <byte> .Shared.Return(borrowedArray);
            }

            return(hash);
        }
Example #11
0
        private unsafe int GetHashCodeOfStringCore(string source, CompareOptions options)
        {
            Debug.Assert(!_invariantMode);

            Debug.Assert(source != null);
            Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);

            if (source.Length == 0)
            {
                return(0);
            }

            uint flags = LCMAP_SORTKEY | (uint)GetNativeCompareFlags(options);

            fixed(char *pSource = source)
            {
                int sortKeyLength = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName,
                                                                   flags,
                                                                   pSource, source.Length,
                                                                   null, 0,
                                                                   null, null, _sortHandle);

                if (sortKeyLength == 0)
                {
                    throw new ArgumentException(SR.Arg_ExternalException);
                }

                byte[]      borrowedArr = null;
                Span <byte> span        = sortKeyLength <= 512 ?
                                          stackalloc byte[512] :
                                          (borrowedArr = ArrayPool <byte> .Shared.Rent(sortKeyLength));

                fixed(byte *pSortKey = &MemoryMarshal.GetReference(span))
                {
                    if (Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName,
                                                       flags,
                                                       pSource, source.Length,
                                                       pSortKey, sortKeyLength,
                                                       null, null, _sortHandle) != sortKeyLength)
                    {
                        throw new ArgumentException(SR.Arg_ExternalException);
                    }
                }

                int hash = Marvin.ComputeHash32(span.Slice(0, sortKeyLength), Marvin.DefaultSeed);

                // Return the borrowed array if necessary.
                if (borrowedArr != null)
                {
                    ArrayPool <byte> .Shared.Return(borrowedArr);
                }

                return(hash);
            }
        }
            public override int GetHashCode(string?obj)
            {
                if (obj is null)
                {
                    return(0);
                }

                // The Ordinal version of Marvin32 operates over bytes, so convert
                // char count -> byte count. Guaranteed not to integer overflow.
                return(Marvin.ComputeHash32(
                           ref Unsafe.As <char, byte>(ref obj.GetRawStringData()),
                           (uint)obj.Length * sizeof(char),
                           _seed.p0, _seed.p1));
            }
            public override int GetHashCode(string?obj)
            {
                if (obj is null)
                {
                    return(0);
                }

                // The OrdinalIgnoreCase version of Marvin32 operates over chars,
                // so pass in the char count directly.
                return(Marvin.ComputeHash32OrdinalIgnoreCase(
                           ref obj.GetRawStringData(),
                           obj.Length,
                           _seed.p0, _seed.p1));
            }
            public override int GetHashCode(string?obj)
            {
                if (obj is null)
                {
                    return(0);
                }

                // The Ordinal version of Marvin32 operates over bytes.
                // The multiplication from # chars -> # bytes will never integer overflow.
                return(Marvin.ComputeHash32(
                           ref Unsafe.As <char, byte>(ref obj.GetRawStringData()),
                           (uint)obj.Length * 2,
                           _seed.p0, _seed.p1));
            }
        public bool TryGetValue(string key, out string value)
        {
            var entries = this._entries;
            var buckets = this._buckets;

            for (int i = buckets[Marvin.ComputeHash32OrdinalIgnoreCase(ref MemoryMarshal.GetReference(key.AsSpan()), key.Length, 0xDEAD, 0xBEEF) & (buckets.Length - 1)] - 1; (uint)i < (uint)entries.Length; i = entries[i].next)
            {
                if (StringComparer.OrdinalIgnoreCase.Compare(key, entries[i].key) == 0)
                {
                    value = entries[i].value;
                    return(true);
                }
            }

            value = default;
            return(false);
        }
Example #16
0
        public override int GetHashCode()
        {
            if (_hashCode != 0)
            {
                return(_hashCode);
            }

            // For IPv6 addresses, we calculate the hashcode by using Marvin
            // on a stack-allocated array containing the Address bytes and ScopeId.
            int hashCode;

            if (IsIPv6)
            {
                const int   addressAndScopeIdLength = IPAddressParserStatics.IPv6AddressBytes + sizeof(uint);
                Span <byte> addressAndScopeIdSpan   = stackalloc byte[addressAndScopeIdLength];

                MemoryMarshal.AsBytes(new ReadOnlySpan <ushort>(_numbers)).CopyTo(addressAndScopeIdSpan);
                Span <byte> scopeIdSpan  = addressAndScopeIdSpan.Slice(IPAddressParserStatics.IPv6AddressBytes);
                bool        scopeWritten = BitConverter.TryWriteBytes(scopeIdSpan, _addressOrScopeId);
                Debug.Assert(scopeWritten);

                hashCode = Marvin.ComputeHash32(
                    addressAndScopeIdSpan,
                    Marvin.DefaultSeed);
            }
            else
            {
                Span <uint> addressOrScopeIdSpan = stackalloc uint[1];
                addressOrScopeIdSpan[0] = _addressOrScopeId;

                // For IPv4 addresses, we use Marvin on the integer representation of the Address.
                hashCode = Marvin.ComputeHash32(
                    MemoryMarshal.AsBytes(addressOrScopeIdSpan),
                    Marvin.DefaultSeed);
            }

            _hashCode = hashCode;
            return(_hashCode);
        }
Example #17
0
        // -----------------------------
        // ---- PAL layer ends here ----
        // -----------------------------

        internal unsafe int GetHashCodeOfStringCore(ReadOnlySpan <char> source, CompareOptions options)
        {
            Debug.Assert(!_invariantMode);
            Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);

            if (source.Length == 0)
            {
                return(0);
            }

            fixed(char *pSource = source)
            {
                int sortKeyLength = Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, null, 0, options);

                byte[]      borrowedArr = null;
                Span <byte> span        = sortKeyLength <= 512 ?
                                          stackalloc byte[512] :
                                          (borrowedArr = ArrayPool <byte> .Shared.Rent(sortKeyLength));

                fixed(byte *pSortKey = &MemoryMarshal.GetReference(span))
                {
                    if (Interop.Globalization.GetSortKey(_sortHandle, pSource, source.Length, pSortKey, sortKeyLength, options) != sortKeyLength)
                    {
                        throw new ArgumentException(SR.Arg_ExternalException);
                    }
                }

                int hash = Marvin.ComputeHash32(span.Slice(0, sortKeyLength), Marvin.DefaultSeed);

                // Return the borrowed array if necessary.
                if (borrowedArr != null)
                {
                    ArrayPool <byte> .Shared.Return(borrowedArr);
                }

                return(hash);
            }
        }
Example #18
0
        public Program()
        {
            BCom   bcom   = new BCom();
            Marvin marvin = new Marvin();

            NodeBySubscriptionString = new Dictionary <string, BCMsg>();
            NodeBySubscriptionHash   = new Dictionary <ushort, BCMsg>();
            DevicePathTable          = new Dictionary <uint, string>();

            Connect(Properties.Settings.Default.ComPort, 115200);
            ConnectToBroker();

            LampController lc1    = new LampController(client, 0x01945431, "huis/tuin/Lc");
            LampController lc2    = new LampController(client, 0x01945230, "work/desk/1/Lc");
            LampController lc3    = new LampController(client, 0x0F947A10, "work/desk/2/Lc");
            LampController lcWork = new LampController(client, 0x00000000, "group/Lc");

            LampController toren1  = new LampController(client, 0x0194540D, "toren/1");
            LampController toren2  = new LampController(client, 0x0F942418, "toren/2");
            LampController toren3  = new LampController(client, 0x01945B2A, "toren/3");
            LampController toren4  = new LampController(client, 0x0F942423, "toren/4");
            LampController toren5  = new LampController(client, 0x0F943E29, "toren/5");
            LampController toren6  = new LampController(client, 0x0F942E39, "toren/6");
            LampController toren7  = new LampController(client, 0x13D42C64, "toren/7");
            LampController toren8  = new LampController(client, 0x01945418, "toren/8");
            LampController toren9  = new LampController(client, 0x01945429, "toren/9");
            LampController toren10 = new LampController(client, 0x01945B1C, "toren/10");
            LampController toren11 = new LampController(client, 0x0F942F2F, "toren/11");
            LampController toren12 = new LampController(client, 0x0F942420, "toren/12");
            LampController toren13 = new LampController(client, 0x0F945A30, "toren/13");
            LampController toren14 = new LampController(client, 0x0F94241C, "toren/14");

            LampController toren = new LampController(client, 0x00000000, "toren/");

            toren1.relay.LinkedPeripherals.Add(toren.relay);
            toren2.relay.LinkedPeripherals.Add(toren.relay);
            toren3.relay.LinkedPeripherals.Add(toren.relay);
            toren4.relay.LinkedPeripherals.Add(toren.relay);
            toren5.relay.LinkedPeripherals.Add(toren.relay);
            toren6.relay.LinkedPeripherals.Add(toren.relay);
            toren7.relay.LinkedPeripherals.Add(toren.relay);
            toren8.relay.LinkedPeripherals.Add(toren.relay);
            toren9.relay.LinkedPeripherals.Add(toren.relay);
            toren10.relay.LinkedPeripherals.Add(toren.relay);
            toren11.relay.LinkedPeripherals.Add(toren.relay);
            toren12.relay.LinkedPeripherals.Add(toren.relay);
            toren13.relay.LinkedPeripherals.Add(toren.relay);
            toren14.relay.LinkedPeripherals.Add(toren.relay);

            marvin.Add(toren1);
            marvin.Add(toren2);
            marvin.Add(toren3);
            marvin.Add(toren4);
            marvin.Add(toren5);
            marvin.Add(toren6);
            marvin.Add(toren7);
            marvin.Add(toren8);
            marvin.Add(toren9);
            marvin.Add(toren10);
            marvin.Add(toren11);
            marvin.Add(toren12);
            marvin.Add(toren13);
            marvin.Add(toren14);
            marvin.Add(toren);

            marvin.Add(lc1);
            marvin.Add(lc2);
            marvin.Add(lc3);
            marvin.Add(lcWork);



            AddDeviceToHashTable(toren1);
            AddDeviceToHashTable(toren2);
            AddDeviceToHashTable(toren3);
            AddDeviceToHashTable(toren4);
            AddDeviceToHashTable(toren5);
            AddDeviceToHashTable(toren6);
            AddDeviceToHashTable(toren7);
            AddDeviceToHashTable(toren8);
            AddDeviceToHashTable(toren9);
            AddDeviceToHashTable(toren10);
            AddDeviceToHashTable(toren11);
            AddDeviceToHashTable(toren12);
            AddDeviceToHashTable(toren13);
            AddDeviceToHashTable(toren14);
            AddDeviceToHashTable(toren);

            lc1.AddSubscription(lcWork);

            DeviceManager deviceManager = new DeviceManager(client, 0x00001234, "system/network");

            marvin.Add(deviceManager);

            AddDeviceToHashTable(lc1);
            AddDeviceToHashTable(lc2);
            AddDeviceToHashTable(lc3);
            AddDeviceToHashTable(lcWork);

            AddDeviceToHashTable(deviceManager);

            deviceManager.addHashTables(NodeBySubscriptionString, NodeBySubscriptionHash, DevicePathTable);

            lc1.relay.Reset();
            lc3.relay.LinkedPeripherals.Add(lc1.relay);
            lc2.relay.LinkedPeripherals.Add(lc1.relay);

            lc1.relay.LinkedPeripherals.Add(lcWork.relay);
            lc2.relay.LinkedPeripherals.Add(lcWork.relay);
            lc3.relay.LinkedPeripherals.Add(lcWork.relay);

            //Console.WriteLine(marvin.Export());


            Console.ReadLine();
        }
Example #19
0
        private static int ComputeHash32(string key)
        {
            ReadOnlySpan <byte> bytes = key.AsSpan().AsBytes();

            return(Marvin.ComputeHash32(bytes, Marvin.DefaultSeed));
        }
Example #20
0
        private static int ComputeHash32(char[] key, int start, int len)
        {
            ReadOnlySpan <byte> bytes = MemoryMarshal.AsBytes(new ReadOnlySpan <char>(key, start, len));

            return(Marvin.ComputeHash32(bytes, Marvin.DefaultSeed));
        }
Example #21
0
        internal static int ComputeHash32(string key)
        {
            ReadOnlySpan <byte> bytes = MemoryMarshal.AsBytes(key.AsSpan());

            return(Marvin.ComputeHash32(bytes, Marvin.DefaultSeed));
        }
Example #22
0
        private static int ComputeHash32(char[] key, int start, int len)
        {
            ReadOnlySpan <byte> bytes = key.AsSpan(start, len).AsBytes();

            return(Marvin.ComputeHash32(bytes, Marvin.DefaultSeed));
        }
Example #23
0
 public override int GetHashCode()
 {
     // keep this in sync with CompareInfo.GetHashCodeOfString
     return(Marvin.ComputeHash32(_keyData, Marvin.DefaultSeed));
 }
Example #24
0
        private unsafe int NlsGetHashCodeOfString(ReadOnlySpan <char> source, CompareOptions options)
        {
            Debug.Assert(!GlobalizationMode.Invariant);
            Debug.Assert(GlobalizationMode.UseNls);
            Debug.Assert((options & (CompareOptions.Ordinal | CompareOptions.OrdinalIgnoreCase)) == 0);

#if TARGET_WINDOWS
            if (!Environment.IsWindows8OrAbove)
            {
                // On Windows 7 / Server 2008, LCMapStringEx exhibits strange behaviors if the destination
                // buffer is both non-null and too small for the required output. To prevent this from
                // causing issues for us, we need to make an immutable copy of the input buffer so that
                // its contents can't change between when we calculate the required sort key length and
                // when we populate the sort key buffer.

                source = source.ToString();
            }
#endif

            // LCMapStringEx doesn't support passing cchSrc = 0, so if given a null or empty input
            // we'll normalize it to an empty null-terminated string and pass -1 to indicate that
            // the underlying OS function should read until it encounters the null terminator.

            int sourceLength = source.Length;
            if (sourceLength == 0)
            {
                source       = string.Empty;
                sourceLength = -1;
            }

            uint flags = LCMAP_SORTKEY | (uint)GetNativeCompareFlags(options);

            fixed(char *pSource = &MemoryMarshal.GetReference(source))
            {
                int sortKeyLength = Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName,
                                                                   flags,
                                                                   pSource, sourceLength /* in chars */,
                                                                   null, 0,
                                                                   null, null, _sortHandle);

                if (sortKeyLength == 0)
                {
                    throw new ArgumentException(SR.Arg_ExternalException);
                }

                // Note in calls to LCMapStringEx below, the input buffer is specified in wchars (and wchar count),
                // but the output buffer is specified in bytes (and byte count). This is because when generating
                // sort keys, LCMapStringEx treats the output buffer as containing opaque binary data.
                // See https://docs.microsoft.com/en-us/windows/desktop/api/winnls/nf-winnls-lcmapstringex.

                byte[]? borrowedArr = null;
                Span <byte> span = sortKeyLength <= 512 ?
                                   stackalloc byte[512] :
                                   (borrowedArr = ArrayPool <byte> .Shared.Rent(sortKeyLength));

                fixed(byte *pSortKey = &MemoryMarshal.GetReference(span))
                {
                    if (Interop.Kernel32.LCMapStringEx(_sortHandle != IntPtr.Zero ? null : _sortName,
                                                       flags,
                                                       pSource, sourceLength /* in chars */,
                                                       pSortKey, sortKeyLength,
                                                       null, null, _sortHandle) != sortKeyLength)
                    {
                        throw new ArgumentException(SR.Arg_ExternalException);
                    }
                }

                int hash = Marvin.ComputeHash32(span.Slice(0, sortKeyLength), Marvin.DefaultSeed);

                // Return the borrowed array if necessary.
                if (borrowedArr != null)
                {
                    ArrayPool <byte> .Shared.Return(borrowedArr);
                }

                return(hash);
            }
        }