/// <summary>
        /// Reads a <see cref="System.Decimal"/> value.
        /// </summary>
        /// <param name="stream">Stream to read the value from.</param>
        /// <returns>The read value.</returns>
        internal decimal ReadPrimitive_Decimal(Stream stream)
        {
            const int elementSize = sizeof(decimal);
            int       bytesRead   = stream.Read(TempBuffer_Buffer, 0, elementSize);

            if (bytesRead < elementSize)
            {
                throw new SerializationException("Unexpected end of stream.");
            }
#if NETSTANDARD2_0 || NETSTANDARD2_1 || NET461
            Buffer.BlockCopy(TempBuffer_Buffer, 0, TempBuffer_Int32, 0, elementSize);
            if (mDeserializingLittleEndian != BitConverter.IsLittleEndian)
            {
                EndiannessHelper.SwapBytes(ref TempBuffer_Int32[0]);
                EndiannessHelper.SwapBytes(ref TempBuffer_Int32[1]);
                EndiannessHelper.SwapBytes(ref TempBuffer_Int32[2]);
                EndiannessHelper.SwapBytes(ref TempBuffer_Int32[3]);
            }

            return(new decimal(TempBuffer_Int32));
#elif NET5_0_OR_GREATER
            var intBuffer = MemoryMarshal.Cast <byte, int>(TempBuffer_Buffer.AsSpan(0, elementSize));
            if (mDeserializingLittleEndian != BitConverter.IsLittleEndian)
            {
                EndiannessHelper.SwapBytes(ref intBuffer[0]);
                EndiannessHelper.SwapBytes(ref intBuffer[1]);
                EndiannessHelper.SwapBytes(ref intBuffer[2]);
                EndiannessHelper.SwapBytes(ref intBuffer[3]);
            }

            return(new decimal(intBuffer));
#else
                        #error Unhandled .NET framework
#endif
        }
        /// <summary>
        /// Writes a <see cref="System.String"/> object.
        /// </summary>
        /// <param name="value">String to write.</param>
        /// <param name="writer">Buffer writer to write the string to.</param>
        internal void WritePrimitive_String(string value, IBufferWriter <byte> writer)
        {
            if (SerializationOptimization == SerializationOptimization.Speed)
            {
                // use UTF-16 encoding
                // => .NET strings are always UTF-16 encoded itself, so no further encoding steps are needed...

                // write the encoded string
                int valueByteCount = value.Length * sizeof(char);
                int maxSize        = 1 + Leb128EncodingHelper.MaxBytesFor32BitValue + valueByteCount;
                var buffer         = writer.GetSpan(maxSize);
                int bufferIndex    = 0;
                buffer[bufferIndex++] = (byte)PayloadType.String_UTF16;
                bufferIndex          += Leb128EncodingHelper.Write(buffer.Slice(bufferIndex), value.Length);
                MemoryMarshal.AsBytes(value.AsSpan()).CopyTo(buffer.Slice(bufferIndex));
                bufferIndex += valueByteCount;
                writer.Advance(bufferIndex);
            }
            else
            {
                // use UTF-8 encoding

                // resize temporary buffer for the encoding the string
                int size = sUtf8Encoding.GetMaxByteCount(value.Length);
                EnsureTemporaryByteBufferSize(size);

                // encode the string
                int valueByteCount = sUtf8Encoding.GetBytes(value, 0, value.Length, TempBuffer_Buffer, 0);

                // write the encoded string
                int maxSize     = 1 + Leb128EncodingHelper.MaxBytesFor32BitValue + valueByteCount;
                var buffer      = writer.GetSpan(maxSize);
                int bufferIndex = 0;
                buffer[bufferIndex++] = (byte)PayloadType.String_UTF8;
                bufferIndex          += Leb128EncodingHelper.Write(buffer.Slice(1), valueByteCount);
                TempBuffer_Buffer.AsSpan().Slice(0, valueByteCount).CopyTo(buffer.Slice(bufferIndex));
                bufferIndex += valueByteCount;
                writer.Advance(bufferIndex);
            }

            // assign an object id to the serialized string
            mSerializedObjectIdTable.Add(value, mNextSerializedObjectId++);
        }
        /// <summary>
        /// Reads a <see cref="System.DateTimeOffset"/> object.
        /// </summary>
        /// <param name="stream">Stream to read the DateTimeOffset object from.</param>
        /// <returns>The read <see cref="System.DateTimeOffset"/> object.</returns>
        internal DateTimeOffset ReadPrimitive_DateTimeOffset(Stream stream)
        {
            const int elementSize = 2 * sizeof(long);
            int       bytesRead   = stream.Read(TempBuffer_Buffer, 0, elementSize);

            if (bytesRead < elementSize)
            {
                throw new SerializationException("Unexpected end of stream.");
            }
            long dateTimeTicks       = MemoryMarshal.Read <long>(TempBuffer_Buffer);
            long timezoneOffsetTicks = MemoryMarshal.Read <long>(TempBuffer_Buffer.AsSpan().Slice(8));

            if (mDeserializingLittleEndian != BitConverter.IsLittleEndian)
            {
                EndiannessHelper.SwapBytes(ref dateTimeTicks);
                EndiannessHelper.SwapBytes(ref timezoneOffsetTicks);
            }

            return(new DateTimeOffset(dateTimeTicks, new TimeSpan(timezoneOffsetTicks)));
        }
        /// <summary>
        /// Writes a <see cref="System.Decimal"/> value.
        /// </summary>
        /// <param name="value">Value to write.</param>
        /// <param name="writer">Buffer writer to write the value to.</param>
        internal void WritePrimitive_Decimal(decimal value, IBufferWriter <byte> writer)
        {
            const int elementSize = sizeof(decimal);
            var       buffer      = writer.GetSpan(1 + elementSize);

            buffer[0] = (byte)PayloadType.Decimal;
#if NETSTANDARD2_0 || NETSTANDARD2_1 || NET461
            int[] bits = decimal.GetBits(value);
            Buffer.BlockCopy(bits, 0, TempBuffer_Buffer, 0, elementSize);
            TempBuffer_Buffer.AsSpan(0, elementSize).CopyTo(buffer.Slice(1));
#elif NET5_0_OR_GREATER
            Span <int> temp = stackalloc int[4];
            decimal.TryGetBits(value, temp, out int valuesWritten);
            Debug.Assert(valuesWritten * sizeof(int) == elementSize);
            MemoryMarshal.Cast <int, byte>(temp).CopyTo(buffer.Slice(1));
#else
                        #error Unhandled .NET framework
#endif
            writer.Advance(1 + elementSize);
        }
        /// <summary>
        /// Reads a <see cref="System.String"/> object (UTF-16 encoding).
        /// </summary>
        /// <param name="stream">Stream to read the string object from.</param>
        /// <returns>The read string.</returns>
        internal unsafe string ReadPrimitive_String_UTF16(Stream stream)
        {
            // read the number of UTF-16 code units
            int codeUnitCount = Leb128EncodingHelper.ReadInt32(stream);
            int size          = codeUnitCount * sizeof(char);

            // read encoded string
            EnsureTemporaryByteBufferSize(size);
            int bytesRead = stream.Read(TempBuffer_Buffer, 0, size);

            if (bytesRead < size)
            {
                throw new SerializationException("Unexpected end of stream.");
            }

            // swap bytes to fix endianness issues, if necessary
            var buffer = MemoryMarshal.Cast <byte, char>(TempBuffer_Buffer.AsSpan(0, bytesRead));

            if (mDeserializingLittleEndian != BitConverter.IsLittleEndian)
            {
                for (int i = 0; i < buffer.Length; i++)
                {
                    EndiannessHelper.SwapBytes(ref buffer[i]);
                }
            }

            // create a string from the buffer
#if NETSTANDARD2_0 || NET461
            string s;
            fixed(char *p = buffer)
            {
                s = new string(p, 0, buffer.Length);
            }
#elif NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER
            string s = new string(buffer);
#else
                        #error Unhandled .NET framework
#endif
            mDeserializedObjectIdTable.Add(mNextDeserializedObjectId++, s);
            return(s);
        }
        /// <summary>
        /// Reads a <see cref="System.Guid"/> object.
        /// </summary>
        /// <param name="stream">Stream to read the Guid object from.</param>
        /// <returns>The read <see cref="System.Guid"/> object.</returns>
        internal Guid ReadPrimitive_Guid(Stream stream)
        {
            const int elementSize = 16;

#if NETSTANDARD2_0 || NET461
            byte[] buffer    = new byte[elementSize];
            int    bytesRead = stream.Read(buffer, 0, elementSize);
            if (bytesRead < elementSize)
            {
                throw new SerializationException("Unexpected end of stream.");
            }
            return(new Guid(buffer));
#elif NETSTANDARD2_1 || NET5_0_OR_GREATER
            int bytesRead = stream.Read(TempBuffer_Buffer, 0, elementSize);
            if (bytesRead < elementSize)
            {
                throw new SerializationException("Unexpected end of stream.");
            }
            return(new Guid(TempBuffer_Buffer.AsSpan(0, 16)));
#else
                        #error Unhandled .NET framework
#endif
        }