HashLib4CSharp-用C#编写的综合哈希库

异或

成员
已加入
2020年5月28日
留言内容
16
编程经验
3-5
HashLib4CSharp 是用C#编写的全面且易于使用的哈希库。它提供了灵活的界面,使使用变得轻而易举。散列可以单次通过或增量方式进行。它还提供了克隆每个哈希对象内部状态的方法。
它提供 适配器 使其易于插入接受标准.NET的方法的类 哈希算法, HMAC 或者 派生字节 根据需要抽象基类。

该库的所有功能都使用 单位 framework.

该库是根据NET Standard 2.1构建的。

*请注意,这是我的C#端口 HashLib4Pascal 具有各种改进的库。

GitHub储存库网址
HashLib4CSharp
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,607
地点
弗吉尼亚州切萨皮克
编程经验
10+
@ Xor-el :为什么在这里定义自己的例外:
//github.com/Xor-el/HashLib4CSharp/blob/master/HashLib4CSharp/src/Utils/HashLibException.cs:
/*
(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
{ *                             HashLib4CSharp Library                              * }
{ *                      Copyright (c) 2020 Ugochukwu Mmaduekwe                     * }
{ *                 GitHub Profile URL <//github.com/Xor-el>                  * }

{ *  Distributed under the MIT software license, see the accompanying LICENSE file  * }
{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }

{ *                              Acknowledgements:                                  * }
{ *                                                                                 * }
{ *   This library was sponsored by Sphere 10 Software (//www.sphere10.com)   * }
{ *         for the purposes of supporting the XXX (//YYY) project.           * }
{ *                                                                                 * }
(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
*/

using System;

namespace HashLib4CSharp.Utils
{
    public class HashLibException : Exception
    {
        protected HashLibException(string message) : base(message)
        {
        }
    }

    public sealed class NullReferenceHashLibException : HashLibException
    {
        internal NullReferenceHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class InvalidOperationHashLibException : HashLibException
    {
        internal InvalidOperationHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class IndexOutOfRangeHashLibException : HashLibException
    {
        internal IndexOutOfRangeHashLibException(string message) : base(message)
        {
        }
    }

    public class ArgumentHashLibException : HashLibException
    {
        internal ArgumentHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentInvalidHashLibException : ArgumentHashLibException
    {
        internal ArgumentInvalidHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentNullHashLibException : ArgumentHashLibException
    {
        public ArgumentNullHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentOutOfRangeHashLibException : ArgumentHashLibException
    {
        internal ArgumentOutOfRangeHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class NotImplementedHashLibException : HashLibException
    {
        internal NotImplementedHashLibException(string message) : base(message)
        {
        }
    }

    public class IOHashLibException : HashLibException
    {
        internal IOHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class FileNotFoundHashLibException : IOHashLibException
    {
        internal FileNotFoundHashLibException(string message) : base(message)
        {
        }
    }
}

.NET Framework何时 文件资料 says:
选择标准例外
当您必须引发福彩12选5走势图时,通常可以在.NET Framework中使用现有的福彩12选5走势图类型,而不是实现自定义福彩12选5走势图。您应该在以下两种情况下使用标准福彩12选5走势图类型:
  • 您将引发由使用错误(即,调用您的方法的开发人员在程序逻辑中的错误)引起的福彩12选5走势图。 ...
  • 您正在处理一个错误,该错误可以与现有.NET Framework福彩12选5走势图传达给调用方。您应该抛出最可能派生的福彩12选5走势图。 ...

实现自定义福彩12选5走势图
在以下情况下,使用现有的.NET Framework福彩12选5走势图来处理错误情况是不够的:

  • 当福彩12选5走势图反映了无法映射到现有.NET Framework福彩12选5走势图的唯一程序错误时。
  • 当福彩12选5走势图需要的处理与适用于现有.NET Framework福彩12选5走势图的处理不同时,或者必须将该福彩12选5走势图与相似的福彩12选5走势图进行区分。例如,如果您抛出一个 ArgumentOutOfRangeException 解析超出目标整数类型范围的字符串的数字表示形式时出现福彩12选5走势图,对于因调用方在调用方法时未提供适当的约束值而导致的错误,您不希望使用相同的福彩12选5走势图。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,607
地点
弗吉尼亚州切萨皮克
编程经验
10+
Also there is a lot of use of unsafe pointers in the code. I've only done a brief scan so far, but I believe a majority of it can be replaced with using 跨度 <byte> so that you don't have to compile with the unsafe flag on.
 

异或

成员
已加入
2020年5月28日
留言内容
16
编程经验
3-5
Also there is a lot of use of unsafe pointers in the code. I've only done a brief scan so far, but I believe a majority of it can be replaced with using 跨度 <byte> so that you don't have to compile with the unsafe flag on.

使用跨度的示例的任何建议<byte>会比不安全的代码更合适?
 

异或

成员
已加入
2020年5月28日
留言内容
16
编程经验
3-5
@ Xor-el :为什么在这里定义自己的例外:
//github.com/Xor-el/HashLib4CSharp/blob/master/HashLib4CSharp/src/Utils/HashLibException.cs:
/*
(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
{ *                             HashLib4CSharp Library                              * }
{ *                      Copyright (c) 2020 Ugochukwu Mmaduekwe                     * }
{ *                 GitHub Profile URL <//github.com/Xor-el>                  * }

{ *  Distributed under the MIT software license, see the accompanying LICENSE file  * }
{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }

{ *                              Acknowledgements:                                  * }
{ *                                                                                 * }
{ *   This library was sponsored by Sphere 10 Software (//www.sphere10.com)   * }
{ *         for the purposes of supporting the XXX (//YYY) project.           * }
{ *                                                                                 * }
(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
*/

using System;

namespace HashLib4CSharp.Utils
{
    public class HashLibException : Exception
    {
        protected HashLibException(string message) : base(message)
        {
        }
    }

    public sealed class NullReferenceHashLibException : HashLibException
    {
        internal NullReferenceHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class InvalidOperationHashLibException : HashLibException
    {
        internal InvalidOperationHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class IndexOutOfRangeHashLibException : HashLibException
    {
        internal IndexOutOfRangeHashLibException(string message) : base(message)
        {
        }
    }

    public class ArgumentHashLibException : HashLibException
    {
        internal ArgumentHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentInvalidHashLibException : ArgumentHashLibException
    {
        internal ArgumentInvalidHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentNullHashLibException : ArgumentHashLibException
    {
        public ArgumentNullHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class ArgumentOutOfRangeHashLibException : ArgumentHashLibException
    {
        internal ArgumentOutOfRangeHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class NotImplementedHashLibException : HashLibException
    {
        internal NotImplementedHashLibException(string message) : base(message)
        {
        }
    }

    public class IOHashLibException : HashLibException
    {
        internal IOHashLibException(string message) : base(message)
        {
        }
    }

    public sealed class FileNotFoundHashLibException : IOHashLibException
    {
        internal FileNotFoundHashLibException(string message) : base(message)
        {
        }
    }
}

.NET Framework何时 文件资料 says:

非常感谢这个建议,将按照指示实施。
 

异或

成员
已加入
2020年5月28日
留言内容
16
编程经验
3-5
@跳伞 ,对于这种情况

"您不希望对调用方在调用方法时未提供适当的约束值而导致的错误使用相同的福彩12选5走势图"

您建议使用哪种类型的内置福彩12选5走势图?是"ArgumentException"这样的情况好吗?
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,607
地点
弗吉尼亚州切萨皮克
编程经验
10+
我倾向于使用ArgumentException并放置有关参数为何无效的更详细的消息。仅在需要让调用者能够区分由我创建自定义福彩12选5走势图的单行代码导致的多种ArgumentException福彩12选5走势图的情况下。该自定义福彩12选5走势图仍将源自我可以找到的最特定的内置福彩12选5走势图,但是我将创建一个子类,以进一步对其进行区分。这样,调用方可以选择捕获特定的福彩12选5走势图,也可以选择捕获更通用的福彩12选5走势图。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,607
地点
弗吉尼亚州切萨皮克
编程经验
10+
您能否建议合适的注释标题样式。
我建议只是保持简单:
C#:
/*
HashLib4CSharp Library
Copyright (c) 2020 Ugochukwu Mmaduekwe
GitHub Profile URL <//github.com/Xor-el>

Distributed under the MIT software license, see the accompanying LICENSE file
or visit http://www.opensource.org/licenses/mit-license.php.

Acknowledgements:
This library was sponsored by Sphere 10 Software (//www.sphere10.com)
for the purposes of supporting the XXX (//YYY) project.
*/
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,607
地点
弗吉尼亚州切萨皮克
编程经验
10+
我看到您收到了我昨晚发送的有关性能测试用户界面的2个请求请求。我可能会尽快处理您的回购请求。我一直在调试CRC32代码,因为我的Span实现未通过单元测试。大约5分钟前才知道。我将很快在这里发布不安全代码与跨度代码的比较。
 

异或

成员
已加入
2020年5月28日
留言内容
16
编程经验
3-5
我看到您收到了我昨晚发送的有关性能测试用户界面的2个请求请求。我可能会尽快处理您的回购请求。我一直在调试CRC32代码,因为我的Span实现未通过单元测试。大约5分钟前才知道。我将很快在这里发布不安全代码与跨度代码的比较。
是的,我做到了,谢谢那些,也非常感谢您的时间。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,607
地点
弗吉尼亚州切萨皮克
编程经验
10+
So here is a version of the CRC32 computation using 跨度 <T>:
使用Span并在紧密循环内取出分支:
protected void LocalCrcCompute(uint[][] crcTable, byte[] data, int index, int length)
{
    if (data == null) throw new ArgumentNullHashLibException(nameof(data));
    Debug.Assert(index >= 0);
    Debug.Assert(length >= 0);
    Debug.Assert(index + length <= data.Length);

    const int unroll = 4;
    const int bytesAtOnce = 16 * unroll;

    var crc = ~CurrentCRC;
    var leftovers = BitConverter.IsLittleEndian ? ComputeLittleEndianBlocks()
        : ComputeBigEndianBlocks();

    // remaining 1 to 63 bytes (standard algorithm)
    foreach (var b in leftovers)
        crc = (crc >> 8) ^ crcTable[0][(crc & 0xFF) ^ b];

    CurrentCRC = ~crc;

    ReadOnlySpan<byte> ComputeLittleEndianBlocks()
    {
        var dataSpan = data.AsSpan(index, length);
        while (dataSpan.Length >= bytesAtOnce)
        {
            var dataUints = MemoryMarshal.Cast<byte, uint>(dataSpan);
            for (int unrolling = 0; unrolling < unroll; unrolling++, dataUints = dataUints.Slice(4))
            {
                var one = dataUints[0] ^ crc;
                var two = dataUints[1];
                var three = dataUints[2];
                var four = dataUints[3];

                crc = crcTable[0][(four >> 24) & 0xFF] ^
                    crcTable[1][(four >> 16) & 0xFF] ^
                    crcTable[2][(four >> 8) & 0xFF] ^
                    crcTable[3][four & 0xFF] ^
                    crcTable[4][(three >> 24) & 0xFF] ^
                    crcTable[5][(three >> 16) & 0xFF] ^
                    crcTable[6][(three >> 8) & 0xFF] ^
                    crcTable[7][three & 0xFF] ^
                    crcTable[8][(two >> 24) & 0xFF] ^
                    crcTable[9][(two >> 16) & 0xFF] ^
                    crcTable[10][(two >> 8) & 0xFF] ^
                    crcTable[11][two & 0xFF] ^
                    crcTable[12][(one >> 24) & 0xFF] ^
                    crcTable[13][(one >> 16) & 0xFF] ^
                    crcTable[14][(one >> 8) & 0xFF] ^
                    crcTable[15][one & 0xFF];
            }

            dataSpan = dataSpan.Slice(bytesAtOnce);
        }
        return dataSpan;
    }

    ReadOnlySpan<byte> ComputeBigEndianBlocks()
    {
        var dataSpan = data.AsSpan(index, length);
        while (dataSpan.Length >= bytesAtOnce)
        {
            var dataUints = MemoryMarshal.Cast<byte, uint>(dataSpan);
            for (int unrolling = 0; unrolling < unroll; unrolling++, dataUints = dataUints.Slice(4))
            {
                var one = dataUints[0] ^ Bits.ReverseBytesUInt32(crc);
                var two = dataUints[1];
                var three = dataUints[2];
                var four = dataUints[3];

                crc = crcTable[0][four & 0xFF] ^
                    crcTable[1][(four >> 8) & 0xFF] ^
                    crcTable[2][(four >> 16) & 0xFF] ^
                    crcTable[3][(four >> 24) & 0xFF] ^
                    crcTable[4][three & 0xFF] ^
                    crcTable[5][(three >> 8) & 0xFF] ^
                    crcTable[6][(three >> 16) & 0xFF] ^
                    crcTable[7][(three >> 24) & 0xFF] ^
                    crcTable[8][two & 0xFF] ^
                    crcTable[9][(two >> 8) & 0xFF] ^
                    crcTable[10][(two >> 16) & 0xFF] ^
                    crcTable[11][(two >> 24) & 0xFF] ^
                    crcTable[12][one & 0xFF] ^
                    crcTable[13][(one >> 8) & 0xFF] ^
                    crcTable[14][(one >> 16) & 0xFF] ^
                    crcTable[15][(one >> 24) & 0xFF];
            }

            dataSpan = dataSpan.Slice(bytesAtOnce);
        }
        return dataSpan;
    }
}

和 here is the original code that used unsafe pointers as well the branch inside the loop:
带有指针和分支的原始不安全代码在紧密循环内:
protected unsafe void LocalCrcCompute(uint[][] crcTable, byte[] data, int index,
                                      int length)
{
    if (data == null) throw new ArgumentNullHashLibException(nameof(data));
    Debug.Assert(index >= 0);
    Debug.Assert(length >= 0);
    Debug.Assert(index + length <= data.Length);

    const int unroll = 4;
    const int bytesAtOnce = 16 * unroll;
    var crc = ~CurrentCRC;

    fixed (byte* dataPtr = data)
    {
        var srcPtr = (uint*) (dataPtr + index);
        while (length >= bytesAtOnce)
        {
            var unrolling = 0;
            while (unrolling < unroll)
            {
                var one = Converters.ReadPCardinalAsUInt32(srcPtr)
                    ^ Converters.le2me_32(crc);
                srcPtr++;
                var two = Converters.ReadPCardinalAsUInt32(srcPtr);
                srcPtr++;
                var three = Converters.ReadPCardinalAsUInt32(srcPtr);
                srcPtr++;
                var four = Converters.ReadPCardinalAsUInt32(srcPtr);
                srcPtr++;

                if (BitConverter.IsLittleEndian)
                {
                    crc = crcTable[0][(four >> 24) & 0xFF] ^ crcTable[1]
                        [(four >> 16) & 0xFF] ^ crcTable[2][(four >> 8) & 0xFF]
                        ^ crcTable[3][four & 0xFF] ^ crcTable[4]
                        [(three >> 24) & 0xFF] ^ crcTable[5][(three >> 16) & 0xFF]
                        ^ crcTable[6][(three >> 8) & 0xFF] ^ crcTable[7]
                        [three & 0xFF] ^ crcTable[8][(two >> 24) & 0xFF] ^ crcTable
                        [9][(two >> 16) & 0xFF] ^ crcTable[10][(two >> 8) & 0xFF]
                        ^ crcTable[11][two & 0xFF] ^ crcTable[12][(one >> 24) & 0xFF]
                        ^ crcTable[13][(one >> 16) & 0xFF] ^ crcTable[14]
                        [(one >> 8) & 0xFF] ^ crcTable[15][one & 0xFF];
                }
                else
                {
                    crc = crcTable[0][four & 0xFF] ^ crcTable[1]
                        [(four >> 8) & 0xFF] ^ crcTable[2][(four >> 16) & 0xFF]
                        ^ crcTable[3][(four >> 24) & 0xFF] ^ crcTable[4]
                        [three & 0xFF] ^ crcTable[5][(three >> 8) & 0xFF] ^ crcTable
                        [6][(three >> 16) & 0xFF] ^ crcTable[7][(three >> 24) & 0xFF]
                        ^ crcTable[8][two & 0xFF] ^ crcTable[9][(two >> 8) & 0xFF]
                        ^ crcTable[10][(two >> 16) & 0xFF] ^ crcTable[11]
                        [(two >> 24) & 0xFF] ^ crcTable[12][one & 0xFF] ^ crcTable
                        [13][(one >> 8) & 0xFF] ^ crcTable[14][(one >> 16) & 0xFF]
                        ^ crcTable[15][(one >> 24) & 0xFF];
                }

                unrolling++;
            }

            length -= bytesAtOnce;
        }

        var srcPtr2 = (byte*) srcPtr;
        // remaining 1 to 63 bytes (standard algorithm)
        while (length != 0)
        {
            crc = (crc >> 8) ^ crcTable[0][(crc & 0xFF) ^ *srcPtr2];
            srcPtr2++;
            length--;
        }

        CurrentCRC = ~crc;
    }
}

在我的旧机器上,这是我得到的相对吞吐量:
C#:
657 659
684 680
676 677
大约在2012年使用旧AMD系统的时间:
                         Castagnoli PKZip  (in MB/s)
Original                    657      659
Original without branch     684      680
Span<T> without branch      676      677
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,607
地点
弗吉尼亚州切萨皮克
编程经验
10+
Yes, it is faster, but also more complex. Its still uses unsafe pointers. The reason I put in that statistic there was to show that using the 跨度 <T> lets you have safer code, but the performance penalty is not that big.

请参阅请求请求4。相关代码为:
C#:
protected unsafe void LocalCrcCompute(uint[][] crcTable, byte[] data, int index,
                                      int length)
{
    if (data == null) throw new ArgumentNullException(nameof(data));
    Debug.Assert(index >= 0);
    Debug.Assert(length >= 0);
    Debug.Assert(index + length <= data.Length);

    const int unroll = 4;
    const int bytesAtOnce = 16 * unroll;
    var crc = ~CurrentCRC;

    if (BitConverter.IsLittleEndian)
        ComputeLittleEndianBlocks();
    else
        ComputeBigEndianBlocks();

    CurrentCRC = ~crc;

    void ComputeLittleEndianBlocks()
    {
        fixed (byte* dataPtr = data)
        {
            var srcPtr = (uint*)(dataPtr + index);
            while (length >= bytesAtOnce)
            {
                var unrolling = 0;
                while (unrolling < unroll)
                {
                    var one = Converters.ReadPCardinalAsUInt32(srcPtr) ^ crc;
                    srcPtr++;
                    var two = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;
                    var three = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;
                    var four = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;

                    crc = crcTable[0][(four >> 24) & 0xFF] ^ crcTable[1]
                        [(four >> 16) & 0xFF] ^ crcTable[2][(four >> 8) & 0xFF]
                        ^ crcTable[3][four & 0xFF] ^ crcTable[4]
                        [(three >> 24) & 0xFF] ^ crcTable[5][(three >> 16) & 0xFF]
                        ^ crcTable[6][(three >> 8) & 0xFF] ^ crcTable[7]
                        [three & 0xFF] ^ crcTable[8][(two >> 24) & 0xFF] ^ crcTable
                        [9][(two >> 16) & 0xFF] ^ crcTable[10][(two >> 8) & 0xFF]
                        ^ crcTable[11][two & 0xFF] ^ crcTable[12][(one >> 24) & 0xFF]
                        ^ crcTable[13][(one >> 16) & 0xFF] ^ crcTable[14]
                        [(one >> 8) & 0xFF] ^ crcTable[15][one & 0xFF];

                    unrolling++;
                }

                length -= bytesAtOnce;
            }

            var srcPtr2 = (byte*)srcPtr;
            // remaining 1 to 63 bytes (standard algorithm)
            while (length != 0)
            {
                crc = (crc >> 8) ^ crcTable[0][(crc & 0xFF) ^ *srcPtr2];
                srcPtr2++;
                length--;
            }
        }
    }


    void ComputeBigEndianBlocks()
    {
        fixed (byte* dataPtr = data)
        {
            var srcPtr = (uint*)(dataPtr + index);
            while (length >= bytesAtOnce)
            {
                var unrolling = 0;
                while (unrolling < unroll)
                {
                    var one = Converters.ReadPCardinalAsUInt32(srcPtr) ^ Bits.ReverseBytesUInt32(crc);
                    srcPtr++;
                    var two = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;
                    var three = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;
                    var four = Converters.ReadPCardinalAsUInt32(srcPtr);
                    srcPtr++;

                    crc = crcTable[0][four & 0xFF] ^ crcTable[1]
                        [(four >> 8) & 0xFF] ^ crcTable[2][(four >> 16) & 0xFF]
                        ^ crcTable[3][(four >> 24) & 0xFF] ^ crcTable[4]
                        [three & 0xFF] ^ crcTable[5][(three >> 8) & 0xFF] ^ crcTable
                        [6][(three >> 16) & 0xFF] ^ crcTable[7][(three >> 24) & 0xFF]
                        ^ crcTable[8][two & 0xFF] ^ crcTable[9][(two >> 8) & 0xFF]
                        ^ crcTable[10][(two >> 16) & 0xFF] ^ crcTable[11]
                        [(two >> 24) & 0xFF] ^ crcTable[12][one & 0xFF] ^ crcTable
                        [13][(one >> 8) & 0xFF] ^ crcTable[14][(one >> 16) & 0xFF]
                        ^ crcTable[15][(one >> 24) & 0xFF];

                    unrolling++;
                }

                length -= bytesAtOnce;
            }

            var srcPtr2 = (byte*)srcPtr;
            // remaining 1 to 63 bytes (standard algorithm)
            while (length != 0)
            {
                crc = (crc >> 8) ^ crcTable[0][(crc & 0xFF) ^ *srcPtr2];
                srcPtr2++;
                length--;
            }
        }
    }
}
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,607
地点
弗吉尼亚州切萨皮克
编程经验
10+
没问题。这是你的图书馆。我猜您将只处理有人弄清楚如何使用您的库在有人报告时导致缓冲区覆盖或特权提升的任何情况。性能为王,这就是为什么我们仍然使用C和C ++之类的语言进行编程的原因……安全性,安全性……是别人的身份被盗或被机器拥有...
 
最佳 底部