首页 > 其他分享 >lazarus使用CNvcl 中的CNSM4

lazarus使用CNvcl 中的CNSM4

时间:2024-02-25 10:23:35浏览次数:24  
标签:function begin end PCardinal CNSM4 lazarus Result CNvcl integer

参考了Yang杨。老师的代码,原来是delphi代码,因为个人转到lazarus,所以进行移植了。

方法如下:下载最新的CNVCL,CnNative,要修改一下,其它引用单元注释掉

{.$I CnPack.inc} //加个点
CnNative,加个定义{$DEFine SUPPORT_UINT64} 
其它可以参考原文:https://www.cnblogs.com/Yang-YaChao/p/16351961.html
{******************************************************************************}
{                       CnPack For Delphi/C++Builder                           }
{                     中国人自己的开放源码第三方开发包                         }
{                   (C)Copyright 2001-2024 CnPack 开发组                       }
{                   ------------------------------------                       }
{                                                                              }
{            本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修        }
{        改和重新发布这一程序。                                                }
{                                                                              }
{            发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有        }
{        适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。        }
{                                                                              }
{            您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果        }
{        还没有,可访问我们的网站:                                            }
{                                                                              }
{            网站地址:http://www.cnpack.org                                   }
{            电子邮件:[email protected]                                       }
{                                                                              }
{******************************************************************************}

unit CnSM4;
{* |<PRE>
================================================================================
* 软件名称:开发包基础库
* 单元名称:国产分组密码算法 SM4 单元
* 单元作者:刘啸([email protected])
* 备    注:参考国密算法公开文档 SM4 Encryption alogrithm
*           并参考移植 goldboar 的 C 代码*
*           本单元未处理对齐方式,默认只在末尾补 0,
*           如需要 PKCS 之类的支持,,请在外部调用CnPemUtils 中的 PKCS 处理函数
*           另外高版本 Delphi 中请尽量避免使用 AnsiString 参数版本的函数(十六进制除外),
*           避免不可视字符出现乱码影响加解密结果。
* 开发平台:Windows 7 + Delphi 5.0
* 兼容测试:PWin9X/2000/XP/7 + Delphi 5/6 + MaxOS 64
* 本 地 化:该单元中的字符串均符合本地化处理方式
* 修改记录:2022.07.21 V1.7
*               加入 CTR 模式的支持
*           2022.06.21 V1.6
*               加入几个字节数组到十六进制字符串之间的加解密函数
*           2022.04.26 V1.5
*               修改 LongWord 与 Integer 地址转换以支持 MacOS64
*           2022.04.19 V1.4
*               使用初始化向量时内部备份,不修改传入的内容
*           2021.12.12 V1.3
*               加入 CFB/OFB 模式的支持
*           2020.03.24 V1.2
*               增加部分封装函数包括流函数
*           2019.04.15 V1.1
*               支持 Win32/Win64/MacOS
*           2014.09.25 V1.0
*               移植并创建单元
================================================================================
|</PRE>}

interface

{.$I CnPack.inc}

uses
  Classes, SysUtils, CnNative;

const
  CN_SM4_KEYSIZE = 16;
  {* SM4 的密码长度 16 字节}

  CN_SM4_BLOCKSIZE = 16;
  {* SM4 的分块长度 16 字节}

  CN_SM4_NONCESIZE = 8;
  {* SM4 的 CTR 模式下的准初始化向量长度 8 字节}

type
  TCnSM4Key    = array[0..CN_SM4_KEYSIZE - 1] of Byte;
  {* SM4 的加密 Key}

  TCnSM4Buffer = array[0..CN_SM4_BLOCKSIZE - 1] of Byte;
  {* SM4 的加密块}

  TCnSM4Iv     = array[0..CN_SM4_BLOCKSIZE - 1] of Byte;
  {* SM4 的 CBC/CFB/OFB 等的初始化向量}

  TCnSM4Nonce  = array[0..CN_SM4_NONCESIZE - 1] of Byte;
  {* SM4 的 CTR 模式下的初始化向量,与一个八字节计数器拼在一起作为真正的 Iv}

  TCnSM4Context = packed record
    Mode: Integer;                                       {!<  encrypt/decrypt }
    Sk: array[0..CN_SM4_KEYSIZE * 2 - 1] of Cardinal;    {!<  SM4 subkeys     }
  end;

procedure SM4Encrypt(Key: PAnsiChar; Input: PAnsiChar; Output: PAnsiChar; Len: Integer);
{* 原始的 SM4 加密数据块,ECB 模式,将 Input 内的明文内容加密搁到 Output 中
  调用者自行保证 Key 指向内容至少 16 字节,Input 和 Output 指向内容长相等并且都为 Len 字节
  且 Len 必须被 16 整除}

procedure SM4Decrypt(Key: PAnsiChar; Input: PAnsiChar; Output: PAnsiChar; Len: Integer);
{* 原始的 SM4 解密数据块,ECB 模式,将 Input 内的密文内容解密搁到 Output 中
  调用者自行保证 Key 指向内容至少需 16 字节,Input 和 Output 指向内容长相等并且都为 Len 字节
  且 Len 必须被 16 整除}

// ============== 明文字符串与密文十六进制字符串之间的加解密 ===================

procedure SM4EncryptEcbStr(Key: AnsiString; const Input: AnsiString; Output: PAnsiChar);
{* SM4-ECB 封装好的针对 AnsiString 的加密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 #0
  Input    原始待加密字符串,其长度如不是 16 倍数,计算时会被填充 #0 至长度达到 16 的倍数
  Output   Output 输出区,其长度必须大于或等于 (((Length(Input) - 1) div 16) + 1) * 16
 |</PRE>}

procedure SM4DecryptEcbStr(Key: AnsiString; const Input: AnsiString; Output: PAnsiChar);
{* SM4-ECB 封装好的针对 AnsiString 的解密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 #0
  Input    原始待解密字符串,其长度如不是 16 倍数,计算时会被填充 #0 至长度达到 16 的倍数
  Output   Output 输出区,其长度必须大于或等于 (((Length(Input) - 1) div 16) + 1) * 16
 |</PRE>}

procedure SM4EncryptCbcStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
{* SM4-CBC 封装好的针对 AnsiString 的加密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 #0
  Iv       不短于 16 字节的初始化向量,太长则超出部分忽略
  Input    原始待加密字符串
  Output   Output 输出区,其长度必须大于或等于 (((Length(Input) - 1) div 16) + 1) * 16
 |</PRE>}

procedure SM4DecryptCbcStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
{* SM4-CBC 封装好的针对 AnsiString 的解密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 #0
  Iv       不短于 16 字节的初始化向量,太长则超出部分忽略
  Input    原始待解密字符串
  Output   Output 输出区,其长度必须大于或等于 (((Length(Input) - 1) div 16) + 1) * 16
 |</PRE>}

procedure SM4EncryptCfbStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
{* SM4-CFB 封装好的针对 AnsiString 的加密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 #0
  Iv       不短于 16 字节的初始化向量,太长则超出部分忽略
  Input    原始待加密字符串
  Output   Output 输出区,其长度必须大于或等于 (((Length(Input) - 1) div 16) + 1) * 16
 |</PRE>}

procedure SM4DecryptCfbStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
{* SM4-CFB 封装好的针对 AnsiString 的解密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 #0
  Iv       不短于 16 字节的初始化向量,太长则超出部分忽略
  Input    原始待解密字符串
  Output   Output 输出区,其长度必须大于或等于 (((Length(Input) - 1) div 16) + 1) * 16
 |</PRE>}

procedure SM4EncryptOfbStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
{* SM4-OFB 封装好的针对 AnsiString 的加密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 #0
  Iv       不短于 16 字节的初始化向量,太长则超出部分忽略
  Input    原始待加密字符串
  Output   Output 输出区,其长度必须大于或等于 (((Length(Input) - 1) div 16) + 1) * 16
 |</PRE>}

procedure SM4DecryptOfbStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
{* SM4-OFB 封装好的针对 AnsiString 的解密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 #0
  Iv       不短于 16 字节的初始化向量,太长则超出部分忽略
  Input    原始待解密字符串
  Output   Output 输出区,其长度必须大于或等于 (((Length(Input) - 1) div 16) + 1) * 16
 |</PRE>}

procedure SM4EncryptCtrStr(Key: AnsiString; Nonce: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
{* SM4-OFB 封装好的针对 AnsiString 的加密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 #0
  Nonce    不短于 8 字节的初始化向量,太长则超出部分忽略
  Input    原始待加密字符串
  Output   Output 输出区,其长度必须大于或等于 Length(Input)
 |</PRE>}

procedure SM4DecryptCtrStr(Key: AnsiString; Nonce: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
{* SM4-OFB 封装好的针对 AnsiString 的解密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 #0
  Nonce    不短于 8 字节的初始化向量,太长则超出部分忽略
  Input    原始待解密字符串
  Output   Output 输出区,其长度必须大于或等于 Length(Input)
 |</PRE>}

// ================= 明文字节数组与密文字节数组之间的加解密 ====================

function SM4EncryptEcbBytes(Key: TBytes; const Input: TBytes): TBytes;
{* SM4-ECB 封装好的针对 TBytes 的加密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Input    原始待加密内容,其长度如不是 16 倍数,计算时会被填充 0 至长度达到 16 的倍数
  返回值   加密内容
 |</PRE>}

function SM4DecryptEcbBytes(Key: TBytes; const Input: TBytes): TBytes;
{* SM4-ECB 封装好的针对 TBytes 的解密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Input    原始待加密内容,其长度如不是 16 倍数,计算时会被填充 0 至长度达到 16 的倍数
  返回值   解密内容
 |</PRE>}

function SM4EncryptCbcBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
{* SM4-CBC 封装好的针对 TBytes 的加密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    原始待加密内容
  返回值   加密内容
 |</PRE>}

function SM4DecryptCbcBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
{* SM4-CBC 封装好的针对 TBytes 的解密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    input 密文
  返回值   解密内容
 |</PRE>}

function SM4EncryptCfbBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
{* SM4-CFB 封装好的针对 TBytes 的加密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    原始待加密内容
  返回值   加密内容
 |</PRE>}

function SM4DecryptCfbBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
{* SM4-CFB 封装好的针对 TBytes 的解密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    input 密文
  返回值   解密内容
 |</PRE>}

function SM4EncryptOfbBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
{* SM4-OFB 封装好的针对 TBytes 的加密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    原始待加密内容
  返回值   加密内容
 |</PRE>}

function SM4DecryptOfbBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
{* SM4-OFB 封装好的针对 TBytes 的解密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    input 密文
  返回值   解密内容
 |</PRE>}

function SM4EncryptCtrBytes(Key, Nonce: TBytes; const Input: TBytes): TBytes;
{* SM4-CTR 封装好的针对 TBytes 的加密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Nonce    8 字节初始化向量,太长则超出部分忽略,不足则在 Nonce 后补 0
  Input    原始待加密内容
  返回值   加密内容
 |</PRE>}

function SM4DecryptCtrBytes(Key, Nonce: TBytes; const Input: TBytes): TBytes;
{* SM4-CTR 封装好的针对 TBytes 的解密方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Nonce    8 字节初始化向量,太长则超出部分忽略,不足则在 Nonce 后补 0
  Input    input 密文
  返回值   解密内容
 |</PRE>}

// ============== 明文字节数组与密文十六进制字符串之间的加解密 =================

function SM4EncryptEcbBytesToHex(Key: TBytes; const Input: TBytes): AnsiString;
{* SM4-ECB 封装好的针对 TBytes 的加密并转换成十六进制字符串的方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Input    原始待加密内容,其长度如不是 16 倍数,计算时会被填充 0 至长度达到 16 的倍数
  返回值   加密内容
 |</PRE>}

function SM4DecryptEcbBytesFromHex(Key: TBytes; const Input: AnsiString): TBytes;
{* SM4-ECB 封装好的针对十六进制字符串解密成 TBytes 的方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Input    十六进制密文,其解码后的长度如不是 16 倍数,计算时会被填充 0 至长度达到 16 的倍数
  返回值   解密内容
 |</PRE>}

function SM4EncryptCbcBytesToHex(Key, Iv: TBytes; const Input: TBytes): AnsiString;
{* SM4-CBC 封装好的针对 TBytes 的加密并转换成十六进制字符串的方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    原始待加密内容
  返回值   加密内容
 |</PRE>}

function SM4DecryptCbcBytesFromHex(Key, Iv: TBytes; const Input: AnsiString): TBytes;
{* SM4-CBC 封装好的针对十六进制字符串解密成 TBytes 的方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    十六进制密文
  返回值   解密内容
 |</PRE>}

function SM4EncryptCfbBytesToHex(Key, Iv: TBytes; const Input: TBytes): AnsiString;
{* SM4-CFB 封装好的针对 TBytes 的加密并转换成十六进制字符串的方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    原始待加密内容
  返回值   加密内容
 |</PRE>}

function SM4DecryptCfbBytesFromHex(Key, Iv: TBytes; const Input: AnsiString): TBytes;
{* SM4-CFB 封装好的针对十六进制字符串解密成 TBytes 的方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    十六进制密文
  返回值   解密内容
 |</PRE>}

function SM4EncryptOfbBytesToHex(Key, Iv: TBytes; const Input: TBytes): AnsiString;
{* SM4-OFB 封装好的针对 TBytes 的加密并转换成十六进制字符串的方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    原始待加密内容
  返回值   加密内容
 |</PRE>}

function SM4DecryptOfbBytesFromHex(Key, Iv: TBytes; const Input: AnsiString): TBytes;
{* SM4-OFB 封装好的针对十六进制字符串解密成 TBytes 的方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Iv       16 字节初始化向量,太长则超出部分忽略,不足则在 Iv 后补 0
  Input    十六进制密文
  返回值   解密内容
 |</PRE>}

function SM4EncryptCtrBytesToHex(Key, Nonce: TBytes; const Input: TBytes): AnsiString;
{* SM4-CTR 封装好的针对 TBytes 的加密并转换成十六进制字符串的方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Nonce    8 字节初始化向量,太长则超出部分忽略,不足则在 Nonce 后补 0
  Input    原始待加密内容
  返回值   加密内容
 |</PRE>}

function SM4DecryptCtrBytesFromHex(Key, Nonce: TBytes; const Input: AnsiString): TBytes;
{* SM4-CTR 封装好的针对十六进制字符串解密成 TBytes 的方法
 |<PRE>
  Key      16 字节密码,太长则截断,不足则补 0
  Nonce    8 字节初始化向量,太长则超出部分忽略,不足则在 Nonce 后补 0
  Input    十六进制密文
  返回值   解密内容
 |</PRE>}

// ======================= 明文流与密文流之间的加解密 ==========================

procedure SM4EncryptStreamECB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; Dest: TStream); overload;
{* SM4-ECB 流加密,Count 为 0 表示从头加密整个流,否则只加密 Stream 当前位置起 Count 的字节数}

procedure SM4DecryptStreamECB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; Dest: TStream); overload;
{* SM4-ECB 流解密,Count 为 0 表示从头解密整个流,否则只解密 Stream 当前位置起 Count 的字节数}

procedure SM4EncryptStreamCBC(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
{* SM4-CBC 流加密,Count 为 0 表示从头加密整个流,否则只加密 Stream 当前位置起 Count 的字节数}

procedure SM4DecryptStreamCBC(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
{* SM4-CBC 流解密,Count 为 0 表示从头解密整个流,否则只解密 Stream 当前位置起 Count 的字节数}

procedure SM4EncryptStreamCFB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
{* SM4-CFB 流加密,Count 为 0 表示从头加密整个流,否则只加密 Stream 当前位置起 Count 的字节数}

procedure SM4DecryptStreamCFB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
{* SM4-CFB 流解密,Count 为 0 表示从头解密整个流,否则只解密 Stream 当前位置起 Count 的字节数}

procedure SM4EncryptStreamOFB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
{* SM4-OFB 流加密,Count 为 0 表示从头加密整个流,否则只加密 Stream 当前位置起 Count 的字节数}

procedure SM4DecryptStreamOFB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
{* SM4-OFB 流解密,Count 为 0 表示从头解密整个流,否则只解密 Stream 当前位置起 Count 的字节数}

procedure SM4EncryptStreamCTR(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Nonce; Dest: TStream);
{* SM4-CTR 流加密,Count 为 0 表示从头加密整个流,否则只加密 Stream 当前位置起 Count 的字节数}

procedure SM4DecryptStreamCTR(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Nonce; Dest: TStream);
{* SM4-CTR 流解密,Count 为 0 表示从头解密整个流,否则只解密 Stream 当前位置起 Count 的字节数}

// 以下仨函数为底层加密函数,开放出来供外部挨块加密使用

procedure SM4SetKeyEnc(var Ctx: TCnSM4Context; Key: PAnsiChar);
{* 将 16 字节 Key 塞进 Context 并设置为加密模式}

procedure SM4SetKeyDec(var Ctx: TCnSM4Context; Key: PAnsiChar);
{* 将 16 字节 Key 塞进 Context 并设置为解密模式}

procedure SM4OneRound(SK: PCardinal; Input: PAnsiChar; Output: PAnsiChar);
{* 加解密一个块,内容从 Input 至 Output,长度 16 字节,两者可以是同一个区域
  SK是 TSM4Context 的 Sk,加还是解由其决定}

implementation

resourcestring
  SCnErrorSM4InvalidInBufSize = 'Invalid Buffer Size for Decryption';
  SCnErrorSM4ReadError = 'Stream Read Error';
  SCnErrorSM4WriteError = 'Stream Write Error';

const
  SM4_ENCRYPT = 1;
  SM4_DECRYPT = 0;

  SBoxTable: array[0..CN_SM4_KEYSIZE - 1] of array[0..CN_SM4_KEYSIZE - 1] of Byte = (
    ($D6, $90, $E9, $FE, $CC, $E1, $3D, $B7, $16, $B6, $14, $C2, $28, $FB, $2C, $05),
    ($2B, $67, $9A, $76, $2A, $BE, $04, $C3, $AA, $44, $13, $26, $49, $86, $06, $99),
    ($9C, $42, $50, $F4, $91, $EF, $98, $7A, $33, $54, $0B, $43, $ED, $CF, $AC, $62),
    ($E4, $B3, $1C, $A9, $C9, $08, $E8, $95, $80, $DF, $94, $FA, $75, $8F, $3F, $A6),
    ($47, $07, $A7, $FC, $F3, $73, $17, $BA, $83, $59, $3C, $19, $E6, $85, $4F, $A8),
    ($68, $6B, $81, $B2, $71, $64, $DA, $8B, $F8, $EB, $0F, $4B, $70, $56, $9D, $35),
    ($1E, $24, $0E, $5E, $63, $58, $D1, $A2, $25, $22, $7C, $3B, $01, $21, $78, $87),
    ($D4, $00, $46, $57, $9F, $D3, $27, $52, $4C, $36, $02, $E7, $A0, $C4, $C8, $9E),
    ($EA, $BF, $8A, $D2, $40, $C7, $38, $B5, $A3, $F7, $F2, $CE, $F9, $61, $15, $A1),
    ($E0, $AE, $5D, $A4, $9B, $34, $1A, $55, $AD, $93, $32, $30, $F5, $8C, $B1, $E3),
    ($1D, $F6, $E2, $2E, $82, $66, $CA, $60, $C0, $29, $23, $AB, $0D, $53, $4E, $6F),
    ($D5, $DB, $37, $45, $DE, $FD, $8E, $2F, $03, $FF, $6A, $72, $6D, $6C, $5B, $51),
    ($8D, $1B, $AF, $92, $BB, $DD, $BC, $7F, $11, $D9, $5C, $41, $1F, $10, $5A, $D8),
    ($0A, $C1, $31, $88, $A5, $CD, $7B, $BD, $2D, $74, $D0, $12, $B8, $E5, $B4, $B0),
    ($89, $69, $97, $4A, $0C, $96, $77, $7E, $65, $B9, $F1, $09, $C5, $6E, $C6, $84),
    ($18, $F0, $7D, $EC, $3A, $DC, $4D, $20, $79, $EE, $5F, $3E, $D7, $CB, $39, $48)
  );

  FK: array[0..3] of Cardinal = ($A3B1BAC6, $56AA3350, $677D9197, $B27022DC);

  CK: array[0..CN_SM4_KEYSIZE * 2 - 1] of Cardinal = (
    $00070E15, $1C232A31, $383F464D, $545B6269,
    $70777E85, $8C939AA1, $A8AFB6BD, $C4CBD2D9,
    $E0E7EEF5, $FC030A11, $181F262D, $343B4249,
    $50575E65, $6C737A81, $888F969D, $A4ABB2B9,
    $C0C7CED5, $DCE3EAF1, $F8FF060D, $141B2229,
    $30373E45, $4C535A61, $686F767D, $848B9299,
    $A0A7AEB5, $BCC3CAD1, $D8DFE6ED, $F4FB0209,
    $10171E25, $2C333A41, $484F565D, $646B7279 );

function Min(A, B: Integer): Integer; {$IFDEF SUPPORT_INLINE} inline; {$ENDIF}
begin
  if A < B then
    Result := A
  else
    Result := B;
end;

procedure GetULongBe(var N: Cardinal; B: PAnsiChar; I: Integer);
var
  D: Cardinal;
begin
  D := (Cardinal(B[I]) shl 24) or (Cardinal(B[I + 1]) shl 16) or
    (Cardinal(B[I + 2]) shl 8) or (Cardinal(B[I + 3]));
  N := D;
end;

procedure PutULongBe(N: Cardinal; B: PAnsiChar; I: Integer);
begin
  B[I] := AnsiChar(N shr 24);
  B[I + 1] := AnsiChar(N shr 16);
  B[I + 2] := AnsiChar(N shr 8);
  B[I + 3] := AnsiChar(N);
end;

function SM4Shl(X: Cardinal; N: Integer): Cardinal;
begin
  Result := (X and $FFFFFFFF) shl N;
end;

function ROTL(X: Cardinal; N: Integer): Cardinal;
begin
  Result := SM4Shl(X, N) or (X shr (32 - N));
end;

procedure Swap(var A: Cardinal; var B: Cardinal);
var
  T: Cardinal;
begin
  T := A;
  A := B;
  B := T;
end;

function SM4SBox(Inch: Byte): Byte;
var
  PTable: Pointer;
begin
  PTable := @(SboxTable[0][0]);
  Result := PByte(TCnNativeInt(PTable) + Inch)^;
end;

function SM4Lt(Ka: Cardinal): Cardinal;
var
  BB: Cardinal;
  A: array[0..3] of Byte;
  B: array[0..3] of Byte;
begin
  BB := 0;
  PutULongBe(Ka, @(A[0]), 0);
  B[0] := SM4SBox(A[0]);
  B[1] := SM4SBox(A[1]);
  B[2] := SM4SBox(A[2]);
  B[3] := SM4SBox(A[3]);
  GetULongBe(BB, @(B[0]), 0);

  Result := BB xor (ROTL(BB, 2)) xor (ROTL(BB, 10)) xor (ROTL(BB, 18))
    xor (ROTL(BB, 24));
end;

function SM4F(X0: Cardinal; X1: Cardinal; X2: Cardinal; X3: Cardinal; RK: Cardinal): Cardinal;
begin
  Result := X0 xor SM4Lt(X1 xor X2 xor X3 xor RK);
end;

function SM4CalciRK(Ka: Cardinal): Cardinal;
var
  BB: Cardinal;
  A: array[0..3] of Byte;
  B: array[0..3] of Byte;
begin
  PutULongBe(Ka, @(A[0]), 0);
  B[0] := SM4SBox(A[0]);
  B[1] := SM4SBox(A[1]);
  B[2] := SM4SBox(A[2]);
  B[3] := SM4SBox(A[3]);
  GetULongBe(BB, @(B[0]), 0);
  Result := BB xor ROTL(BB, 13) xor ROTL(BB, 23);
end;

// SK Points to 32 DWord Array; Key Points to 16 Byte Array
procedure SM4SetKey(SK: PCardinal; Key: PAnsiChar);
var
  MK: array[0..3] of Cardinal;
  K: array[0..35] of Cardinal;
  I: Integer;
begin
  GetULongBe(MK[0], Key, 0);
  GetULongBe(MK[1], Key, 4);
  GetULongBe(MK[2], Key, 8);
  GetULongBe(MK[3], Key, 12);

  K[0] := MK[0] xor FK[0];
  K[1] := MK[1] xor FK[1];
  K[2] := MK[2] xor FK[2];
  K[3] := MK[3] xor FK[3];

  for I := 0 to 31 do
  begin
    K[I + 4] := K[I] xor SM4CalciRK(K[I + 1] xor K[I + 2] xor K[I + 3] xor CK[I]);
    (PCardinal(TCnNativeInt(SK) + I * SizeOf(Cardinal)))^ := K[I + 4];
  end;
end;

// SK Points to 32 DWord Array; Input/Output Points to 16 Byte Array
// Input 和 Output 可以是同一处区域
procedure SM4OneRound(SK: PCardinal; Input: PAnsiChar; Output: PAnsiChar);
var
  I: Integer;
  UlBuf: array[0..35] of Cardinal;
begin
  FillChar(UlBuf[0], SizeOf(UlBuf), 0);

  GetULongBe(UlBuf[0], Input, 0);
  GetULongBe(UlBuf[1], Input, 4);
  GetULongBe(UlBuf[2], Input, 8);
  GetULongBe(UlBuf[3], Input, 12);

  for I := 0 to 31 do
  begin
    UlBuf[I + 4] := SM4F(UlBuf[I], UlBuf[I + 1], UlBuf[I + 2], UlBuf[I + 3],
      (PCardinal(TCnNativeInt(SK) + I * SizeOf(Cardinal)))^);
  end;

  PutULongBe(UlBuf[35], Output, 0);
  PutULongBe(UlBuf[34], Output, 4);
  PutULongBe(UlBuf[33], Output, 8);
  PutULongBe(UlBuf[32], Output, 12);
end;

procedure SM4SetKeyEnc(var Ctx: TCnSM4Context; Key: PAnsiChar);
begin
  Ctx.Mode := SM4_ENCRYPT;
  SM4SetKey(@(Ctx.Sk[0]), Key);
end;

procedure SM4SetKeyDec(var Ctx: TCnSM4Context; Key: PAnsiChar);
var
  I: Integer;
begin
  Ctx.Mode := SM4_DECRYPT;
  SM4SetKey(@(Ctx.Sk[0]), Key);

  for I := 0 to CN_SM4_KEYSIZE - 1 do
    Swap(Ctx.Sk[I], Ctx.Sk[31 - I]);
end;

procedure SM4CryptEcb(var Ctx: TCnSM4Context; Mode: Integer; Length: Integer;
  Input: PAnsiChar; Output: PAnsiChar);
var
  EndBuf: TCnSM4Buffer;
begin
  while Length > 0 do
  begin
    if Length >= CN_SM4_BLOCKSIZE then
    begin
      SM4OneRound(@(Ctx.Sk[0]), Input, Output);
    end
    else
    begin
      // 尾部不足 16,补 0
      FillChar(EndBuf[0], CN_SM4_BLOCKSIZE, 0);
      Move(Input^, EndBuf[0], Length);
      SM4OneRound(@(Ctx.Sk[0]), @(EndBuf[0]), Output);
    end;
    Inc(Input, CN_SM4_BLOCKSIZE);
    Inc(Output, CN_SM4_BLOCKSIZE);
    Dec(Length, CN_SM4_BLOCKSIZE);
  end;
end;

procedure SM4CryptEcbStr(Mode: Integer; Key: AnsiString;
  const Input: AnsiString; Output: PAnsiChar);
var
  Ctx: TCnSM4Context;
begin
  if Length(Key) < CN_SM4_KEYSIZE then
    while Length(Key) < CN_SM4_KEYSIZE do Key := Key + Chr(0) // 16 bytes at least padding 0.
  else if Length(Key) > CN_SM4_KEYSIZE then
    Key := Copy(Key, 1, CN_SM4_KEYSIZE);  // Only keep 16

  if Mode = SM4_ENCRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[1]));
    SM4CryptEcb(Ctx, SM4_ENCRYPT, Length(Input), @(Input[1]), @(Output[0]));
  end
  else if Mode = SM4_DECRYPT then
  begin
    SM4SetKeyDec(Ctx, @(Key[1]));
    SM4CryptEcb(Ctx, SM4_DECRYPT, Length(Input), @(Input[1]), @(Output[0]));
  end;
end;

procedure SM4CryptCbc(var Ctx: TCnSM4Context; Mode: Integer; Length: Integer;
  Iv: PAnsiChar; Input: PAnsiChar; Output: PAnsiChar);
var
  I: Integer;
  EndBuf: TCnSM4Buffer;
  LocalIv: TCnSM4Iv;
begin
  Move(Iv^, LocalIv[0], CN_SM4_BLOCKSIZE);
  if Mode = SM4_ENCRYPT then
  begin
    while Length > 0 do
    begin
      if Length >= CN_SM4_BLOCKSIZE then
      begin
        for I := 0 to CN_SM4_BLOCKSIZE - 1 do
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Input) + I))^
            xor LocalIv[I];

        SM4OneRound(@(Ctx.Sk[0]), Output, Output);
        Move(Output[0], LocalIv[0], CN_SM4_BLOCKSIZE);
      end
      else
      begin
        // 尾部不足 16,补 0
        FillChar(EndBuf[0], SizeOf(EndBuf), 0);
        Move(Input^, EndBuf[0], Length);

        for I := 0 to CN_SM4_BLOCKSIZE - 1 do
          (PByte(TCnNativeInt(Output) + I))^ := EndBuf[I]
            xor LocalIv[I];

        SM4OneRound(@(Ctx.Sk[0]), Output, Output);
        Move(Output[0], LocalIv[0], CN_SM4_BLOCKSIZE);
      end;

      Inc(Input, CN_SM4_BLOCKSIZE);
      Inc(Output, CN_SM4_BLOCKSIZE);
      Dec(Length, CN_SM4_BLOCKSIZE);
    end;
  end
  else if Mode = SM4_DECRYPT then
  begin
    while Length > 0 do
    begin
      if Length >= CN_SM4_BLOCKSIZE then
      begin
        SM4OneRound(@(Ctx.Sk[0]), Input, Output);

        for I := 0 to CN_SM4_BLOCKSIZE - 1 do
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Output) + I))^
            xor LocalIv[I];

        Move(Input^, LocalIv[0], CN_SM4_BLOCKSIZE);
      end
      else
      begin
        // 尾部不足 16,补 0
        FillChar(EndBuf[0], SizeOf(EndBuf), 0);
        Move(Input^, EndBuf[0], Length);
        SM4OneRound(@(Ctx.Sk[0]), @(EndBuf[0]), Output);

        for I := 0 to CN_SM4_BLOCKSIZE - 1 do
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Output) + I))^
            xor LocalIv[I];

        Move(EndBuf[0], LocalIv[0], CN_SM4_BLOCKSIZE);
      end;

      Inc(Input, CN_SM4_BLOCKSIZE);
      Inc(Output, CN_SM4_BLOCKSIZE);
      Dec(Length, CN_SM4_BLOCKSIZE);
    end;
  end;
end;

procedure SM4CryptCfb(var Ctx: TCnSM4Context; Mode: Integer; Length: Integer;
  Iv: PAnsiChar; Input: PAnsiChar; Output: PAnsiChar);
var
  I: Integer;
  LocalIv: TCnSM4Iv;
begin
  Move(Iv^, LocalIv[0], CN_SM4_BLOCKSIZE);
  if Mode = SM4_ENCRYPT then
  begin
    while Length > 0 do
    begin
      if Length >= CN_SM4_BLOCKSIZE then
      begin
        SM4OneRound(@(Ctx.Sk[0]), @LocalIv[0], Output);  // 先加密 Iv

        for I := 0 to CN_SM4_BLOCKSIZE - 1 do
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Input) + I))^
            xor (PByte(TCnNativeInt(Output) + I))^;  // 加密结果与明文异或作为输出密文

        Move(Output[0], LocalIv[0], CN_SM4_BLOCKSIZE);  // 密文取代 Iv 以备下一轮
      end
      else
      begin
        SM4OneRound(@(Ctx.Sk[0]), @LocalIv[0], Output);

        for I := 0 to Length - 1 do // 只需异或剩余长度,无需处理完整的 16 字节
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Input) + I))^
            xor (PByte(TCnNativeInt(Output) + I))^;
      end;

      Inc(Input, CN_SM4_BLOCKSIZE);
      Inc(Output, CN_SM4_BLOCKSIZE);
      Dec(Length, CN_SM4_BLOCKSIZE);
    end;
  end
  else if Mode = SM4_DECRYPT then
  begin
    while Length > 0 do
    begin
      if Length >= CN_SM4_BLOCKSIZE then
      begin
        SM4OneRound(@(Ctx.Sk[0]), @LocalIv[0], Output);   // 先加密 Iv

        for I := 0 to CN_SM4_BLOCKSIZE - 1 do
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Output) + I))^
            xor (PByte(TCnNativeInt(Input) + I))^;    // 加密结果与密文异或得到明文

        Move(Input[0], LocalIv[0], CN_SM4_BLOCKSIZE);    // 密文取代 Iv 再拿去下一轮加密
      end
      else
      begin
        SM4OneRound(@(Ctx.Sk[0]), @LocalIv[0], Output);

        for I := 0 to Length - 1 do
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Output) + I))^
            xor (PByte(TCnNativeInt(Input) + I))^;
      end;

      Inc(Input, CN_SM4_BLOCKSIZE);
      Inc(Output, CN_SM4_BLOCKSIZE);
      Dec(Length, CN_SM4_BLOCKSIZE);
    end;
  end;
end;

procedure SM4CryptOfb(var Ctx: TCnSM4Context; Mode: Integer; Length: Integer;
  Iv: PAnsiChar; Input: PAnsiChar; Output: PAnsiChar);
var
  I: Integer;
  LocalIv: TCnSM4Iv;
begin
  Move(Iv^, LocalIv[0], CN_SM4_BLOCKSIZE);
  if Mode = SM4_ENCRYPT then
  begin
    while Length > 0 do
    begin
      if Length >= CN_SM4_BLOCKSIZE then
      begin
        SM4OneRound(@(Ctx.Sk[0]), @LocalIv[0], Output);  // 先加密 Iv

        Move(Output[0], LocalIv[0], CN_SM4_BLOCKSIZE);  // 加密结果先留存给下一步

        for I := 0 to CN_SM4_BLOCKSIZE - 1 do      // 加密结果与明文异或出密文
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Input) + I))^
            xor (PByte(TCnNativeInt(Output) + I))^;
      end
      else
      begin
        SM4OneRound(@(Ctx.Sk[0]), @LocalIv[0], Output);  // 先加密 Iv

        for I := 0 to Length - 1 do             // 无需完整 16 字节
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Input) + I))^
            xor (PByte(TCnNativeInt(Output) + I))^;
      end;

      Inc(Input, CN_SM4_BLOCKSIZE);
      Inc(Output, CN_SM4_BLOCKSIZE);
      Dec(Length, CN_SM4_BLOCKSIZE);
    end;
  end
  else if Mode = SM4_DECRYPT then
  begin
    while Length > 0 do
    begin
      if Length >= CN_SM4_BLOCKSIZE then
      begin
        SM4OneRound(@(Ctx.Sk[0]), @LocalIv[0], Output);   // 先加密 Iv

        Move(Output[0], LocalIv[0], CN_SM4_BLOCKSIZE);   // 加密结果先留存给下一步

        for I := 0 to CN_SM4_BLOCKSIZE - 1 do       // 加密内容与密文异或得到明文
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Output) + I))^
            xor (PByte(TCnNativeInt(Input) + I))^;
      end
      else
      begin
        SM4OneRound(@(Ctx.Sk[0]), @LocalIv[0], Output);   // 先加密 Iv

        for I := 0 to Length - 1 do
          (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Output) + I))^
            xor (PByte(TCnNativeInt(Input) + I))^;
      end;

      Inc(Input, CN_SM4_BLOCKSIZE);
      Inc(Output, CN_SM4_BLOCKSIZE);
      Dec(Length, CN_SM4_BLOCKSIZE);
    end;
  end;
end;

// CTR 模式加密数据块。Output 长度可以和 Input 一样,不必向上取整
procedure SM4CryptCtr(var Ctx: TCnSM4Context; Mode: Integer; Length: Integer;
  Nonce: PAnsiChar; Input: PAnsiChar; Output: PAnsiChar);
var
  I: Integer;
  LocalIv: TCnSM4Iv;
  Cnt, T: Int64;
begin
  Cnt := 1;

  // 不区分加解密
  while Length > 0 do
  begin
    if Length >= CN_SM4_BLOCKSIZE then
    begin
      Move(Nonce^, LocalIv[0], SizeOf(TCnSM4Nonce));
      T := Int64HostToNetwork(Cnt);
      Move(T, LocalIv[SizeOf(TCnSM4Nonce)], SizeOf(Int64));

      SM4OneRound(@(Ctx.Sk[0]), @LocalIv[0], @LocalIv[0]);  // 先加密 Iv

      for I := 0 to CN_SM4_BLOCKSIZE - 1 do      // 加密结果与明文异或出密文
        (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Input) + I))^
          xor LocalIv[I];
    end
    else
    begin
      Move(Nonce^, LocalIv[0], SizeOf(TCnSM4Nonce));
      T := Int64HostToNetwork(Cnt);
      Move(T, LocalIv[SizeOf(TCnSM4Nonce)], SizeOf(Int64));

      SM4OneRound(@(Ctx.Sk[0]), @LocalIv[0], @LocalIv[0]);  // 先加密 Iv

      for I := 0 to Length - 1 do             // 无需完整 16 字节
        (PByte(TCnNativeInt(Output) + I))^ := (PByte(TCnNativeInt(Input) + I))^
          xor LocalIv[I];
    end;

    Inc(Input, CN_SM4_BLOCKSIZE);
    Inc(Output, CN_SM4_BLOCKSIZE);
    Dec(Length, CN_SM4_BLOCKSIZE);
    Inc(Cnt);
  end;
end;

procedure SM4CryptCbcStr(Mode: Integer; Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
var
  Ctx: TCnSM4Context;
begin
  if Length(Key) < CN_SM4_KEYSIZE then
    while Length(Key) < CN_SM4_KEYSIZE do Key := Key + Chr(0) // 16 bytes at least padding 0.
  else if Length(Key) > CN_SM4_KEYSIZE then
    Key := Copy(Key, 1, CN_SM4_KEYSIZE);  // Only keep 16

  if Mode = SM4_ENCRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[1]));
    SM4CryptCbc(Ctx, SM4_ENCRYPT, Length(Input), @(Iv[0]), @(Input[1]), @(Output[0]));
  end
  else if Mode = SM4_DECRYPT then
  begin
    SM4SetKeyDec(Ctx, @(Key[1]));
    SM4CryptCbc(Ctx, SM4_DECRYPT, Length(Input), @(Iv[0]), @(Input[1]), @(Output[0]));
  end;
end;

procedure SM4CryptCfbStr(Mode: Integer; Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
var
  Ctx: TCnSM4Context;
begin
  if Length(Key) < CN_SM4_KEYSIZE then
    while Length(Key) < CN_SM4_KEYSIZE do Key := Key + Chr(0) // 16 bytes at least padding 0.
  else if Length(Key) > CN_SM4_KEYSIZE then
    Key := Copy(Key, 1, CN_SM4_KEYSIZE);  // Only keep 16

  if Mode = SM4_ENCRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[1]));
    SM4CryptCfb(Ctx, SM4_ENCRYPT, Length(Input), @(Iv[0]), @(Input[1]), @(Output[0]));
  end
  else if Mode = SM4_DECRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[1])); // 注意 CFB 的解密也用的是加密!
    SM4CryptCfb(Ctx, SM4_DECRYPT, Length(Input), @(Iv[0]), @(Input[1]), @(Output[0]));
  end;
end;

procedure SM4CryptOfbStr(Mode: Integer; Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
var
  Ctx: TCnSM4Context;
begin
  if Length(Key) < CN_SM4_KEYSIZE then
    while Length(Key) < CN_SM4_KEYSIZE do Key := Key + Chr(0) // 16 bytes at least padding 0.
  else if Length(Key) > CN_SM4_KEYSIZE then
    Key := Copy(Key, 1, CN_SM4_KEYSIZE);  // Only keep 16

  if Mode = SM4_ENCRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[1]));
    SM4CryptOfb(Ctx, SM4_ENCRYPT, Length(Input), @(Iv[0]), @(Input[1]), @(Output[0]));
  end
  else if Mode = SM4_DECRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[1])); // 注意 OFB 的解密也用的是加密!
    SM4CryptOfb(Ctx, SM4_DECRYPT, Length(Input), @(Iv[0]), @(Input[1]), @(Output[0]));
  end;
end;

procedure SM4CryptCtrStr(Mode: Integer; Key: AnsiString; Nonce: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
var
  Ctx: TCnSM4Context;
begin
  if Length(Key) < CN_SM4_KEYSIZE then
    while Length(Key) < CN_SM4_KEYSIZE do Key := Key + Chr(0) // 16 bytes at least padding 0.
  else if Length(Key) > CN_SM4_KEYSIZE then
    Key := Copy(Key, 1, CN_SM4_KEYSIZE);  // Only keep 16

  if Mode = SM4_ENCRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[1]));
    SM4CryptCtr(Ctx, SM4_ENCRYPT, Length(Input), @(Nonce[0]), @(Input[1]), @(Output[0]));
  end
  else if Mode = SM4_DECRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[1])); // 注意 CTR 的解密也用的是加密!
    SM4CryptCtr(Ctx, SM4_DECRYPT, Length(Input), @(Nonce[0]), @(Input[1]), @(Output[0]));
  end;
end;

procedure SM4EncryptEcbStr(Key: AnsiString; const Input: AnsiString; Output: PAnsiChar);
begin
  SM4CryptEcbStr(SM4_ENCRYPT, Key, Input, Output);
end;

procedure SM4DecryptEcbStr(Key: AnsiString; const Input: AnsiString; Output: PAnsiChar);
begin
  SM4CryptEcbStr(SM4_DECRYPT, Key, Input, Output);
end;

procedure SM4EncryptCbcStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
begin
  SM4CryptCbcStr(SM4_ENCRYPT, Key, Iv, Input, Output);
end;

procedure SM4DecryptCbcStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
begin
  SM4CryptCbcStr(SM4_DECRYPT, Key, Iv, Input, Output);
end;

procedure SM4EncryptCfbStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
begin
  SM4CryptCfbStr(SM4_ENCRYPT, Key, Iv, Input, Output);
end;

procedure SM4DecryptCfbStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
begin
  SM4CryptCfbStr(SM4_DECRYPT, Key, Iv, Input, Output);
end;

procedure SM4EncryptOfbStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
begin
  SM4CryptOfbStr(SM4_ENCRYPT, Key, Iv, Input, Output);
end;

procedure SM4DecryptOfbStr(Key: AnsiString; Iv: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
begin
  SM4CryptOfbStr(SM4_DECRYPT, Key, Iv, Input, Output);
end;

procedure SM4EncryptCtrStr(Key: AnsiString; Nonce: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
begin
  SM4CryptCtrStr(SM4_ENCRYPT, Key, Nonce, Input, Output);
end;

procedure SM4DecryptCtrStr(Key: AnsiString; Nonce: PAnsiChar;
  const Input: AnsiString; Output: PAnsiChar);
begin
  SM4CryptCtrStr(SM4_DECRYPT, Key, Nonce, Input, Output);
end;

function SM4CryptEcbBytes(Mode: Integer; Key: TBytes;
  const Input: TBytes): TBytes;
var
  Ctx: TCnSM4Context;
  I, Len: Integer;
begin
  Len := Length(Input);
  if Len <= 0 then
  begin
    Result := nil;
    Exit;
  end;
  SetLength(Result, (((Len - 1) div 16) + 1) * 16);

  Len := Length(Key);
  if Len < CN_SM4_KEYSIZE then // Key 长度小于 16 字节补 0
  begin
    SetLength(Key, CN_SM4_KEYSIZE);
    for I := Len to CN_SM4_KEYSIZE - 1 do
      Key[I] := 0;
  end;
  // 长度大于 16 字节时 SM4SetKeyEnc 会自动忽略后面的部分

  if Mode = SM4_ENCRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[0]));
    SM4CryptEcb(Ctx, SM4_ENCRYPT, Length(Input), @(Input[0]), @(Result[0]));
  end
  else if Mode = SM4_DECRYPT then
  begin
    SM4SetKeyDec(Ctx, @(Key[0]));
    SM4CryptEcb(Ctx, SM4_DECRYPT, Length(Input), @(Input[0]), @(Result[0]));
  end;
end;

function SM4CryptCbcBytes(Mode: Integer; Key, Iv: TBytes;
  const Input: TBytes): TBytes;
var
  Ctx: TCnSM4Context;
  I, Len: Integer;
begin
  Len := Length(Input);
  if Len <= 0 then
  begin
    Result := nil;
    Exit;
  end;
  SetLength(Result, (((Len - 1) div 16) + 1) * 16);

  Len := Length(Key);
  if Len < CN_SM4_KEYSIZE then // Key 长度小于 16 字节补 0
  begin
    SetLength(Key, CN_SM4_KEYSIZE);
    for I := Len to CN_SM4_KEYSIZE - 1 do
      Key[I] := 0;
  end;
  // 长度大于 16 字节时 SM4SetKeyEnc 会自动忽略后面的部分

  Len := Length(Iv);
  if Len < CN_SM4_BLOCKSIZE then // Iv 长度小于 16 字节补 0
  begin
    SetLength(Iv, CN_SM4_BLOCKSIZE);
    for I := Len to CN_SM4_BLOCKSIZE - 1 do
      Iv[I] := 0;
  end;

  if Mode = SM4_ENCRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[0]));
    SM4CryptCbc(Ctx, SM4_ENCRYPT, Length(Input), @(Iv[0]), @(Input[0]), @(Result[0]));
  end
  else if Mode = SM4_DECRYPT then
  begin
    SM4SetKeyDec(Ctx, @(Key[0]));
    SM4CryptCbc(Ctx, SM4_DECRYPT, Length(Input), @(Iv[0]), @(Input[0]), @(Result[0]));
  end;
end;

function SM4CryptCfbBytes(Mode: Integer; Key, Iv: TBytes;
  const Input: TBytes): TBytes;
var
  Ctx: TCnSM4Context;
  I, Len: Integer;
begin
  Len := Length(Input);
  if Len <= 0 then
  begin
    Result := nil;
    Exit;
  end;
  SetLength(Result, (((Len - 1) div 16) + 1) * 16);

  Len := Length(Key);
  if Len < CN_SM4_KEYSIZE then // Key 长度小于 16 字节补 0
  begin
    SetLength(Key, CN_SM4_KEYSIZE);
    for I := Len to CN_SM4_KEYSIZE - 1 do
      Key[I] := 0;
  end;
  // 长度大于 16 字节时 SM4SetKeyEnc 会自动忽略后面的部分

  Len := Length(Iv);
  if Len < CN_SM4_BLOCKSIZE then // Iv 长度小于 16 字节补 0
  begin
    SetLength(Iv, CN_SM4_BLOCKSIZE);
    for I := Len to CN_SM4_BLOCKSIZE - 1 do
      Iv[I] := 0;
  end;

  if Mode = SM4_ENCRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[0]));
    SM4CryptCfb(Ctx, SM4_ENCRYPT, Length(Input), @(Iv[0]), @(Input[0]), @(Result[0]));
  end
  else if Mode = SM4_DECRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[0])); // 注意 CFB 的解密也用的是加密!
    SM4CryptCfb(Ctx, SM4_DECRYPT, Length(Input), @(Iv[0]), @(Input[0]), @(Result[0]));
  end;
end;

function SM4CryptOfbBytes(Mode: Integer; Key, Iv: TBytes;
  const Input: TBytes): TBytes;
var
  Ctx: TCnSM4Context;
  I, Len: Integer;
begin
  Len := Length(Input);
  if Len <= 0 then
  begin
    Result := nil;
    Exit;
  end;
  SetLength(Result, (((Len - 1) div 16) + 1) * 16);

  Len := Length(Key);
  if Len < CN_SM4_KEYSIZE then // Key 长度小于 16 字节补 0
  begin
    SetLength(Key, CN_SM4_KEYSIZE);
    for I := Len to CN_SM4_KEYSIZE - 1 do
      Key[I] := 0;
  end;
  // 长度大于 16 字节时 SM4SetKeyEnc 会自动忽略后面的部分

  Len := Length(Iv);
  if Len < CN_SM4_BLOCKSIZE then // Iv 长度小于 16 字节补 0
  begin
    SetLength(Iv, CN_SM4_BLOCKSIZE);
    for I := Len to CN_SM4_BLOCKSIZE - 1 do
      Iv[I] := 0;
  end;

  if Mode = SM4_ENCRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[0]));
    SM4CryptOfb(Ctx, SM4_ENCRYPT, Length(Input), @(Iv[0]), @(Input[0]), @(Result[0]));
  end
  else if Mode = SM4_DECRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[0])); // 注意 OFB 的解密也用的是加密!
    SM4CryptOfb(Ctx, SM4_DECRYPT, Length(Input), @(Iv[0]), @(Input[0]), @(Result[0]));
  end;
end;

function SM4CryptCtrBytes(Mode: Integer; Key, Nonce: TBytes;
  const Input: TBytes): TBytes;
var
  Ctx: TCnSM4Context;
  I, Len: Integer;
begin
  Len := Length(Input);
  if Len <= 0 then
  begin
    Result := nil;
    Exit;
  end;
  SetLength(Result, Len);

  Len := Length(Key);
  if Len < CN_SM4_KEYSIZE then // Key 长度小于 16 字节补 0
  begin
    SetLength(Key, CN_SM4_KEYSIZE);
    for I := Len to CN_SM4_KEYSIZE - 1 do
      Key[I] := 0;
  end;
  // 长度大于 16 字节时 SM4SetKeyEnc 会自动忽略后面的部分

  Len := Length(Nonce);
  if Len < CN_SM4_NONCESIZE then // Nonce 长度小于 16 字节补 0
  begin
    SetLength(Nonce, CN_SM4_NONCESIZE);
    for I := Len to CN_SM4_NONCESIZE - 1 do
      Nonce[I] := 0;
  end;

  if Mode = SM4_ENCRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[0]));
    SM4CryptCtr(Ctx, SM4_ENCRYPT, Length(Input), @(Nonce[0]), @(Input[0]), @(Result[0]));
  end
  else if Mode = SM4_DECRYPT then
  begin
    SM4SetKeyEnc(Ctx, @(Key[0])); // 注意 CTR 的解密也用的是加密!
    SM4CryptCtr(Ctx, SM4_DECRYPT, Length(Input), @(Nonce[0]), @(Input[0]), @(Result[0]));
  end;
end;

function SM4EncryptEcbBytes(Key: TBytes; const Input: TBytes): TBytes;
begin
  Result := SM4CryptEcbBytes(SM4_ENCRYPT, Key, Input);
end;

function SM4DecryptEcbBytes(Key: TBytes; const Input: TBytes): TBytes;
begin
  Result := SM4CryptEcbBytes(SM4_DECRYPT, Key, Input);
end;

function SM4EncryptCbcBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
begin
  Result := SM4CryptCbcBytes(SM4_ENCRYPT, Key, Iv, Input);
end;

function SM4DecryptCbcBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
begin
  Result := SM4CryptCbcBytes(SM4_DECRYPT, Key, Iv, Input);
end;

function SM4EncryptCfbBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
begin
  Result := SM4CryptCfbBytes(SM4_ENCRYPT, Key, Iv, Input);
end;

function SM4DecryptCfbBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
begin
  Result := SM4CryptCfbBytes(SM4_DECRYPT, Key, Iv, Input);
end;

function SM4EncryptOfbBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
begin
  Result := SM4CryptOfbBytes(SM4_ENCRYPT, Key, Iv, Input);
end;

function SM4DecryptOfbBytes(Key, Iv: TBytes; const Input: TBytes): TBytes;
begin
  Result := SM4CryptOfbBytes(SM4_DECRYPT, Key, Iv, Input);
end;

function SM4EncryptCtrBytes(Key, Nonce: TBytes; const Input: TBytes): TBytes;
begin
  Result := SM4CryptCtrBytes(SM4_ENCRYPT, Key, Nonce, Input);
end;

function SM4DecryptCtrBytes(Key, Nonce: TBytes; const Input: TBytes): TBytes;
begin
  Result := SM4CryptCtrBytes(SM4_DECRYPT, Key, Nonce, Input);
end;

function SM4EncryptEcbBytesToHex(Key: TBytes; const Input: TBytes): AnsiString;
begin
  Result := AnsiString(BytesToHex(SM4EncryptEcbBytes(Key, Input)));
end;

function SM4DecryptEcbBytesFromHex(Key: TBytes; const Input: AnsiString): TBytes;
begin
  Result := SM4DecryptEcbBytes(Key, HexToBytes(string(Input)));
end;

function SM4EncryptCbcBytesToHex(Key, Iv: TBytes; const Input: TBytes): AnsiString;
begin
  Result := AnsiString(BytesToHex(SM4EncryptCbcBytes(Key, Iv, Input)));
end;

function SM4DecryptCbcBytesFromHex(Key, Iv: TBytes; const Input: AnsiString): TBytes;
begin
  Result := SM4DecryptCbcBytes(Key, Iv, HexToBytes(string(Input)));
end;

function SM4EncryptCfbBytesToHex(Key, Iv: TBytes; const Input: TBytes): AnsiString;
begin
  Result := AnsiString(BytesToHex(SM4EncryptCfbBytes(Key, Iv, Input)));
end;

function SM4DecryptCfbBytesFromHex(Key, Iv: TBytes; const Input: AnsiString): TBytes;
begin
  Result := SM4DecryptCfbBytes(Key, Iv, HexToBytes(string(Input)));
end;

function SM4EncryptOfbBytesToHex(Key, Iv: TBytes; const Input: TBytes): AnsiString;
begin
  Result := AnsiString(BytesToHex(SM4EncryptOfbBytes(Key, Iv, Input)));
end;

function SM4DecryptOfbBytesFromHex(Key, Iv: TBytes; const Input: AnsiString): TBytes;
begin
  Result := SM4DecryptOfbBytes(Key, Iv, HexToBytes(string(Input)));
end;

function SM4EncryptCtrBytesToHex(Key, Nonce: TBytes; const Input: TBytes): AnsiString;
begin
  Result := AnsiString(BytesToHex(SM4EncryptCtrBytes(Key, Nonce, Input)));
end;

function SM4DecryptCtrBytesFromHex(Key, Nonce: TBytes; const Input: AnsiString): TBytes;
begin
  Result := SM4DecryptCtrBytes(Key, Nonce, HexToBytes(string(Input)));
end;

procedure SM4EncryptStreamECB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; Dest: TStream);
var
  TempIn, TempOut: TCnSM4Buffer;
  Done: Cardinal;
  Ctx: TCnSM4Context;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end
  else
    Count := Min(Count, Source.Size - Source.Position);

  if Count = 0 then
    Exit;

  SM4SetKeyEnc(Ctx, @(Key[0]));
  while Count >= SizeOf(TCnSM4Buffer) do
  begin
    Done := Source.Read(TempIn, SizeOf(TempIn));
    if Done < SizeOf(TempIn) then
      raise EStreamError.Create(SCnErrorSM4ReadError);

    SM4OneRound(@(Ctx.Sk[0]), @(TempIn[0]), @(TempOut[0]));

    Done := Dest.Write(TempOut, SizeOf(TempOut));
    if Done < SizeOf(TempOut) then
      raise EStreamError.Create(SCnErrorSM4WriteError);

    Dec(Count, SizeOf(TCnSM4Buffer));
  end;

  if Count > 0 then // 尾部补 0
  begin
    Done := Source.Read(TempIn, Count);
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4ReadError);
    FillChar(TempIn[Count], SizeOf(TempIn) - Count, 0);

    SM4OneRound(@(Ctx.Sk[0]), @(TempIn[0]), @(TempOut[0]));

    Done := Dest.Write(TempOut, SizeOf(TempOut));
    if Done < SizeOf(TempOut) then
      raise EStreamError.Create(SCnErrorSM4WriteError);
  end;
end;

procedure SM4DecryptStreamECB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; Dest: TStream);
var
  TempIn, TempOut: TCnSM4Buffer;
  Done: Cardinal;
  Ctx: TCnSM4Context;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end
  else
    Count := Min(Count, Source.Size - Source.Position);

  if Count = 0 then
    Exit;
  if (Count mod SizeOf(TCnSM4Buffer)) > 0 then
    raise Exception.Create(SCnErrorSM4InvalidInBufSize);

  SM4SetKeyDec(Ctx, @(Key[0]));
  while Count >= SizeOf(TCnSM4Buffer) do
  begin
    Done := Source.Read(TempIn, SizeOf(TempIn));
    if Done < SizeOf(TempIn) then
      raise EStreamError.Create(SCnErrorSM4ReadError);

    SM4OneRound(@(Ctx.Sk[0]), @(TempIn[0]), @(TempOut[0]));

    Done := Dest.Write(TempOut, SizeOf(TempOut));
    if Done < SizeOf(TempOut) then
      raise EStreamError.Create(SCnErrorSM4WriteError);

    Dec(Count, SizeOf(TCnSM4Buffer));
  end;
end;

procedure SM4EncryptStreamCBC(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
var
  TempIn, TempOut: TCnSM4Buffer;
  Vector: TCnSM4Iv;
  Done: Cardinal;
  Ctx: TCnSM4Context;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end
  else
    Count := Min(Count, Source.Size - Source.Position);

  if Count = 0 then
    Exit;

  Vector := InitVector;
  SM4SetKeyEnc(Ctx, @(Key[0]));

  while Count >= SizeOf(TCnSM4Buffer) do
  begin
    Done := Source.Read(TempIn, SizeOf(TempIn));
    if Done < SizeOf(TempIn) then
      raise EStreamError.Create(SCnErrorSM4ReadError);

    PCardinal(@TempIn[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@Vector[0])^;
    PCardinal(@TempIn[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@Vector[4])^;
    PCardinal(@TempIn[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@Vector[8])^;
    PCardinal(@TempIn[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@Vector[12])^;

    SM4OneRound(@(Ctx.Sk[0]), @(TempIn[0]), @(TempOut[0]));

    Done := Dest.Write(TempOut, SizeOf(TempOut));
    if Done < SizeOf(TempOut) then
      raise EStreamError.Create(SCnErrorSM4WriteError);

    Move(TempOut[0], Vector[0], SizeOf(TCnSM4Iv));
    Dec(Count, SizeOf(TCnSM4Buffer));
  end;

  if Count > 0 then
  begin
    Done := Source.Read(TempIn, Count);
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4ReadError);
    FillChar(TempIn[Count], SizeOf(TempIn) - Count, 0);

    PCardinal(@TempIn[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@Vector[0])^;
    PCardinal(@TempIn[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@Vector[4])^;
    PCardinal(@TempIn[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@Vector[8])^;
    PCardinal(@TempIn[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@Vector[12])^;

    SM4OneRound(@(Ctx.Sk[0]), @(TempIn[0]), @(TempOut[0]));

    Done := Dest.Write(TempOut, SizeOf(TempOut));
    if Done < SizeOf(TempOut) then
      raise EStreamError.Create(SCnErrorSM4WriteError);
  end;
end;

procedure SM4DecryptStreamCBC(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
var
  TempIn, TempOut: TCnSM4Buffer;
  Vector1, Vector2: TCnSM4Iv;
  Done: Cardinal;
  Ctx: TCnSM4Context;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end
  else
    Count := Min(Count, Source.Size - Source.Position);

  if Count = 0 then
    Exit;
  if (Count mod SizeOf(TCnSM4Buffer)) > 0 then
    raise Exception.Create(SCnErrorSM4InvalidInBufSize);

  Vector1 := InitVector;
  SM4SetKeyDec(Ctx, @(Key[0]));

  while Count >= SizeOf(TCnSM4Buffer) do
  begin
    Done := Source.Read(TempIn, SizeOf(TempIn));
    if Done < SizeOf(TempIn) then
      raise EStreamError(SCnErrorSM4ReadError);

    Move(TempIn[0], Vector2[0], SizeOf(TCnSM4Iv));
    SM4OneRound(@(Ctx.Sk[0]), @(TempIn[0]), @(TempOut[0]));

    PCardinal(@TempOut[0])^ := PCardinal(@TempOut[0])^ xor PCardinal(@Vector1[0])^;
    PCardinal(@TempOut[4])^ := PCardinal(@TempOut[4])^ xor PCardinal(@Vector1[4])^;
    PCardinal(@TempOut[8])^ := PCardinal(@TempOut[8])^ xor PCardinal(@Vector1[8])^;
    PCardinal(@TempOut[12])^ := PCardinal(@TempOut[12])^ xor PCardinal(@Vector1[12])^;

    Done := Dest.Write(TempOut, SizeOf(TempOut));
    if Done < SizeOf(TempOut) then
      raise EStreamError(SCnErrorSM4WriteError);

    Vector1 := Vector2;
    Dec(Count, SizeOf(TCnSM4Buffer));
  end;
end;

procedure SM4EncryptStreamCFB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
var
  TempIn, TempOut: TCnSM4Buffer;
  Vector: TCnSM4Iv;
  Done: Cardinal;
  Ctx: TCnSM4Context;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end
  else
    Count := Min(Count, Source.Size - Source.Position);

  if Count = 0 then
    Exit;

  Vector := InitVector;
  SM4SetKeyEnc(Ctx, @(Key[0]));

  while Count >= SizeOf(TCnSM4Buffer) do
  begin
    Done := Source.Read(TempIn, SizeOf(TempIn));
    if Done < SizeOf(TempIn) then
      raise EStreamError.Create(SCnErrorSM4ReadError);

    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));     // Key 先加密 Iv

    PCardinal(@TempOut[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@TempOut[0])^;  // 加密结果与明文异或
    PCardinal(@TempOut[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@TempOut[4])^;
    PCardinal(@TempOut[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@TempOut[8])^;
    PCardinal(@TempOut[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@TempOut[12])^;

    Done := Dest.Write(TempOut, SizeOf(TempOut));   // 异或的结果写进密文结果
    if Done < SizeOf(TempOut) then
      raise EStreamError.Create(SCnErrorSM4WriteError);

    Move(TempOut[0], Vector[0], SizeOf(TCnSM4Iv));    // 密文结果取代 Iv 供下一轮加密
    Dec(Count, SizeOf(TCnSM4Buffer));
  end;

  if Count > 0 then
  begin
    Done := Source.Read(TempIn, Count);
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4ReadError);
    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));

    PCardinal(@TempOut[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@TempOut[0])^;
    PCardinal(@TempOut[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@TempOut[4])^;
    PCardinal(@TempOut[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@TempOut[8])^;
    PCardinal(@TempOut[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@TempOut[12])^;

    Done := Dest.Write(TempOut, Count);  // 最后写入的只包括密文长度的部分,无需整个块
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4WriteError);
  end;
end;

procedure SM4DecryptStreamCFB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
var
  TempIn, TempOut: TCnSM4Buffer;
  Vector: TCnSM4Iv;
  Done: Cardinal;
  Ctx: TCnSM4Context;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end
  else
    Count := Min(Count, Source.Size - Source.Position);

  if Count = 0 then
    Exit;

  Vector := InitVector;
  SM4SetKeyEnc(Ctx, @(Key[0]));  // 注意是加密!不是解密!

  while Count >= SizeOf(TCnSM4Buffer) do
  begin
    Done := Source.Read(TempIn, SizeOf(TempIn));             // 密文读入至 TempIn
    if Done < SizeOf(TempIn) then
      raise EStreamError(SCnErrorSM4ReadError);
    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0])); // Iv 先加密至 TempOut

    // 加密后的内容 TempOut 和密文 TempIn 异或得到明文 TempOut
    PCardinal(@TempOut[0])^ := PCardinal(@TempOut[0])^ xor PCardinal(@TempIn[0])^;
    PCardinal(@TempOut[4])^ := PCardinal(@TempOut[4])^ xor PCardinal(@TempIn[4])^;
    PCardinal(@TempOut[8])^ := PCardinal(@TempOut[8])^ xor PCardinal(@TempIn[8])^;
    PCardinal(@TempOut[12])^ := PCardinal(@TempOut[12])^ xor PCardinal(@TempIn[12])^;

    Done := Dest.Write(TempOut, SizeOf(TempOut));      // 明文 TempOut 写出去
    if Done < SizeOf(TempOut) then
      raise EStreamError(SCnErrorSM4WriteError);
    Move(TempIn[0], Vector[0], SizeOf(TCnSM4Iv));       // 保留密文 TempIn 取代 Iv 作为下一次加密再异或的内容
    Dec(Count, SizeOf(TCnSM4Buffer));
  end;

  if Count > 0 then
  begin
    Done := Source.Read(TempIn, Count);
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4ReadError);
    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));

    PCardinal(@TempOut[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@TempOut[0])^;
    PCardinal(@TempOut[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@TempOut[4])^;
    PCardinal(@TempOut[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@TempOut[8])^;
    PCardinal(@TempOut[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@TempOut[12])^;

    Done := Dest.Write(TempOut, Count);  // 最后写入的只包括密文长度的部分,无需整个块
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4WriteError);
  end;
end;

procedure SM4EncryptStreamOFB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
var
  TempIn, TempOut: TCnSM4Buffer;
  Vector: TCnSM4Iv;
  Done: Cardinal;
  Ctx: TCnSM4Context;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end
  else
    Count := Min(Count, Source.Size - Source.Position);

  if Count = 0 then
    Exit;

  Vector := InitVector;
  SM4SetKeyEnc(Ctx, @(Key[0]));

  while Count >= SizeOf(TCnSM4Buffer) do
  begin
    Done := Source.Read(TempIn, SizeOf(TempIn));
    if Done < SizeOf(TempIn) then
      raise EStreamError.Create(SCnErrorSM4ReadError);

    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));     // Key 先加密 Iv

    PCardinal(@TempIn[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@TempOut[0])^;  // 加密结果与明文异或
    PCardinal(@TempIn[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@TempOut[4])^;
    PCardinal(@TempIn[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@TempOut[8])^;
    PCardinal(@TempIn[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@TempOut[12])^;

    Done := Dest.Write(TempIn, SizeOf(TempIn));     // 异或的结果写进密文结果
    if Done < SizeOf(TempIn) then
      raise EStreamError.Create(SCnErrorSM4WriteError);

    Move(TempOut[0], Vector[0], SizeOf(TCnSM4Iv));    // 加密结果取代 Iv 供下一轮加密,注意不是异或结果
    Dec(Count, SizeOf(TCnSM4Buffer));
  end;

  if Count > 0 then
  begin
    Done := Source.Read(TempIn, Count);
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4ReadError);
    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));

    PCardinal(@TempIn[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@TempOut[0])^;
    PCardinal(@TempIn[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@TempOut[4])^;
    PCardinal(@TempIn[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@TempOut[8])^;
    PCardinal(@TempIn[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@TempOut[12])^;

    Done := Dest.Write(TempIn, Count);  // 最后写入的只包括密文长度的部分,无需整个块
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4WriteError);
  end;
end;

procedure SM4DecryptStreamOFB(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Iv; Dest: TStream);
var
  TempIn, TempOut: TCnSM4Buffer;
  Vector: TCnSM4Iv;
  Done: Cardinal;
  Ctx: TCnSM4Context;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end
  else
    Count := Min(Count, Source.Size - Source.Position);

  if Count = 0 then
    Exit;

  Vector := InitVector;
  SM4SetKeyEnc(Ctx, @(Key[0]));  // 注意是加密!不是解密!

  while Count >= SizeOf(TCnSM4Buffer) do
  begin
    Done := Source.Read(TempIn, SizeOf(TempIn));             // 密文读入至 TempIn
    if Done < SizeOf(TempIn) then
      raise EStreamError(SCnErrorSM4ReadError);
    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));  // Iv 先加密至 TempOut

    // 加密后的内容 TempOut 和密文 TempIn 异或得到明文 TempIn
    PCardinal(@TempIn[0])^ := PCardinal(@TempOut[0])^ xor PCardinal(@TempIn[0])^;
    PCardinal(@TempIn[4])^ := PCardinal(@TempOut[4])^ xor PCardinal(@TempIn[4])^;
    PCardinal(@TempIn[8])^ := PCardinal(@TempOut[8])^ xor PCardinal(@TempIn[8])^;
    PCardinal(@TempIn[12])^ := PCardinal(@TempOut[12])^ xor PCardinal(@TempIn[12])^;

    Done := Dest.Write(TempIn, SizeOf(TempIn));        // 明文 TempIn 写出去
    if Done < SizeOf(TempIn) then
      raise EStreamError(SCnErrorSM4WriteError);
    Move(TempOut[0], Vector[0], SizeOf(TCnSM4Iv));       // 保留加密结果 TempOut 取代 Iv 作为下一次加密再异或的内容
    Dec(Count, SizeOf(TCnSM4Buffer));
  end;

  if Count > 0 then
  begin
    Done := Source.Read(TempIn, Count);
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4ReadError);
    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));

    PCardinal(@TempIn[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@TempOut[0])^;
    PCardinal(@TempIn[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@TempOut[4])^;
    PCardinal(@TempIn[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@TempOut[8])^;
    PCardinal(@TempIn[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@TempOut[12])^;

    Done := Dest.Write(TempOut, Count);  // 最后写入的只包括密文长度的部分,无需整个块
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4WriteError);
  end;
end;

procedure SM4EncryptStreamCTR(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Nonce; Dest: TStream);
var
  TempIn, TempOut: TCnSM4Buffer;
  Vector: TCnSM4Iv;
  Done: Cardinal;
  Ctx: TCnSM4Context;
  Cnt, T: Int64;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end
  else
    Count := Min(Count, Source.Size - Source.Position);

  if Count = 0 then
    Exit;

  Cnt := 1;
  SM4SetKeyEnc(Ctx, @(Key[0]));

  while Count >= SizeOf(TCnSM4Buffer) do
  begin
    Done := Source.Read(TempIn, SizeOf(TempIn));
    if Done < SizeOf(TempIn) then
      raise EStreamError.Create(SCnErrorSM4ReadError);

    // Nonce 和计数器拼成 Iv
    T := Int64HostToNetwork(Cnt);
    Move(InitVector[0], Vector[0], SizeOf(TCnSM4Nonce));
    Move(T, Vector[SizeOf(TCnSM4Nonce)], SizeOf(Int64));

    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));     // Key 先加密 Iv

    PCardinal(@TempIn[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@TempOut[0])^;  // 加密结果与明文异或
    PCardinal(@TempIn[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@TempOut[4])^;
    PCardinal(@TempIn[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@TempOut[8])^;
    PCardinal(@TempIn[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@TempOut[12])^;

    Done := Dest.Write(TempIn, SizeOf(TempIn));   // 异或的结果写进密文结果
    if Done < SizeOf(TempIn) then
      raise EStreamError.Create(SCnErrorSM4WriteError);

    Inc(Cnt);
    Dec(Count, SizeOf(TCnSM4Buffer));
  end;

  if Count > 0 then
  begin
    Done := Source.Read(TempIn, Count);
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4ReadError);

    // Nonce 和计数器拼成 Iv
    T := Int64HostToNetwork(Cnt);
    Move(InitVector[0], Vector[0], SizeOf(TCnSM4Nonce));
    Move(T, Vector[SizeOf(TCnSM4Nonce)], SizeOf(Int64));

    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));

    PCardinal(@TempIn[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@TempOut[0])^;
    PCardinal(@TempIn[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@TempOut[4])^;
    PCardinal(@TempIn[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@TempOut[8])^;
    PCardinal(@TempIn[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@TempOut[12])^;

    Done := Dest.Write(TempIn, Count);  // 最后写入的只包括密文长度的部分,无需整个块
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4WriteError);
  end;
end;

procedure SM4DecryptStreamCTR(Source: TStream; Count: Cardinal;
  const Key: TCnSM4Key; const InitVector: TCnSM4Nonce; Dest: TStream);
var
  TempIn, TempOut: TCnSM4Buffer;
  Vector: TCnSM4Iv;
  Done: Cardinal;
  Ctx: TCnSM4Context;
  Cnt, T: Int64;
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end
  else
    Count := Min(Count, Source.Size - Source.Position);

  if Count = 0 then
    Exit;

  Cnt := 1;
  SM4SetKeyEnc(Ctx, @(Key[0]));    // 注意是加密!不是解密!

  while Count >= SizeOf(TCnSM4Buffer) do
  begin
    Done := Source.Read(TempIn, SizeOf(TempIn));
    if Done < SizeOf(TempIn) then
      raise EStreamError.Create(SCnErrorSM4ReadError);

    // Nonce 和计数器拼成 Iv
    T := Int64HostToNetwork(Cnt);
    Move(InitVector[0], Vector[0], SizeOf(TCnSM4Nonce));
    Move(T, Vector[SizeOf(TCnSM4Nonce)], SizeOf(Int64));

    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));     // Key 先加密 Iv

    PCardinal(@TempIn[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@TempOut[0])^;  // 加密结果与密文异或
    PCardinal(@TempIn[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@TempOut[4])^;
    PCardinal(@TempIn[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@TempOut[8])^;
    PCardinal(@TempIn[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@TempOut[12])^;

    Done := Dest.Write(TempIn, SizeOf(TempIn));   // 异或的结果写进明文结果
    if Done < SizeOf(TempIn) then
      raise EStreamError.Create(SCnErrorSM4WriteError);

    Inc(Cnt);
    Dec(Count, SizeOf(TCnSM4Buffer));
  end;

  if Count > 0 then
  begin
    Done := Source.Read(TempIn, Count);
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4ReadError);

    // Nonce 和计数器拼成 Iv
    T := Int64HostToNetwork(Cnt);
    Move(InitVector[0], Vector[0], SizeOf(TCnSM4Nonce));
    Move(T, Vector[SizeOf(TCnSM4Nonce)], SizeOf(Int64));

    SM4OneRound(@(Ctx.Sk[0]), @(Vector[0]), @(TempOut[0]));

    PCardinal(@TempIn[0])^ := PCardinal(@TempIn[0])^ xor PCardinal(@TempOut[0])^;
    PCardinal(@TempIn[4])^ := PCardinal(@TempIn[4])^ xor PCardinal(@TempOut[4])^;
    PCardinal(@TempIn[8])^ := PCardinal(@TempIn[8])^ xor PCardinal(@TempOut[8])^;
    PCardinal(@TempIn[12])^ := PCardinal(@TempIn[12])^ xor PCardinal(@TempOut[12])^;

    Done := Dest.Write(TempIn, Count);  // 最后写入的只包括密文长度的部分,无需整个块
    if Done < Count then
      raise EStreamError.Create(SCnErrorSM4WriteError);
  end;
end;

procedure SM4Encrypt(Key: PAnsiChar; Input: PAnsiChar; Output: PAnsiChar; Len: Integer);
var
  Ctx: TCnSM4Context;
begin
  SM4SetKeyEnc(Ctx, Key);
  SM4CryptEcb(Ctx, SM4_ENCRYPT, Len, Input, Output);
end;

procedure SM4Decrypt(Key: PAnsiChar; Input: PAnsiChar; Output: PAnsiChar; Len: Integer);
var
  Ctx: TCnSM4Context;
begin
  SM4SetKeyDec(Ctx, Key);
  SM4CryptEcb(Ctx, SM4_DECRYPT, Len, Input, Output);
end;

end.

  

{******************************************************************************}
{                       CnPack For Delphi/C++Builder                           }
{                     中国人自己的开放源码第三方开发包                         }
{                   (C)Copyright 2001-2024 CnPack 开发组                       }
{                   ------------------------------------                       }

{            本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修        }
{        改和重新发布这一程序。                                                }

{            发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有        }
{        适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。        }

{            您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果        }
{        还没有,可访问我们的网站:                                            }

{            网站地址:http://www.cnpack.org                                   }
{            电子邮件:[email protected]                                       }

{******************************************************************************}

unit CnNative;

{* |<PRE>
================================================================================
* 软件名称:CnPack 组件包
* 单元名称:32 位和 64 位的一些统一声明以及一堆基础实现
* 单元作者:刘啸 ([email protected])
* 备    注:Delphi XE 2 支持 32 和 64 以来,开放出的 NativeInt 和 NativeUInt 随
*           当前是 32 位还是 64 而动态变化,影响到的是 Pointer、Reference等东西。
*           考虑到兼容性,固定长度的 32 位 Cardinal/Integer 等和 Pointer 这些就
*           不能再通用了,即使 32 位下也被编译器禁止。因此本单元声明了几个类型,
*           供同时在低版本和高版本的 Delphi 中使用。
*           后来加入 UInt64 的包装,注意 D567 下不直接支持 UInt64 的运算,需要用
*           辅助函数实现,目前实现了 div 与 mod
*           另外地址运算 Integer(APtr) 在 64 位下尤其是 MacOS 上容易出现截断,需要用 NativeInt
*           后来补上大量底层的函数与工具类
* 开发平台:PWin2000 + Delphi 5.0
* 兼容测试:PWin9X/2000/XP + Delphi 5/6/7 XE 2
* 本 地 化:该单元中的字符串均符合本地化处理方式
* 修改记录:2023.08.14 V2.4
*               补上几个时间固定的函数并改名
*           2022.11.11 V2.3
*               补上几个无符号数的字节顺序调换函数
*           2022.07.23 V2.2
*               增加几个内存位运算函数与二进制转换字符串函数,并改名为 CnNative
*           2022.06.08 V2.1
*               增加四个时间固定的交换函数以及内存倒排函数
*           2022.03.14 V2.0
*               增加几个十六进制转换函数
*           2022.02.17 V1.9
*               增加 FPC 的编译支持
*           2022.02.09 V1.8
*               加入运行期的大小端判断函数
*           2021.09.05 V1.7
*               加入 Int64/UInt64 的整数次幂与根的运算函数
*           2020.10.28 V1.6
*               加入 UInt64 溢出相关的判断与运算函数
*           2020.09.06 V1.5
*               加入求 UInt64 整数平方根的函数
*           2020.07.01 V1.5
*               加入判断 32 位与 64 位有无符号数相加是否溢出的函数
*           2020.06.20 V1.4
*               加入 32 位与 64 位获取最高与最低的 1 位位置的函数
*           2020.01.01 V1.3
*               加入 32 位无符号整型的 mul 运算,在不支持 UInt64 的系统上以 Int64 代替以避免溢出
*           2018.06.05 V1.2
*               加入 64 位整型的 div/mod 运算,在不支持 UInt64 的系统上以 Int64 代替
*           2016.09.27 V1.1
*               加入 64 位整型的一些定义
*           2011.07.06 V1.0
*               创建单元,实现功能
================================================================================
|</PRE>}

interface

{.$I CnPack.inc}

uses
Classes, SysUtils, SysConst, Math {$IFDEF COMPILER5}, Windows {$ENDIF};
// D5 下需要引用 Windows 中的 PByte
type
{$IFDEF COMPILER5}
  PCardinal = ^Cardinal;
  {* D5 下 System 单元中未定义,定义上}
  PByte = Windows.PByte;
  {* D5 下 PByte 定义在 Windows 中,其他版本定义在 System 中,
    这里统一一下供外界使用 PByte 时无需 uses Windows,以有利于跨平台}
{$ENDIF}
{$DEFine SUPPORT_UINT64}
{$IFDEF SUPPORT_32_AND_64}
  TCnNativeInt     = NativeInt;
  TCnNativeUInt    = NativeUInt;
  TCnNativePointer = NativeInt;
  TCnNativeIntPtr  = PNativeInt;
  TCnNativeUIntPtr = PNativeUInt;
{$ELSE}
TCnNativeInt     = integer;
TCnNativeUInt    = cardinal;
TCnNativePointer = integer;
TCnNativeIntPtr  = PInteger;
TCnNativeUIntPtr = PCardinal;
{$ENDIF}

{$IFDEF CPU64BITS}
  TCnUInt64        = NativeUInt;
  TCnInt64         = NativeInt;
{$ELSE}
  {$IFDEF SUPPORT_UINT64}
  TCnUInt64        = UInt64;
  {$ELSE}
TCnUInt64 = packed record  // 只能用这样的结构代替
  case boolean of
    True: (Value: int64);
    False: (Lo32, Hi32: cardinal);
end;
  {$ENDIF}
TCnInt64 = int64;
{$ENDIF}

// TUInt64 用于 cnvcl 库中不支持 UInt64 的运算如 div mod 等
{$IFDEF SUPPORT_UINT64}
  TUInt64          = UInt64;
  {$IFNDEF SUPPORT_PUINT64}
  PUInt64          = ^UInt64;
  {$ENDIF}
{$ELSE}
TUInt64 = int64;
PUInt64 = ^TUInt64;
{$ENDIF}

{$IFNDEF SUPPORT_INT64ARRAY}
// 如果系统没有定义 Int64Array
Int64Array  = array[0..$0FFFFFFE] of int64;
PInt64Array = ^Int64Array;
{$ENDIF}

TUInt64Array = array of TUInt64;
// 这个动态数组声明似乎容易和静态数组声明有冲突

ExtendedArray  = array[0..65537] of extended;
PExtendedArray = ^ExtendedArray;

PCnWord16Array = ^TCnWord16Array;
TCnWord16Array = array [0..0] of word;

{$IFDEF POSIX64}
  TCnLongWord32 = Cardinal; // Linux64/MacOS64 (or POSIX64?) LongWord is 64 Bits
{$ELSE}
TCnLongWord32 = longword;
{$ENDIF}
PCnLongWord32 = ^TCnLongWord32;

TCnLongWord32Array = array [0..MaxInt div SizeOf(integer) - 1] of TCnLongWord32;

PCnLongWord32Array = ^TCnLongWord32Array;

{$IFNDEF TBYTES_DEFINED}
TBytes = array of byte;
{* 无符号字节动态数组,未定义时定义上}
{$ENDIF}

TShortInts = array of shortint;
{* 有符号字节动态数组}

TSmallInts = array of smallint;
{* 有符号双字节动态数组}

TWords = array of word;
{* 无符号双字节动态数组}

TIntegers = array of integer;
{* 有符号四字节动态数组}

TCardinals = array of cardinal;
{* 无符号四字节动态数组}

PCnByte = ^byte;
PCnWord = ^word;

TCnBitOperation = (boAnd, boOr, boXor, boNot);
{* 位操作类型}

type
TCnMemSortCompareProc = function(p1, p2: Pointer; ElementByteSize: integer): integer;
{* 内存固定块尺寸的数组排序比较函数原型}

const
CN_MAX_SQRT_INT64: cardinal = 3037000499;
CN_MAX_INT64: int64     = $7FFFFFFFFFFFFFFF;
CN_MIN_INT64: int64     = $8000000000000000;
CN_MAX_UINT16: word     = $FFFF;
CN_MAX_UINT32: cardinal = $FFFFFFFF;
CN_MAX_TUINT64: TUInt64 = $FFFFFFFFFFFFFFFF;
CN_MAX_SIGNED_INT64_IN_TUINT64: TUInt64 = $7FFFFFFFFFFFFFFF;

{*
  对于 D567 等不支持 UInt64 的编译器,虽然可以用 Int64 代替 UInt64 进行加减、存储
  但乘除运算则无法直接完成,这里封装了两个调用 System 库中的 _lludiv 与 _llumod
  函数,实现以 Int64 表示的 UInt64 数据的 div 与 mod 功能。
}
function UInt64Mod(a, b: TUInt64): TUInt64;
{* 两个 UInt64 求余}

function UInt64Div(a, b: TUInt64): TUInt64;
{* 两个 UInt64 整除}

function UInt64Mul(a, b: cardinal): TUInt64;
{* 无符号 32 位整数不溢出的相乘,在不支持 UInt64 的平台上,结果以 UInt64 的形式放在 Int64 里,
  如果结果直接使用 Int64 计算则有可能溢出}

procedure UInt64AddUInt64(a, b: TUInt64; var ResLo, ResHi: TUInt64);
{* 两个无符号 64 位整数相加,处理溢出的情况,结果放 ResLo 与 ResHi 中
  注:内部实现按算法来看较为复杂,实际上如果溢出,ResHi 必然是 1,直接判断溢出并将其设 1 即可}

procedure UInt64MulUInt64(a, b: TUInt64; var ResLo, ResHi: TUInt64);
{* 两个无符号 64 位整数相乘,结果放 ResLo 与 ResHi 中,64 位下用汇编实现,提速约一倍以上}

function UInt64ToHex(N: TUInt64): string;
{* 将 UInt64 转换为十六进制字符串}

function UInt64ToStr(N: TUInt64): string;
{* 将 UInt64 转换为字符串}

function StrToUInt64(const S: string): TUInt64;
{* 将字符串转换为 UInt64}

function UInt64Compare(a, b: TUInt64): integer;
{* 比较两个 UInt64 值,分别根据 > = < 返回 1、0、-1}

function UInt64Sqrt(N: TUInt64): TUInt64;
{* 求 UInt64 的平方根的整数部分}

function UInt32IsNegative(N: cardinal): boolean;
{* 该 Cardinal 被当成 Integer 时是否小于 0}

function UInt64IsNegative(N: TUInt64): boolean;
{* 该 UInt64 被当成 Int64 时是否小于 0}

procedure UInt64SetBit(var b: TUInt64; Index: integer);
{* 给 UInt64 的某一位置 1,位 Index 从 0 开始}

procedure UInt64ClearBit(var b: TUInt64; Index: integer);
{* 给 UInt64 的某一位置 0,位 Index 从 0 开始}

function GetUInt64BitSet(b: TUInt64; Index: integer): boolean;
{* 返回 UInt64 的某一位是否是 1,位 Index 从 0 开始}

function GetUInt64HighBits(b: TUInt64): integer;
{* 返回 UInt64 的是 1 的最高二进制位是第几位,最低位是 0,如果没有 1,返回 -1}

function GetUInt32HighBits(b: cardinal): integer;
{* 返回 Cardinal 的是 1 的最高二进制位是第几位,最低位是 0,如果没有 1,返回 -1}

function GetUInt16HighBits(b: word): integer;
{* 返回 Word 的是 1 的最高二进制位是第几位,最低位是 0,如果没有 1,返回 -1}

function GetUInt8HighBits(b: byte): integer;
{* 返回 Byte 的是 1 的最高二进制位是第几位,最低位是 0,如果没有 1,返回 -1}

function GetUInt64LowBits(b: TUInt64): integer;
{* 返回 Int64 的是 1 的最低二进制位是第几位,最低位是 0,基本等同于末尾几个 0。如果没有 1,返回 -1}

function GetUInt32LowBits(b: cardinal): integer;
{* 返回 Cardinal 的是 1 的最低二进制位是第几位,最低位是 0,基本等同于末尾几个 0。如果没有 1,返回 -1}

function GetUInt16LowBits(b: word): integer;
{* 返回 Word 的是 1 的最低二进制位是第几位,最低位是 0,基本等同于末尾几个 0。如果没有 1,返回 -1}

function GetUInt8LowBits(b: byte): integer;
{* 返回 Byte 的是 1 的最低二进制位是第几位,最低位是 0,基本等同于末尾几个 0。如果没有 1,返回 -1}

function Int64Mod(M, N: int64): int64;
{* 封装的 Int64 Mod,M 碰到负值时取反求模再模减,但 N 仍要求正数否则结果不靠谱}

function IsUInt32PowerOf2(N: cardinal): boolean;
{* 判断一 32 位无符号整数是否 2 的整数次幂}

function IsUInt64PowerOf2(N: TUInt64): boolean;
{* 判断一 64 位无符号整数是否 2 的整数次幂}

function GetUInt32PowerOf2GreaterEqual(N: cardinal): cardinal;
{* 得到一比指定 32 位无符号整数数大或等的 2 的整数次幂,如溢出则返回 0}

function GetUInt64PowerOf2GreaterEqual(N: TUInt64): TUInt64;
{* 得到一比指定 64 位无符号整数数大或等的 2 的整数次幂,如溢出则返回 0}

function IsInt32AddOverflow(a, b: integer): boolean;
{* 判断两个 32 位有符号数相加是否溢出 32 位有符号上限}

function IsUInt32AddOverflow(a, b: cardinal): boolean;
{* 判断两个 32 位无符号数相加是否溢出 32 位无符号上限}

function IsInt64AddOverflow(a, b: int64): boolean;
{* 判断两个 64 位有符号数相加是否溢出 64 位有符号上限}

function IsUInt64AddOverflow(a, b: TUInt64): boolean;
{* 判断两个 64 位无符号数相加是否溢出 64 位无符号上限}

procedure UInt64Add(var r: TUInt64; a, b: TUInt64; out Carry: integer);
{* 两个 64 位无符号数相加,A + B => R,如果有溢出,则溢出的 1 搁进位标记里,否则清零}

procedure UInt64Sub(var r: TUInt64; a, b: TUInt64; out Carry: integer);
{* 两个 64 位无符号数相减,A - B => R,如果不够减有借位,则借的 1 搁借位标记里,否则清零}

function IsInt32MulOverflow(a, b: integer): boolean;
{* 判断两个 32 位有符号数相乘是否溢出 32 位有符号上限}

function IsUInt32MulOverflow(a, b: cardinal): boolean;
{* 判断两个 32 位无符号数相乘是否溢出 32 位无符号上限}

function IsUInt32MulOverflowInt64(a, b: cardinal; out r: TUInt64): boolean;
{* 判断两个 32 位无符号数相乘是否溢出 64 位有符号数,如未溢出也即返回 False 时,R 中直接返回结果
  如溢出也即返回 True,外界需要重新调用 UInt64Mul 才能实施相乘}

function IsInt64MulOverflow(a, b: int64): boolean;
{* 判断两个 64 位有符号数相乘是否溢出 64 位有符号上限}

function PointerToInteger(P: Pointer): integer;
{* 指针类型转换成整型,支持 32/64 位,注意 64 位下可能会丢超出 32 位的内容}

function IntegerToPointer(i: integer): Pointer;
{* 整型转换成指针类型,支持 32/64 位}

function Int64NonNegativeAddMod(a, b, N: int64): int64;
{* 求 Int64 范围内俩加数的和求余,处理溢出的情况,要求 N 大于 0}

function UInt64NonNegativeAddMod(a, b, N: TUInt64): TUInt64;
{* 求 UInt64 范围内俩加数的和求余,处理溢出的情况,要求 N 大于 0}

function Int64NonNegativeMulMod(a, b, N: int64): int64;
{* Int64 范围内的相乘求余,不能直接计算,容易溢出。要求 N 大于 0}

function UInt64NonNegativeMulMod(a, b, N: TUInt64): TUInt64;
{* UInt64 范围内的相乘求余,不能直接计算,容易溢出。}

function Int64NonNegativeMod(N: int64; P: int64): int64;
{* 封装的 Int64 非负求余函数,也就是余数为负时,加个除数变正,调用者需保证 P 大于 0}

function Int64NonNegativPower(N: int64; Exp: integer): int64;
{* Int64 的非负整数指数幂,不考虑溢出的情况}

function Int64NonNegativeRoot(N: int64; Exp: integer): int64;
{* 求 Int64 的非负整数次方根的整数部分,不考虑溢出的情况}

function UInt64NonNegativPower(N: TUInt64; Exp: integer): TUInt64;
{* UInt64 的非负整数指数幂,不考虑溢出的情况}

function UInt64NonNegativeRoot(N: TUInt64; Exp: integer): TUInt64;
{* 求 UInt64 的非负整数次方根的整数部分,不考虑溢出的情况}

function CurrentByteOrderIsBigEndian: boolean;
{* 返回当前运行期环境是否是大端,也就是是否将整数中的高序字节存储在较低的起始地址,符合从左到右的阅读习惯,如部分指定的 ARM 和 MIPS}

function CurrentByteOrderIsLittleEndian: boolean;
{* 返回当前运行期环境是否是小端,也就是是否将整数中的高序字节存储在较高的起始地址,如 x86 与部分默认 arm}

function Int64ToBigEndian(Value: int64): int64;
{* 确保 Int64 值为大端,在小端环境中会进行转换}

function Int32ToBigEndian(Value: integer): integer;
{* 确保 Int32 值为大端,在小端环境中会进行转换}

function Int16ToBigEndian(Value: smallint): smallint;
{* 确保 Int16 值为大端,在小端环境中会进行转换}

function Int64ToLittleEndian(Value: int64): int64;
{* 确保 Int64 值为小端,在大端环境中会进行转换}

function Int32ToLittleEndian(Value: integer): integer;
{* 确保 Int32 值为小端,在大端环境中会进行转换}

function Int16ToLittleEndian(Value: smallint): smallint;
{* 确保 Int16 值为小端,在大端环境中会进行转换}

function UInt64ToBigEndian(Value: TUInt64): TUInt64;
{* 确保 UInt64 值为大端,在小端环境中会进行转换}

function UInt32ToBigEndian(Value: cardinal): cardinal;
{* 确保 UInt32 值为大端,在小端环境中会进行转换}

function UInt16ToBigEndian(Value: word): word;
{* 确保 UInt16 值为大端,在小端环境中会进行转换}

function UInt64ToLittleEndian(Value: TUInt64): TUInt64;
{* 确保 UInt64 值为小端,在大端环境中会进行转换}

function UInt32ToLittleEndian(Value: cardinal): cardinal;
{* 确保 UInt32 值为小端,在大端环境中会进行转换}

function UInt16ToLittleEndian(Value: word): word;
{* 确保 UInt16 值为小端,在大端环境中会进行转换}

function Int64HostToNetwork(Value: int64): int64;
{* 将 Int64 值从主机字节顺序转换为网络字节顺序,在小端环境中会进行转换}

function Int32HostToNetwork(Value: integer): integer;
{* 将 Int32 值从主机字节顺序转换为网络字节顺序,在小端环境中会进行转换}

function Int16HostToNetwork(Value: smallint): smallint;
{* 将 Int16 值从主机字节顺序转换为网络字节顺序,在小端环境中会进行转换}

function Int64NetworkToHost(Value: int64): int64;
{* 将 Int64 值从网络字节顺序转换为主机字节顺序,在小端环境中会进行转换}

function Int32NetworkToHost(Value: integer): integer;
{* 将 Int32值从网络字节顺序转换为主机字节顺序,在小端环境中会进行转换}

function Int16NetworkToHost(Value: smallint): smallint;
{* 将 Int16 值从网络字节顺序转换为主机字节顺序,在小端环境中会进行转换}

function UInt64HostToNetwork(Value: TUInt64): TUInt64;
{* 将 UInt64 值从主机字节顺序转换为网络字节顺序,在小端环境中会进行转换}

function UInt32HostToNetwork(Value: cardinal): cardinal;
{* 将 UInt32 值从主机字节顺序转换为网络字节顺序,在小端环境中会进行转换}

function UInt16HostToNetwork(Value: word): word;
{* 将 UInt16 值从主机字节顺序转换为网络字节顺序,在小端环境中会进行转换}

function UInt64NetworkToHost(Value: TUInt64): TUInt64;
{* 将 UInt64 值从网络字节顺序转换为主机字节顺序,在小端环境中会进行转换}

function UInt32NetworkToHost(Value: cardinal): cardinal;
{* 将 UInt32值从网络字节顺序转换为主机字节顺序,在小端环境中会进行转换}

function UInt16NetworkToHost(Value: word): word;
{* 将 UInt16 值从网络字节顺序转换为主机字节顺序,在小端环境中会进行转换}

procedure MemoryNetworkToHost(AMem: Pointer; MemByteLen: integer);
{* 将一片内存区域从网络字节顺序转换为主机字节顺序,在小端环境中会进行转换,
  该方法应用场合较少,大多二/四/八字节转换已经足够}

procedure MemoryHostToNetwork(AMem: Pointer; MemByteLen: integer);
{* 将一片内存区域从主机字节顺序转换为网络字节顺序,在小端环境中会进行转换,
  该方法应用场合较少,大多二/四/八字节转换已经足够}

procedure ReverseMemory(AMem: Pointer; MemByteLen: integer);
{* 按字节顺序倒置一块内存块,字节内部不变}

function ReverseBitsInInt8(V: byte): byte;
{* 倒置一字节内部的位的内容}

function ReverseBitsInInt16(V: word): word;
{* 倒置二字节及其内部位的内容}

function ReverseBitsInInt32(V: cardinal): cardinal;
{* 倒置四字节及其内部位的内容}

function ReverseBitsInInt64(V: int64): int64;
{* 倒置八字节及其内部位的内容}

procedure ReverseMemoryWithBits(AMem: Pointer; MemByteLen: integer);
{* 按字节顺序倒置一块内存块,并且每个字节也倒过来}

procedure MemoryAnd(AMem, BMem: Pointer; MemByteLen: integer; ResMem: Pointer);
{* 两块长度相同的内存 AMem 和 BMem 按位与,结果放 ResMem 中,三者可相同}

procedure MemoryOr(AMem, BMem: Pointer; MemByteLen: integer; ResMem: Pointer);
{* 两块长度相同的内存 AMem 和 BMem 按位或,结果放 ResMem 中,三者可相同}

procedure MemoryXor(AMem, BMem: Pointer; MemByteLen: integer; ResMem: Pointer);
{* 两块长度相同的内存 AMem 和 BMem 按位异或,结果放 ResMem 中,三者可相同}

procedure MemoryNot(AMem: Pointer; MemByteLen: integer; ResMem: Pointer);
{* 一块内存 AMem 取反,结果放 ResMem 中,两者可相同}

procedure MemoryShiftLeft(AMem, BMem: Pointer; MemByteLen: integer; BitCount: integer);
{* AMem 整块内存左移 BitCount 位至 BMem,往内存地址低位移,空位补 0,两者可相等}

procedure MemoryShiftRight(AMem, BMem: Pointer; MemByteLen: integer; BitCount: integer);
{* AMem 整块内存右移 BitCount 位至 BMem,往内存地址高位移,空位补 0,两者可相等}

function MemoryIsBitSet(AMem: Pointer; N: integer): boolean;
{* 返回内存块某 Bit 位是否置 1,内存地址低位是 0,字节内还是右边为 0}

procedure MemorySetBit(AMem: Pointer; N: integer);
{* 给内存块某 Bit 位置 1,内存地址低位是 0,字节内还是右边为 0}

procedure MemoryClearBit(AMem: Pointer; N: integer);
{* 给内存块某 Bit 位置 0,内存地址低位是 0,字节内还是右边为 0}

function MemoryToBinStr(AMem: Pointer; MemByteLen: integer;
Sep: boolean = False): string;
{* 将一块内存内容从低到高字节顺序输出为二进制字符串,Sep 表示是否空格分隔}

procedure MemorySwap(AMem, BMem: Pointer; MemByteLen: integer);
{* 交换两块相同长度的内存块的内容,如两者是相同的内存块则什么都不做}

function MemoryCompare(AMem, BMem: Pointer; MemByteLen: integer): integer;
{* 以无符号数的方式比较两块内存,返回 1、0、-1,如两者是相同的内存块则直接返回 0}

procedure MemoryQuickSort(Mem: Pointer; ElementByteSize: integer;
ElementCount: integer; CompareProc: TCnMemSortCompareProc = nil);
{* 针对固定大小的元素的数组进行排序}

function UInt8ToBinStr(V: byte): string;
{* 将一无符号字节转换为二进制字符串}

function UInt16ToBinStr(V: word): string;
{* 将一无符号字转换为二进制字符串}

function UInt32ToBinStr(V: cardinal): string;
{* 将一四字节无符号整数转换为二进制字符串}

function UInt32ToStr(V: cardinal): string;
{* 将一四字节无符号整数转换为字符串}

function UInt64ToBinStr(V: TUInt64): string;
{* 将一无符号 64 字节整数转换为二进制字符串}

function HexToInt(const Hex: string): integer; overload;
{* 将一十六进制字符串转换为整型,适合较短尤其是 2 字符的字符串}

function HexToInt(Hex: PChar; CharLen: integer): integer; overload;
{* 将一十六进制字符串指针所指的内容转换为整型,适合较短尤其是 2 字符的字符串}

function IsHexString(const Hex: string): boolean;
{* 判断一字符串是否合法的十六进制字符串,不区分大小写}

function DataToHex(InData: Pointer; ByteLength: integer;
UseUpperCase: boolean = True): string;
{* 内存块转换为十六进制字符串,内存低位的内容出现在字符串左方,相当于网络字节顺序,
  UseUpperCase 控制输出内容的大小写}

function HexToData(const Hex: string; OutData: Pointer = nil): integer;
{* 十六进制字符串转换为内存块,字符串左方的内容出现在内存低位,相当于网络字节顺序,
  十六进制字符串长度为奇或转换失败时抛出异常。返回转换成功的字节数
  注意 OutData 应该指向足够容纳转换内容的区域,长度至少为 Length(Hex) div 2
  如果传 nil,则只返回所需的字节长度,不进行正式转换}

function StringToHex(const Data: string; UseUpperCase: boolean = True): string;
{* 字符串转换为十六进制字符串,UseUpperCase 控制输出内容的大小写}

function HexToString(const Hex: string): string;
{* 十六进制字符串转换为字符串,十六进制字符串长度为奇或转换失败时抛出异常}

function HexToAnsiStr(const Hex: ansistring): ansistring;
{* 十六进制字符串转换为字符串,十六进制字符串长度为奇或转换失败时抛出异常}

function AnsiStrToHex(const Data: ansistring; UseUpperCase: boolean = True): ansistring;
{* AnsiString 转换为十六进制字符串,UseUpperCase 控制输出内容的大小写}

function BytesToHex(Data: TBytes; UseUpperCase: boolean = True): string;
{* 字节数组转换为十六进制字符串,下标低位的内容出现在字符串左方,相当于网络字节顺序,
  UseUpperCase 控制输出内容的大小写}

function HexToBytes(const Hex: string): TBytes;
{* 十六进制字符串转换为字节数组,字符串左边的内容出现在下标低位,相当于网络字节顺序,
  字符串长度为奇或转换失败时抛出异常}

function StreamToHex(Stream: TStream; UseUpperCase: boolean = True): string;
{* 将流中的全部内容从头转换为十六进制字符串}

function HexToStream(const Hex: string; Stream: TStream): integer;
{* 将十六进制字符串内容转换后写入流中,返回写入的字节数}

procedure ReverseBytes(Data: TBytes);
{* 按字节顺序倒置一字节数组}

function StreamToBytes(Stream: TStream): TBytes;
{* 从流从头读入全部内容至字节数组,返回创建的字节数组}

function BytesToStream(Data: TBytes; OutStream: TStream): integer;
{* 字节数组写入整个流,返回写入字节数}

function AnsiToBytes(const str: ansistring): TBytes;
{* 将 AnsiString 的内容转换为字节数组,不处理编码}

function BytesToAnsi(const Data: TBytes): ansistring;
{* 将字节数组的内容转换为 AnsiString,不处理编码}

function BytesToString(const Data: TBytes): string;
{* 将字节数组的内容转换为 string,内部逐个赋值,不处理编码}

function MemoryToString(Mem: Pointer; MemByteLen: integer): string;
{* 将内存块的内容转换为 string,内部逐个赋值,不处理编码}

function ConcatBytes(a, b: TBytes): TBytes;
{* 将 A B 两个字节数组顺序拼好返回一个新字节数组,A B 保持不变}

function NewBytesFromMemory(Data: Pointer; DataByteLen: integer): TBytes;
{* 新建一字节数组,并从一片内存区域复制内容过来。}

function CompareBytes(a, b: TBytes): boolean;
{* 比较两个字节数组内容是否相同}

procedure MoveMost(const Source; var Dest; ByteLen, MostLen: integer);
{* 从 Source 移动 ByteLen 且不超过 MostLen 个字节到 Dest 中,
  如 ByteLen 小于 MostLen,则 Dest 填充 0,要求 Dest 容纳至少 MostLen}

// ================ 以下是执行时间固定的无 if 判断的部分逻辑函数 ===============

procedure ConstTimeConditionalSwap8(CanSwap: boolean; var a, b: byte);
{* 针对两个字节变量的执行时间固定的条件交换,CanSwap 为 True 时才实施 A B 交换}

procedure ConstTimeConditionalSwap16(CanSwap: boolean; var a, b: word);
{* 针对两个双字节变量的执行时间固定的条件交换,CanSwap 为 True 时才实施 A B 交换}

procedure ConstTimeConditionalSwap32(CanSwap: boolean; var a, b: cardinal);
{* 针对两个四字节变量的执行时间固定的条件交换,CanSwap 为 True 时才实施 A B 交换}

procedure ConstTimeConditionalSwap64(CanSwap: boolean; var a, b: TUInt64);
{* 针对两个八字节变量的执行时间固定的条件交换,CanSwap 为 True 时才实施 A B 交换}

function ConstTimeEqual8(a, b: byte): boolean;
{* 针对俩单字节的执行时间固定的比较,避免 CPU 指令跳转预测导致的执行时间差异,内容相同时返回 True}

function ConstTimeEqual16(a, b: word): boolean;
{* 针对俩双字节的执行时间固定的比较,避免 CPU 指令跳转预测导致的执行时间差异,内容相同时返回 True}

function ConstTimeEqual32(a, b: cardinal): boolean;
{* 针对俩四字节的执行时间固定的比较,避免 CPU 指令跳转预测导致的执行时间差异,内容相同时返回 True}

function ConstTimeEqual64(a, b: TUInt64): boolean;
{* 针对俩八字节的执行时间固定的比较,避免 CPU 指令跳转预测导致的执行时间差异,内容相同时返回 True}

function ConstTimeBytesEqual(a, b: TBytes): boolean;
{* 针对俩相同长度的字节数组的执行时间固定的比较,内容相同时返回 True}

function ConstTimeExpandBoolean8(V: boolean): byte;
{* 根据 V 的值返回一字节全 1 或全 0}

function ConstTimeExpandBoolean16(V: boolean): word;
{* 根据 V 的值返回俩字节全 1 或全 0}

function ConstTimeExpandBoolean32(V: boolean): cardinal;
{* 根据 V 的值返回四字节全 1 或全 0}

function ConstTimeExpandBoolean64(V: boolean): TUInt64;
{* 根据 V 的值返回八字节全 1 或全 0}

function ConstTimeConditionalSelect8(Condition: boolean; a, b: byte): byte;
{* 针对两个字节变量执行时间固定的判断选择,Condtion 为 True 时返回 A,否则返回 B}

function ConstTimeConditionalSelect16(Condition: boolean; a, b: word): word;
{* 针对两个双字节变量执行时间固定的判断选择,Condtion 为 True 时返回 A,否则返回 B}

function ConstTimeConditionalSelect32(Condition: boolean; a, b: cardinal): cardinal;
{* 针对两个四字节变量执行时间固定的判断选择,Condtion 为 True 时返回 A,否则返回 B}

function ConstTimeConditionalSelect64(Condition: boolean; a, b: TUInt64): TUInt64;
{* 针对两个八字节变量执行时间固定的判断选择,Condtion 为 True 时返回 A,否则返回 B}

// ================ 以上是执行时间固定的无 if 判断的部分逻辑函数 ===============

{$IFDEF MSWINDOWS}

// 这四个函数因为用了 Intel 汇编,因而只支持 32 位和 64 位的 Intel CPU,照理应该用条件:CPUX86 或 CPUX64

procedure Int64DivInt32Mod(a: int64; b: integer; var DivRes, ModRes: integer);
{* 64 位有符号数除以 32 位有符号数,商放 DivRes,余数放 ModRes
  调用者须自行保证商在 32 位范围内,否则会抛溢出异常}

procedure UInt64DivUInt32Mod(a: TUInt64; b: cardinal; var DivRes, ModRes: cardinal);
{* 64 位无符号数除以 32 位无符号数,商放 DivRes,余数放 ModRes
  调用者须自行保证商在 32 位范围内,否则会抛溢出异常}

procedure Int128DivInt64Mod(ALo, AHi: int64; b: int64; var DivRes, ModRes: int64);
{* 128 位有符号数除以 64 位有符号数,商放 DivRes,余数放 ModRes
  调用者须自行保证商在 64 位范围内,否则会抛溢出异常}

procedure UInt128DivUInt64Mod(ALo, AHi: TUInt64; b: TUInt64;
var DivRes, ModRes: TUInt64);
{* 128 位无符号数除以 64 位无符号数,商放 DivRes,余数放 ModRes
  调用者须自行保证商在 64 位范围内,否则会抛溢出异常}

{$ENDIF}

function IsUInt128BitSet(Lo, Hi: TUInt64; N: integer): boolean;
{* 针对两个 Int64 拼成的 128 位数字,返回第 N 位是否为 1,N 从 0 到 127}

procedure SetUInt128Bit(var Lo, Hi: TUInt64; N: integer);
{* 针对两个 Int64 拼成的 128 位数字,设置第 N 位为 1,N 从 0 到 127}

procedure ClearUInt128Bit(var Lo, Hi: TUInt64; N: integer);
{* 针对两个 Int64 拼成的 128 位数字,清掉第 N 位,N 从 0 到 127}

function UnsignedAddWithLimitRadix(a, b, c: cardinal; var r: cardinal;
L, H: cardinal): cardinal;
{* 计算非正常进制的无符号加法,A + B + C,结果放 R 中,返回进位值
  结果确保在 L 和 H 的闭区间内,用户须确保 H 大于 L,不考虑溢出的情形
  该函数多用于字符分区间计算与映射,其中 C 一般是进位}

{$IFDEF COMPILER5}

function BoolToStr(Value: Boolean; UseBoolStrs: Boolean = False): string;
{* Delphi 5 下没有该函数,补上}

{$ENDIF}

implementation

uses
CnFloat;

var
FByteOrderIsBigEndian: boolean = False;

function CurrentByteOrderIsBigEndian: boolean;
type
  TByteOrder = packed record
    case boolean of
      False: (c: array[0..1] of byte);
      True: (W: word);
    end;
var
  T: TByteOrder;
begin
  T.W    := $00CC;
  Result := T.c[1] = $CC;
end;

function CurrentByteOrderIsLittleEndian: boolean;
begin
  Result := not CurrentByteOrderIsBigEndian;
end;

function ReverseInt64(Value: int64): int64;
var
  Lo, Hi: cardinal;
  Rec: Int64Rec;
begin
  Lo     := Int64Rec(Value).Lo;
  Hi     := Int64Rec(Value).Hi;
  Lo     := ((Lo and $000000FF) shl 24) or ((Lo and $0000FF00) shl 8) or
    ((Lo and $00FF0000) shr 8) or ((Lo and $FF000000) shr 24);
  Hi     := ((Hi and $000000FF) shl 24) or ((Hi and $0000FF00) shl 8) or
    ((Hi and $00FF0000) shr 8) or ((Hi and $FF000000) shr 24);
  Rec.Lo := Hi;
  Rec.Hi := Lo;
  Result := int64(Rec);
end;

function ReverseUInt64(Value: TUInt64): TUInt64;
var
  Lo, Hi: cardinal;
  Rec: Int64Rec;
begin
  Lo     := Int64Rec(Value).Lo;
  Hi     := Int64Rec(Value).Hi;
  Lo     := ((Lo and $000000FF) shl 24) or ((Lo and $0000FF00) shl 8) or
    ((Lo and $00FF0000) shr 8) or ((Lo and $FF000000) shr 24);
  Hi     := ((Hi and $000000FF) shl 24) or ((Hi and $0000FF00) shl 8) or
    ((Hi and $00FF0000) shr 8) or ((Hi and $FF000000) shr 24);
  Rec.Lo := Hi;
  Rec.Hi := Lo;
  Result := TUInt64(Rec);
end;

function Int64ToBigEndian(Value: int64): int64;
begin
  if FByteOrderIsBigEndian then
    Result := Value
  else
    Result := ReverseInt64(Value);
end;

function Int32ToBigEndian(Value: integer): integer;
begin
  if FByteOrderIsBigEndian then
    Result := Value
  else
    Result := integer((Value and $000000FF) shl 24) or integer(
      (Value and $0000FF00) shl 8) or integer((Value and $00FF0000) shr 8) or
      integer((Value and $FF000000) shr 24);
end;

function Int16ToBigEndian(Value: smallint): smallint;
begin
  if FByteOrderIsBigEndian then
    Result := Value
  else
    Result := smallint((Value and $00FF) shl 8) or smallint((Value and $FF00) shr 8);
end;

function Int64ToLittleEndian(Value: int64): int64;
begin
  if not FByteOrderIsBigEndian then
    Result := Value
  else
    Result := ReverseInt64(Value);
end;

function Int32ToLittleEndian(Value: integer): integer;
begin
  if not FByteOrderIsBigEndian then
    Result := Value
  else
    Result := integer((Value and $000000FF) shl 24) or integer(
      (Value and $0000FF00) shl 8) or integer((Value and $00FF0000) shr 8) or
      integer((Value and $FF000000) shr 24);
end;

function Int16ToLittleEndian(Value: smallint): smallint;
begin
  if not FByteOrderIsBigEndian then
    Result := Value
  else
    Result := smallint((Value and $00FF) shl 8) or smallint((Value and $FF00) shr 8);
end;

function UInt64ToBigEndian(Value: TUInt64): TUInt64;
begin
  if FByteOrderIsBigEndian then
    Result := Value
  else
    Result := ReverseUInt64(Value);
end;

function UInt32ToBigEndian(Value: cardinal): cardinal;
begin
  if FByteOrderIsBigEndian then
    Result := Value
  else
    Result := cardinal((Value and $000000FF) shl 24) or cardinal(
      (Value and $0000FF00) shl 8) or cardinal((Value and $00FF0000) shr 8) or
      cardinal((Value and $FF000000) shr 24);
end;

function UInt16ToBigEndian(Value: word): word;
begin
  if FByteOrderIsBigEndian then
    Result := Value
  else
    Result := word((Value and $00FF) shl 8) or word((Value and $FF00) shr 8);
end;

function UInt64ToLittleEndian(Value: TUInt64): TUInt64;
begin
  if not FByteOrderIsBigEndian then
    Result := Value
  else
    Result := ReverseUInt64(Value);
end;

function UInt32ToLittleEndian(Value: cardinal): cardinal;
begin
  if not FByteOrderIsBigEndian then
    Result := Value
  else
    Result := cardinal((Value and $000000FF) shl 24) or cardinal(
      (Value and $0000FF00) shl 8) or cardinal((Value and $00FF0000) shr 8) or
      cardinal((Value and $FF000000) shr 24);
end;

function UInt16ToLittleEndian(Value: word): word;
begin
  if not FByteOrderIsBigEndian then
    Result := Value
  else
    Result := word((Value and $00FF) shl 8) or word((Value and $FF00) shr 8);
end;

function Int64HostToNetwork(Value: int64): int64;
begin
  if not FByteOrderIsBigEndian then
    Result := ReverseInt64(Value)
  else
    Result := Value;
end;

function Int32HostToNetwork(Value: integer): integer;
begin
  if not FByteOrderIsBigEndian then
    Result := integer((Value and $000000FF) shl 24) or integer(
      (Value and $0000FF00) shl 8) or integer((Value and $00FF0000) shr 8) or
      integer((Value and $FF000000) shr 24)
  else
    Result := Value;
end;

function Int16HostToNetwork(Value: smallint): smallint;
begin
  if not FByteOrderIsBigEndian then
    Result := smallint((Value and $00FF) shl 8) or smallint((Value and $FF00) shr 8)
  else
    Result := Value;
end;

function Int64NetworkToHost(Value: int64): int64;
begin
  if not FByteOrderIsBigEndian then
    Result := ReverseInt64(Value)
  else
    Result := Value;
end;

function Int32NetworkToHost(Value: integer): integer;
begin
  if not FByteOrderIsBigEndian then
    Result := integer((Value and $000000FF) shl 24) or integer(
      (Value and $0000FF00) shl 8) or integer((Value and $00FF0000) shr 8) or
      integer((Value and $FF000000) shr 24)
  else
    Result := Value;
end;

function Int16NetworkToHost(Value: smallint): smallint;
begin
  if not FByteOrderIsBigEndian then
    Result := smallint((Value and $00FF) shl 8) or smallint((Value and $FF00) shr 8)
  else
    Result := Value;
end;

function UInt64HostToNetwork(Value: TUInt64): TUInt64;
begin
  if CurrentByteOrderIsBigEndian then
    Result := Value
  else
    Result := ReverseUInt64(Value);
end;

function UInt32HostToNetwork(Value: cardinal): cardinal;
begin
  if not FByteOrderIsBigEndian then
    Result := cardinal((Value and $000000FF) shl 24) or cardinal(
      (Value and $0000FF00) shl 8) or cardinal((Value and $00FF0000) shr 8) or
      cardinal((Value and $FF000000) shr 24)
  else
    Result := Value;
end;

function UInt16HostToNetwork(Value: word): word;
begin
  if not FByteOrderIsBigEndian then
    Result := ((Value and $00FF) shl 8) or ((Value and $FF00) shr 8)
  else
    Result := Value;
end;

function UInt64NetworkToHost(Value: TUInt64): TUInt64;
begin
  if CurrentByteOrderIsBigEndian then
    Result := Value
  else
    Result := ReverseUInt64(Value);
end;

function UInt32NetworkToHost(Value: cardinal): cardinal;
begin
  if not FByteOrderIsBigEndian then
    Result := cardinal((Value and $000000FF) shl 24) or cardinal(
      (Value and $0000FF00) shl 8) or cardinal((Value and $00FF0000) shr 8) or
      cardinal((Value and $FF000000) shr 24)
  else
    Result := Value;
end;

function UInt16NetworkToHost(Value: word): word;
begin
  if not FByteOrderIsBigEndian then
    Result := ((Value and $00FF) shl 8) or ((Value and $FF00) shr 8)
  else
    Result := Value;
end;

function ReverseBitsInInt8(V: byte): byte;
begin
  // 0 和 1 交换、2 和 3 交换、4 和 5 交换、6 和 7 交换
  V      := ((V and $AA) shr 1) or ((V and $55) shl 1);
  // 01 和 23 交换、45 和 67 交换
  V      := ((V and $CC) shr 2) or ((V and $33) shl 2);
  // 0123 和 4567 交换
  V      := (V shr 4) or (V shl 4);
  Result := V;
end;

function ReverseBitsInInt16(V: word): word;
begin
  Result := (ReverseBitsInInt8(V and $00FF) shl 8) or ReverseBitsInInt8(
    (V and $FF00) shr 8);
end;

function ReverseBitsInInt32(V: cardinal): cardinal;
begin
  Result := (ReverseBitsInInt16(V and $0000FFFF) shl 16) or
    ReverseBitsInInt16((V and $FFFF0000) shr 16);
end;

function ReverseBitsInInt64(V: int64): int64;
begin
  Result := (int64(ReverseBitsInInt32(V and $00000000FFFFFFFF)) shl 32) or
    ReverseBitsInInt32((V and $FFFFFFFF00000000) shr 32);
end;

procedure ReverseMemory(AMem: Pointer; MemByteLen: integer);
var
  i, L: integer;
  P: PByteArray;
  T: byte;
begin
  if (AMem = nil) or (MemByteLen < 2) then
    Exit;

  L := MemByteLen div 2;
  P := PByteArray(AMem);
  for i := 0 to L - 1 do
    begin
    // 交换第 I 和第 MemLen - I - 1
    T     := P^[i];
    P^[i] := P^[MemByteLen - i - 1];
    P^[MemByteLen - i - 1] := T;
    end;
end;

procedure ReverseMemoryWithBits(AMem: Pointer; MemByteLen: integer);
var
  i: integer;
  P: PByteArray;
begin
  if (AMem = nil) or (MemByteLen <= 0) then
    Exit;

  ReverseMemory(AMem, MemByteLen);
  P := PByteArray(AMem);

  for i := 0 to MemByteLen - 1 do
    P^[i] := ReverseBitsInInt8(P^[i]);
end;

procedure MemoryNetworkToHost(AMem: Pointer; MemByteLen: integer);
begin
  if not FByteOrderIsBigEndian then
    ReverseMemory(AMem, MemByteLen);
end;

procedure MemoryHostToNetwork(AMem: Pointer; MemByteLen: integer);
begin
  if not FByteOrderIsBigEndian then
    ReverseMemory(AMem, MemByteLen);
end;

// N 字节长度的内存块的位操作
procedure MemoryBitOperation(AMem, BMem, RMem: Pointer; N: integer; Op: TCnBitOperation);
var
  a, b, r: PCnLongWord32Array;
  BA, BB, BR: PByteArray;
begin
  if N <= 0 then
    Exit;

  if (AMem = nil) or ((BMem = nil) and (Op <> boNot)) or (RMem = nil) then
    Exit;

  a := PCnLongWord32Array(AMem);
  b := PCnLongWord32Array(BMem);
  r := PCnLongWord32Array(RMem);

  while (N and (not 3)) <> 0 do
    begin
    case Op of
      boAnd:
        r^[0] := a^[0] and b^[0];
      boOr:
        r^[0] := a^[0] or b^[0];
      boXor:
        r^[0] := a^[0] xor b^[0];
      boNot: // 求反时忽略 B
        r^[0] := not a^[0];
      end;

    a := PCnLongWord32Array(TCnNativeInt(a) + SizeOf(cardinal));
    b := PCnLongWord32Array(TCnNativeInt(b) + SizeOf(cardinal));
    r := PCnLongWord32Array(TCnNativeInt(r) + SizeOf(cardinal));

    Dec(N, SizeOf(cardinal));
    end;

  if N > 0 then
    begin
    BA := PByteArray(a);
    BB := PByteArray(b);
    BR := PByteArray(r);

    while N <> 0 do
      begin
      case Op of
        boAnd:
          BR^[0] := BA^[0] and BB^[0];
        boOr:
          BR^[0] := BA^[0] or BB^[0];
        boXor:
          BR^[0] := BA^[0] xor BB^[0];
        boNot:
          BR^[0] := not BA^[0];
        end;

      BA := PByteArray(TCnNativeInt(BA) + SizeOf(byte));
      BB := PByteArray(TCnNativeInt(BB) + SizeOf(byte));
      BR := PByteArray(TCnNativeInt(BR) + SizeOf(byte));
      Dec(N);
      end;
    end;
end;

procedure MemoryAnd(AMem, BMem: Pointer; MemByteLen: integer; ResMem: Pointer);
begin
  MemoryBitOperation(AMem, BMem, ResMem, MemByteLen, boAnd);
end;

procedure MemoryOr(AMem, BMem: Pointer; MemByteLen: integer; ResMem: Pointer);
begin
  MemoryBitOperation(AMem, BMem, ResMem, MemByteLen, boOr);
end;

procedure MemoryXor(AMem, BMem: Pointer; MemByteLen: integer; ResMem: Pointer);
begin
  MemoryBitOperation(AMem, BMem, ResMem, MemByteLen, boXor);
end;

procedure MemoryNot(AMem: Pointer; MemByteLen: integer; ResMem: Pointer);
begin
  MemoryBitOperation(AMem, nil, ResMem, MemByteLen, boNot);
end;

procedure MemoryShiftLeft(AMem, BMem: Pointer; MemByteLen: integer; BitCount: integer);
var
  i, L, N, LB, RB: integer;
  PF, PT: PByteArray;
begin
  if (AMem = nil) or (MemByteLen <= 0) or (BitCount = 0) then
    Exit;

  if BitCount < 0 then
    begin
    MemoryShiftRight(AMem, BMem, MemByteLen, -BitCount);
    Exit;
    end;

  if BMem = nil then
    BMem := AMem;

  if (MemByteLen * 8) <= BitCount then // 移太多不够,全 0
    begin
    FillChar(BMem^, MemByteLen, 0);
    Exit;
    end;

  N  := BitCount div 8;  // 移位超过的整字节数
  RB := BitCount mod 8; // 去除整字节后剩下的位数
  LB := 8 - RB;         // 上面剩下的位数在一字节内再剩下的位数

  PF := PByteArray(AMem);
  PT := PByteArray(BMem);

  if RB = 0 then // 整块,好办,要移位的字节数是 MemLen - NW
    begin
    Move(PF^[N], PT^[0], MemByteLen - N);
    FillChar(PT^[MemByteLen - N], N, 0);
    end
  else
    begin
    // 起点是 PF^[N] 和 PT^[0],长度 MemLen - N 个字节,但相邻字节间有交叉
    L  := MemByteLen - N;
    PF := PByteArray(TCnNativeInt(PF) + N);

    for i := 1 to L do // 从低位往低移动,先处理低的
      begin
      PT^[0] := byte(PF^[0] shl RB);
      if i < L then    // 最高一个字节 PF^[1] 会超界
        PT^[0] := (PF^[1] shr LB) or PT^[0];

      PF := PByteArray(TCnNativeInt(PF) + 1);
      PT := PByteArray(TCnNativeInt(PT) + 1);
      end;

    // 剩下的要填 0
    if N > 0 then
      FillChar(PT^[0], N, 0);
    end;
end;

procedure MemoryShiftRight(AMem, BMem: Pointer; MemByteLen: integer; BitCount: integer);
var
  i, L, N, LB, RB: integer;
  PF, PT: PByteArray;
begin
  if (AMem = nil) or (MemByteLen <= 0) or (BitCount = 0) then
    Exit;

  if BitCount < 0 then
    begin
    MemoryShiftLeft(AMem, BMem, MemByteLen, -BitCount);
    Exit;
    end;

  if BMem = nil then
    BMem := AMem;

  if (MemByteLen * 8) <= BitCount then // 移太多不够,全 0
    begin
    FillChar(BMem^, MemByteLen, 0);
    Exit;
    end;

  N  := BitCount div 8;  // 移位超过的整字节数
  RB := BitCount mod 8; // 去除整字节后剩下的位数
  LB := 8 - RB;         // 上面剩下的位数在一字节内再剩下的位数

  if RB = 0 then // 整块,好办,要移位的字节数是 MemLen - N
    begin
    PF := PByteArray(AMem);
    PT := PByteArray(BMem);

    Move(PF^[0], PT^[N], MemByteLen - N);
    FillChar(PT^[0], N, 0);
    end
  else
    begin
    // 起点是 PF^[0] 和 PT^[N],长度 MemLen - N 个字节,但得从高处开始,且相邻字节间有交叉
    L := MemByteLen - N;

    PF := PByteArray(TCnNativeInt(AMem) + L - 1);
    PT := PByteArray(TCnNativeInt(BMem) + MemByteLen - 1);

    for i := L downto 1 do // 从高位往高位移动,先处理后面的
      begin
      PT^[0] := byte(PF^[0] shr RB);
      if i > 1 then        // 最低一个字节 PF^[-1] 会超界
        begin
        PF     := PByteArray(TCnNativeInt(PF) - 1);
        PT^[0] := (PF^[0] shl LB) or PT^[0];
        end
      else
        PF := PByteArray(TCnNativeInt(PF) - 1);

      PT := PByteArray(TCnNativeInt(PT) - 1);
      end;

    // 剩下的最前面的要填 0
    if N > 0 then
      FillChar(BMem^, N, 0);
    end;
end;

function MemoryIsBitSet(AMem: Pointer; N: integer): boolean;
var
  P: pbyte;
  a, b: integer;
  V: byte;
begin
  if (AMem = nil) or (N < 0) then
    raise Exception.Create(SRangeError);

  a := N div 8;
  b := N mod 8;
  P := pbyte(TCnNativeInt(AMem) + a);

  V      := byte(1 shl b);
  Result := (P^ and V) <> 0;
end;

procedure MemorySetBit(AMem: Pointer; N: integer);
var
  P: pbyte;
  a, b: integer;
  V: byte;
begin
  if (AMem = nil) or (N < 0) then
    raise Exception.Create(SRangeError);

  a := N div 8;
  b := N mod 8;
  P := pbyte(TCnNativeInt(AMem) + a);

  V  := byte(1 shl b);
  P^ := P^ or V;
end;

procedure MemoryClearBit(AMem: Pointer; N: integer);
var
  P: pbyte;
  a, b: integer;
  V: byte;
begin
  if (AMem = nil) or (N < 0) then
    raise Exception.Create(SRangeError);

  a := N div 8;
  b := N mod 8;
  P := pbyte(TCnNativeInt(AMem) + a);

  V  := not byte(1 shl b);
  P^ := P^ and V;
end;

function MemoryToBinStr(AMem: Pointer; MemByteLen: integer; Sep: boolean): string;
var
  j, L: integer;
  P: PByteArray;
  b: PChar;

procedure FillAByteToBuf(V: byte; Buf: PChar);
  const
    M = $80;
  var
    i: integer;
  begin
    for i := 0 to 7 do
      begin
      if (V and M) <> 0 then
        Buf[i] := '1'
      else
        Buf[i] := '0';
      V := V shl 1;
      end;
  end;

begin
  Result := '';
  if (AMem = nil) or (MemByteLen <= 0) then
    Exit;

  L := MemByteLen * 8;
  if Sep then
    L := L + MemByteLen - 1; // 中间用空格分隔

  setlength(Result, L);
  b := PChar(@Result[1]);
  P := PByteArray(AMem);

  for j := 0 to MemByteLen - 1 do
    begin
    FillAByteToBuf(P^[j], b);
    if Sep then
      begin
      b[8] := ' ';
      Inc(b, 9);
      end
    else
      Inc(b, 8);
    end;
end;

procedure MemorySwap(AMem, BMem: Pointer; MemByteLen: integer);
var
  a, b: PCnLongWord32Array;
  BA, BB: PByteArray;
  TC: cardinal;
  TB: byte;
begin
  if (AMem = nil) or (BMem = nil) or (MemByteLen <= 0) then
    Exit;

  a := PCnLongWord32Array(AMem);
  b := PCnLongWord32Array(BMem);

  if a = b then
    Exit;

  while (MemByteLen and (not 3)) <> 0 do
    begin
    TC    := a^[0];
    a^[0] := b^[0];
    b^[0] := TC;

    a := PCnLongWord32Array(TCnNativeInt(a) + SizeOf(cardinal));
    b := PCnLongWord32Array(TCnNativeInt(b) + SizeOf(cardinal));

    Dec(MemByteLen, SizeOf(cardinal));
    end;

  if MemByteLen > 0 then
    begin
    BA := PByteArray(a);
    BB := PByteArray(b);

    while MemByteLen <> 0 do
      begin
      TB     := BA^[0];
      BA^[0] := BB^[0];
      BB^[0] := TB;

      BA := PByteArray(TCnNativeInt(BA) + SizeOf(byte));
      BB := PByteArray(TCnNativeInt(BB) + SizeOf(byte));

      Dec(MemByteLen);
      end;
    end;
end;

function MemoryCompare(AMem, BMem: Pointer; MemByteLen: integer): integer;
var
  a, b: PCnLongWord32Array;
  BA, BB: PByteArray;
begin
  Result := 0;
  if ((AMem = nil) and (BMem = nil)) or (AMem = BMem) then // 同一块
    Exit;

  if MemByteLen <= 0 then
    Exit;

  if AMem = nil then
    begin
    Result := -1;
    Exit;
    end;
  if BMem = nil then
    begin
    Result := 1;
    Exit;
    end;

  a := PCnLongWord32Array(AMem);
  b := PCnLongWord32Array(BMem);

  while (MemByteLen and (not 3)) <> 0 do
    begin
    if a^[0] > b^[0] then
      begin
      Result := 1;
      Exit;
      end
    else if a^[0] < b^[0] then
        begin
        Result := -1;
        Exit;
        end;

    a := PCnLongWord32Array(TCnNativeInt(a) + SizeOf(cardinal));
    b := PCnLongWord32Array(TCnNativeInt(b) + SizeOf(cardinal));

    Dec(MemByteLen, SizeOf(cardinal));
    end;

  if MemByteLen > 0 then
    begin
    BA := PByteArray(a);
    BB := PByteArray(b);

    while MemByteLen <> 0 do
      begin
      if BA^[0] > BB^[0] then
        begin
        Result := 1;
        Exit;
        end
      else if BA^[0] < BB^[0] then
          begin
          Result := -1;
          Exit;
          end;

      BA := PByteArray(TCnNativeInt(BA) + SizeOf(byte));
      BB := PByteArray(TCnNativeInt(BB) + SizeOf(byte));

      Dec(MemByteLen);
      end;
    end;
end;

function UInt8ToBinStr(V: byte): string;
const
  M = $80;
var
  i: integer;
begin
  setlength(Result, 8 * SizeOf(V));
  for i := 1 to 8 * SizeOf(V) do
    begin
    if (V and M) <> 0 then
      Result[i] := '1'
    else
      Result[i] := '0';
    V := V shl 1;
    end;
end;

function UInt16ToBinStr(V: word): string;
const
  M = $8000;
var
  i: integer;
begin
  setlength(Result, 8 * SizeOf(V));
  for i := 1 to 8 * SizeOf(V) do
    begin
    if (V and M) <> 0 then
      Result[i] := '1'
    else
      Result[i] := '0';
    V := V shl 1;
    end;
end;

function UInt32ToBinStr(V: cardinal): string;
const
  M = $80000000;
var
  i: integer;
begin
  setlength(Result, 8 * SizeOf(V));
  for i := 1 to 8 * SizeOf(V) do
    begin
    if (V and M) <> 0 then
      Result[i] := '1'
    else
      Result[i] := '0';
    V := V shl 1;
    end;
end;

function UInt32ToStr(V: cardinal): string;
begin
  Result := format('%u', [V]);
end;

function UInt64ToBinStr(V: TUInt64): string;
const
  M = $8000000000000000;
var
  i: integer;
begin
  setlength(Result, 8 * SizeOf(V));

  for i := 1 to 8 * SizeOf(V) do
    begin
    if (V and M) <> 0 then
      Result[i] := '1'
    else
      Result[i] := '0';
    V := V shl 1;
    end;
end;

const
HiDigits: array[0..15] of char = ('0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');

const
LoDigits: array[0..15] of char = ('0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');

const
AnsiHiDigits: array[0..15] of ansichar = ('0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');

const
AnsiLoDigits: array[0..15] of ansichar = ('0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');

function HexToInt(Hex: PChar; CharLen: integer): integer;
var
  i, Res: integer;
  c: char;
begin
  Res := 0;
  for i := 0 to CharLen - 1 do
    begin
    c := Hex[i];
    if (c >= '0') and (c <= '9') then
      Res := Res * 16 + Ord(c) - Ord('0')
    else if (c >= 'A') and (c <= 'F') then
        Res := Res * 16 + Ord(c) - Ord('A') + 10
      else if (c >= 'a') and (c <= 'f') then
          Res := Res * 16 + Ord(c) - Ord('a') + 10
        else
          raise Exception.Createfmt('Error: not a Hex PChar: %c', [c]);
    end;
  Result := Res;
end;

function HexToInt(const Hex: string): integer;
begin
  Result := HexToInt(PChar(Hex), Length(Hex));
end;

{$WARNINGS OFF}

function IsHexString(const Hex: string): boolean;
var
  i, L: integer;
begin
  Result := False;
  L      := Length(Hex);
  if (L <= 0) or ((L and 1) <> 0) then // 空或非偶长度都不是
    Exit;

  for i := 1 to L do
    begin
    // 注意此处 Unicode 下虽然有 Warning,但并不是将 Hex[I] 这个 WideChar 直接截断至 AnsiChar
    // 后再进行判断(那样会导致“晦晦”这种 $66$66$66$66 的字符串出现误判),而是
    // 直接通过 WideChar 的值(在 ax 中因而是双字节的)加减来判断,不会出现误判
    if not (Hex[i] in ['0'..'9', 'A'..'F', 'a'..'f']) then
      Exit;
    end;
  Result := True;
end;

{$WARNINGS ON}

function DataToHex(InData: Pointer; ByteLength: integer;
UseUpperCase: boolean = True): string;
var
  i: integer;
  b: byte;
begin
  Result := '';
  if ByteLength <= 0 then
    Exit;

  setlength(Result, ByteLength * 2);
  if UseUpperCase then
    begin
    for i := 0 to ByteLength - 1 do
      begin
      b := pbyte(TCnNativeInt(InData) + i * SizeOf(byte))^;
      Result[i * 2 + 1] := HiDigits[(b shr 4) and $0F];
      Result[i * 2 + 2] := HiDigits[b and $0F];
      end;
    end
  else
    begin
    for i := 0 to ByteLength - 1 do
      begin
      b := pbyte(TCnNativeInt(InData) + i * SizeOf(byte))^;
      Result[i * 2 + 1] := LoDigits[(b shr 4) and $0F];
      Result[i * 2 + 2] := LoDigits[b and $0F];
      end;
    end;
end;

function HexToData(const Hex: string; OutData: Pointer): integer;
var
  i, L: integer;
  H: PChar;
begin
  L := Length(Hex);
  if (L mod 2) <> 0 then
    raise Exception.Createfmt('Error Length %d: not a Hex String', [L]);

  if OutData = nil then
    begin
    Result := L div 2;
    Exit;
    end;

  Result := 0;
  H      := PChar(Hex);
  for i := 1 to L div 2 do
    begin
    pbyte(TCnNativeInt(OutData) + i - 1)^ := byte(HexToInt(@H[(i - 1) * 2], 2));
    Inc(Result);
    end;
end;

function StringToHex(const Data: string; UseUpperCase: boolean): string;
var
  i, L: integer;
  b: byte;
  Buffer: PChar;
begin
  Result := '';
  L      := Length(Data);
  if L = 0 then
    Exit;

  setlength(Result, L * 2);
  Buffer := @Data[1];

  if UseUpperCase then
    begin
    for i := 0 to L - 1 do
      begin
      b := pbyte(TCnNativeInt(Buffer) + i * SizeOf(char))^;
      Result[i * 2 + 1] := HiDigits[(b shr 4) and $0F];
      Result[i * 2 + 2] := HiDigits[b and $0F];
      end;
    end
  else
    begin
    for i := 0 to L - 1 do
      begin
      b := pbyte(TCnNativeInt(Buffer) + i * SizeOf(char))^;
      Result[i * 2 + 1] := LoDigits[(b shr 4) and $0F];
      Result[i * 2 + 2] := LoDigits[b and $0F];
      end;
    end;
end;

function HexToString(const Hex: string): string;
var
  i, L: integer;
  H: PChar;
begin
  L := Length(Hex);
  if (L mod 2) <> 0 then
    raise Exception.Createfmt('Error Length %d: not a Hex String', [L]);

  setlength(Result, L div 2);
  H := PChar(Hex);
  for i := 1 to L div 2 do
    Result[i] := Chr(HexToInt(@H[(i - 1) * 2], 2));
end;

function HexToAnsiStr(const Hex: ansistring): ansistring;
var
  i, L: integer;
  S: string;
begin
  L := Length(Hex);
  if (L mod 2) <> 0 then
    raise Exception.Createfmt('Error Length %d: not a Hex AnsiString', [L]);

  setlength(Result, L div 2);
  for i := 1 to L div 2 do
    begin
    S := string(Copy(Hex, i * 2 - 1, 2));
    Result[i] := ansichar(Chr(HexToInt(S)));
    end;
end;

function AnsiStrToHex(const Data: ansistring; UseUpperCase: boolean): ansistring;
var
  i, L: integer;
  b: byte;
  Buffer: pansichar;
begin
  Result := '';
  L      := Length(Data);
  if L = 0 then
    Exit;

  setlength(Result, L * 2);
  Buffer := @Data[1];

  if UseUpperCase then
    begin
    for i := 0 to L - 1 do
      begin
      b := pbyte(TCnNativeInt(Buffer) + i)^;
      Result[i * 2 + 1] := AnsiHiDigits[(b shr 4) and $0F];
      Result[i * 2 + 2] := AnsiHiDigits[b and $0F];
      end;
    end
  else
    begin
    for i := 0 to L - 1 do
      begin
      b := pbyte(TCnNativeInt(Buffer) + i)^;
      Result[i * 2 + 1] := AnsiLoDigits[(b shr 4) and $0F];
      Result[i * 2 + 2] := AnsiLoDigits[b and $0F];
      end;
    end;
end;

function BytesToHex(Data: TBytes; UseUpperCase: boolean): string;
var
  i, L: integer;
  b: byte;
  Buffer: pansichar;
begin
  Result := '';
  L      := Length(Data);
  if L = 0 then
    Exit;

  setlength(Result, L * 2);
  Buffer := @Data[0];

  if UseUpperCase then
    begin
    for i := 0 to L - 1 do
      begin
      b := pbyte(TCnNativeInt(Buffer) + i)^;
      Result[i * 2 + 1] := HiDigits[(b shr 4) and $0F];
      Result[i * 2 + 2] := HiDigits[b and $0F];
      end;
    end
  else
    begin
    for i := 0 to L - 1 do
      begin
      b := pbyte(TCnNativeInt(Buffer) + i)^;
      Result[i * 2 + 1] := LoDigits[(b shr 4) and $0F];
      Result[i * 2 + 2] := LoDigits[b and $0F];
      end;
    end;
end;

function HexToBytes(const Hex: string): TBytes;
var
  i, L: integer;
  H: PChar;
begin
  L := Length(Hex);
  if (L mod 2) <> 0 then
    raise Exception.Createfmt('Error Length %d: not a Hex String', [L]);

  setlength(Result, L div 2);
  H := PChar(Hex);

  for i := 1 to L div 2 do
    Result[i - 1] := byte(HexToInt(@H[(i - 1) * 2], 2));
end;

function StreamToHex(Stream: TStream; UseUpperCase: boolean): string;
var
  b: byte;
  i: integer;
begin
  Result := '';
  if Stream.size > 0 then
    begin
    Stream.Position := 0;
    setlength(Result, Stream.size * 2);
    i := 1;
    if UseUpperCase then
      begin
      while Stream.Read(b, 1) = 1 do
        begin
        Result[i] := HiDigits[(b shr 4) and $0F];
        Inc(i);
        Result[i] := HiDigits[b and $0F];
        Inc(i);
        end;
      end
    else
      begin
      while Stream.Read(b, 1) = 1 do
        begin
        Result[i] := LoDigits[(b shr 4) and $0F];
        Inc(i);
        Result[i] := LoDigits[b and $0F];
        Inc(i);
        end;
      end;
    end;
end;

function HexToStream(const Hex: string; Stream: TStream): integer;
var
  i, L: integer;
  H: PChar;
  b: byte;
begin
  Result := 0;
  L      := Length(Hex);
  if (L mod 2) <> 0 then
    raise Exception.Createfmt('Error Length %d: not a Hex String', [L]);

  H := PChar(Hex);
  for i := 1 to L div 2 do
    begin
    b := byte(HexToInt(@H[(i - 1) * 2], 2));
    Inc(Result, Stream.Write(b, 1));
    end;
end;

procedure ReverseBytes(Data: TBytes);
var
  i, L, M: integer;
  T: byte;
begin
  if (Data = nil) or (Length(Data) <= 1) then
    Exit;
  L := Length(Data);
  M := L div 2;
  for i := 0 to M - 1 do
    begin
    // 交换 I 和 L - I - 1
    T := Data[i];
    Data[i] := Data[L - i - 1];
    Data[L - i - 1] := T;
    end;
end;

function StreamToBytes(Stream: TStream): TBytes;
begin
  Result := nil;
  if (Stream <> nil) and (Stream.size > 0) then
    begin
    setlength(Result, Stream.size);
    Stream.Position := 0;
    Stream.Read(Result[0], Stream.size);
    end;
end;

function BytesToStream(Data: TBytes; OutStream: TStream): integer;
begin
  Result := 0;
  if (Data <> nil) and (Length(Data) > 0) and (OutStream <> nil) then
    begin
    OutStream.size := 0;
    Result := OutStream.Write(Data[0], Length(Data));
    end;
end;

function AnsiToBytes(const str: ansistring): TBytes;
begin
  setlength(Result, Length(str));
  if Length(str) > 0 then
    Move(str[1], Result[0], Length(str));
end;

function BytesToAnsi(const Data: TBytes): ansistring;
begin
  setlength(Result, Length(Data));
  if Length(Data) > 0 then
    Move(Data[0], Result[1], Length(Data));
end;

function BytesToString(const Data: TBytes): string;
var
  i: integer;
begin
  setlength(Result, Length(Data));
  for i := 1 to Length(Data) do
    Result[i] := Chr(Data[i - 1]);
end;

function MemoryToString(Mem: Pointer; MemByteLen: integer): string;
var
  P: PByteArray;
  i: integer;
begin
  if (Mem = nil) or (MemByteLen <= 0) then
    begin
    Result := '';
    Exit;
    end;

  P := PByteArray(Mem);
  setlength(Result, MemByteLen);
  for i := 1 to MemByteLen do
    Result[i] := Chr(P^[i - 1]);
end;

function ConcatBytes(a, b: TBytes): TBytes;
begin
  // 哪怕是 XE7 后也不能直接相加,因为 A 或 B 为空时会返回另一字节数组而不是新数组
  if (a = nil) or (Length(a) = 0) then
    begin
    setlength(Result, Length(b));
    if Length(b) > 0 then
      Move(b[0], Result[0], Length(b));
    end
  else if (b = nil) or (Length(b) = 0) then
      begin
      setlength(Result, Length(a));
      if Length(a) > 0 then
        Move(a[0], Result[0], Length(a));
      end
    else
      begin
      setlength(Result, Length(a) + Length(b));
      Move(a[0], Result[0], Length(a));
      Move(b[0], Result[Length(a)], Length(b));
      end;
end;

function NewBytesFromMemory(Data: Pointer; DataByteLen: integer): TBytes;
begin
  if (Data = nil) or (DataByteLen <= 0) then
    Result := nil
  else
    begin
    setlength(Result, DataByteLen);
    Move(Data^, Result[0], DataByteLen);
    end;
end;

function CompareBytes(a, b: TBytes): boolean;
var
  L: integer;
begin
  Result := False;

  L := Length(a);
  if Length(b) <> L then // 长度不等则退出
    Exit;

  if L = 0 then          // 长度相等
    Result := True       // 如都是 0 视作相等
  else
    Result := CompareMem(@a[0], @b[0], L);
end;

procedure MoveMost(const Source; var Dest; ByteLen, MostLen: integer);
begin
  if MostLen <= 0 then
    Exit;

  if ByteLen > MostLen then
    ByteLen := MostLen
  else if ByteLen < MostLen then
      // TODO: 可优化为只填充不满的部分但后面有空再整
      FillChar(Dest, MostLen, 0);

  Move(Source, Dest, ByteLen);
end;

procedure ConstTimeConditionalSwap8(CanSwap: boolean; var a, b: byte);
var
  T, V: byte;
begin
  T := ConstTimeExpandBoolean8(CanSwap);
  V := (a xor b) and T;
  a := a xor V;
  b := b xor V;
end;

procedure ConstTimeConditionalSwap16(CanSwap: boolean; var a, b: word);
var
  T, V: word;
begin
  T := ConstTimeExpandBoolean16(CanSwap);
  V := (a xor b) and T;
  a := a xor V;
  b := b xor V;
end;

procedure ConstTimeConditionalSwap32(CanSwap: boolean; var a, b: cardinal);
var
  T, V: cardinal;
begin
  T := ConstTimeExpandBoolean32(CanSwap);
  V := (a xor b) and T;
  a := a xor V;
  b := b xor V;
end;

procedure ConstTimeConditionalSwap64(CanSwap: boolean; var a, b: TUInt64);
var
  T, V: TUInt64;
begin
  T := ConstTimeExpandBoolean64(CanSwap);
  V := (a xor b) and T;
  a := a xor V;
  b := b xor V;
end;

function ConstTimeEqual8(a, b: byte): boolean;
var
  r: byte;
begin
  r      := not (a xor b);     // 异或后求反
  r      := r and (r shr 4);   // 以下一半一半地与
  r      := r and (r shr 2);   // 如果有一位出现 0
  r      := r and (r shr 1);   // 最后结果就是 0
  Result := boolean(r);   // 只有全 1 才是 1
end;

function ConstTimeEqual16(a, b: word): boolean;
begin
  Result := ConstTimeEqual8(byte(a shr 8), byte(b shr 8)) and
    ConstTimeEqual8(byte(a and $FF), byte(b and $FF));
end;

function ConstTimeEqual32(a, b: cardinal): boolean;
begin
  Result := ConstTimeEqual16(word(a shr 16), word(b shr 16)) and
    ConstTimeEqual16(word(a and $FFFF), word(b and $FFFF));
end;

function ConstTimeEqual64(a, b: TUInt64): boolean;
begin
  Result := ConstTimeEqual32(cardinal(a shr 32), cardinal(b shr 32)) and
    ConstTimeEqual32(cardinal(a and $FFFFFFFF), cardinal(b and $FFFFFFFF));
end;

function ConstTimeBytesEqual(a, b: TBytes): boolean;
var
  i: integer;
begin
  Result := False;
  if Length(a) <> Length(b) then
    Exit;

  Result := True;
  for i := 0 to Length(a) - 1 do
    // 每个字节都比较,而不是碰到不同就退出
    Result := Result and (ConstTimeEqual8(a[i], b[i]));
end;

function ConstTimeExpandBoolean8(V: boolean): byte;
begin
  Result := byte(V);
  Result := not Result;
  // 如果 V 是 True,非 0,则此步 R 非纯 $FF,R 里头有 0
  Result := Result and (Result shr 4);   // 以下一半一半地与
  Result := Result and (Result shr 2);   // 如果有一位出现 0
  Result := Result and (Result shr 1);   // 最后结果就是 00000000,否则 00000001
  Result := Result or (Result shl 1);
  // True 得到 00000000,False 得到 00000001,再往高位两倍两倍地扩
  Result := Result or (Result shl 2);
  Result := Result or (Result shl 4);    // 最终全 0 或 全 1
  Result := not Result;                  // 反成全 1 或全 0
end;

function ConstTimeExpandBoolean16(V: boolean): word;
var
  r: byte;
begin
  r      := ConstTimeExpandBoolean8(V);
  Result := r;
  Result := (Result shl 8) or r;         // 单字节全 1 或全 0 扩成双字节
end;

function ConstTimeExpandBoolean32(V: boolean): cardinal;
var
  r: word;
begin
  r      := ConstTimeExpandBoolean16(V);
  Result := r;
  Result := (Result shl 16) or r;        // 双字节全 1 或全 0 扩成四字节
end;

function ConstTimeExpandBoolean64(V: boolean): TUInt64;
var
  r: cardinal;
begin
  r      := ConstTimeExpandBoolean32(V);
  Result := r;
  Result := (Result shl 32) or r;        // 四字节全 1 或全 0 扩成八字节
end;

function ConstTimeConditionalSelect8(Condition: boolean; a, b: byte): byte;
begin
  ConstTimeConditionalSwap8(Condition, a, b);
  Result := b;
end;

function ConstTimeConditionalSelect16(Condition: boolean; a, b: word): word;
begin
  ConstTimeConditionalSwap16(Condition, a, b);
  Result := b;
end;

function ConstTimeConditionalSelect32(Condition: boolean; a, b: cardinal): cardinal;
begin
  ConstTimeConditionalSwap32(Condition, a, b);
  Result := b;
end;

function ConstTimeConditionalSelect64(Condition: boolean; a, b: TUInt64): TUInt64;
begin
  ConstTimeConditionalSwap64(Condition, a, b);
  Result := b;
end;

{$IFDEF MSWINDOWS}

{$IFDEF CPUX64}

// 64 位汇编用 IDIV 和 IDIV 指令实现,其中 A 在 RCX 里,B 在 EDX/RDX 里,DivRes 地址在 R8 里,ModRes 地址在 R9 里
procedure Int64DivInt32Mod(A: Int64; B: Integer; var DivRes, ModRes: Integer); assembler;
asm
        PUSH    RCX                           // RCX 是 A
        MOV     RCX, RDX                      // 除数 B 放入 RCX
        POP     RAX                           // 被除数 A 放入 RAX
        XOR     RDX, RDX                      // 被除数高 64 位清零
        IDIV    RCX
        MOV     [R8], EAX                     // 商放入 R8 所指的 DivRes
        MOV     [R9], EDX                     // 余数放入 R9 所指的 ModRes
end;

procedure UInt64DivUInt32Mod(A: TUInt64; B: Cardinal; var DivRes, ModRes: Cardinal); assembler;
asm
        PUSH    RCX                           // RCX 是 A
        MOV     RCX, RDX                      // 除数 B 放入 RCX
        POP     RAX                           // 被除数 A 放入 RAX
        XOR     RDX, RDX                      // 被除数高 64 位清零
        DIV     RCX
        MOV     [R8], EAX                     // 商放入 R8 所指的 DivRes
        MOV     [R9], EDX                     // 余数放入 R9 所指的 ModRes
end;

// 64 位汇编用 IDIV 和 IDIV 指令实现,ALo 在 RCX,AHi 在 RDX,B 在 R8,DivRes 的地址在 R9,
procedure Int128DivInt64Mod(ALo, AHi: Int64; B: Int64; var DivRes, ModRes: Int64); assembler;
asm
        MOV     RAX, RCX                      // ALo 放入 RAX,AHi 已经在 RDX 了
        MOV     RCX, R8                       // B 放入 RCX
        IDIV    RCX
        MOV     [R9], RAX                     // 商放入 R9 所指的 DivRes
        MOV     RAX, [RBP + $30]              // ModRes 地址放入 RAX
        MOV     [RAX], RDX                    // 余数放入 RAX 所指的 ModRes
end;

procedure UInt128DivUInt64Mod(ALo, AHi: UInt64; B: UInt64; var DivRes, ModRes: UInt64); assembler;
asm
        MOV     RAX, RCX                      // ALo 放入 RAX,AHi 已经在 RDX 了
        MOV     RCX, R8                       // B 放入 RCX
        DIV     RCX
        MOV     [R9], RAX                     // 商放入 R9 所指的 DivRes
        MOV     RAX, [RBP + $30]              // ModRes 地址放入 RAX
        MOV     [RAX], RDX                    // 余数放入 RAX 所指的 ModRes
end;

{$ELSE}

// 32 位汇编用 IDIV 和 IDIV 指令实现,其中 A 在堆栈上,B 在 EAX,DivRes 地址在 EDX,ModRes 地址在 ECX
procedure Int64DivInt32Mod(a: int64; b: integer; var DivRes, ModRes: integer); {$asmmode intel} assembler;
asm
         PUSH    ECX                           // ECX 是 ModRes 地址,先保存
         MOV     ECX, B                        // B 在 EAX 中,搬移到 ECX 中
         PUSH    EDX                           // DivRes 的地址在 EDX 中,也保存
         MOV     EAX, [EBP + $8]               // A Lo
         MOV     EDX, [EBP + $C]               // A Hi
         IDIV    ECX
         POP     ECX                           // 弹出 ECX,拿到 DivRes 地址
         MOV     [ECX], EAX
         POP     ECX                           // 弹出 ECX,拿到 ModRes 地址
         MOV     [ECX], EDX
end;

procedure UInt64DivUInt32Mod(a: TUInt64; b: cardinal; var DivRes, ModRes: cardinal);
{$asmmode intel} assembler;
asm
         PUSH    ECX                           // ECX 是 ModRes 地址,先保存
         MOV     ECX, B                        // B 在 EAX 中,搬移到 ECX 中
         PUSH    EDX                           // DivRes 的地址在 EDX 中,也保存
         MOV     EAX, [EBP + $8]               // A Lo
         MOV     EDX, [EBP + $C]               // A Hi
         DIV     ECX
         POP     ECX                           // 弹出 ECX,拿到 DivRes 地址
         MOV     [ECX], EAX
         POP     ECX                           // 弹出 ECX,拿到 ModRes 地址
         MOV     [ECX], EDX
end;

// 32 位下的实现
procedure Int128DivInt64Mod(ALo, AHi: int64; b: int64; var DivRes, ModRes: int64);
var
  c: integer;
begin
  if b = 0 then
    raise EDivByZero.Create(SDivByZero);

  if (AHi = 0) or (AHi = $FFFFFFFFFFFFFFFF) then // 高 64 位为 0 的正值或负值
    begin
    DivRes := ALo div b;
    ModRes := ALo mod b;
    end
  else
    begin
    if b < 0 then // 除数是负数
      begin
      Int128DivInt64Mod(ALo, AHi, -b, DivRes, ModRes);
      DivRes := -DivRes;
      Exit;
      end;

    if AHi < 0 then // 被除数是负数
      begin
      // AHi, ALo 求反加 1,以得到正值
      AHi := not AHi;
      ALo := not ALo;
{$IFDEF SUPPORT_UINT64}
      UInt64Add(UInt64(ALo), UInt64(ALo), 1, C);
{$ELSE}
      UInt64Add(ALo, ALo, 1, c);
{$ENDIF}
      if c > 0 then
        AHi := AHi + c;

      // 被除数转正了
      Int128DivInt64Mod(ALo, AHi, b, DivRes, ModRes);

      // 结果再调整
      if ModRes = 0 then
        DivRes := -DivRes
      else
        begin
        DivRes := -DivRes - 1;
        ModRes := b - ModRes;
        end;
      Exit;
      end;

    // 全正后,按无符号来除
{$IFDEF SUPPORT_UINT64}
    UInt128DivUInt64Mod(TUInt64(ALo), TUInt64(AHi), TUInt64(B), TUInt64(DivRes), TUInt64(ModRes));
{$ELSE}
    UInt128DivUInt64Mod(ALo, AHi, b, DivRes, ModRes);
{$ENDIF}
    end;
end;

procedure UInt128DivUInt64Mod(ALo, AHi: TUInt64; b: TUInt64;
var DivRes, ModRes: TUInt64);
var
  i, Cnt: integer;
  Q, r: TUInt64;
begin
  if b = 0 then
    raise EDivByZero.Create(SDivByZero);

  if AHi = 0 then
    begin
    DivRes := UInt64Div(ALo, b);
    ModRes := UInt64Mod(ALo, b);
    end
  else
    begin
    // 有高位有低位咋办?先判断是否会溢出,如果 AHi >= B,则表示商要超 64 位,溢出
    if UInt64Compare(AHi, b) >= 0 then
      raise Exception.Create(SIntOverflow);

    Q   := 0;
    r   := 0;
    Cnt := GetUInt64LowBits(AHi) + 64;
    for i := Cnt downto 0 do
      begin
      r := r shl 1;
      if IsUInt128BitSet(ALo, AHi, i) then  // 被除数的第 I 位是否是 0
        r := r or 1
      else
        r := r and TUInt64(not 1);

      if UInt64Compare(r, b) >= 0 then
        begin
        r := r - b;
        Q := Q or (TUInt64(1) shl i);
        end;
      end;
    DivRes := Q;
    ModRes := r;
    end;
end;

{$ENDIF}

{$ENDIF}

{$IFDEF SUPPORT_UINT64}

// 只要支持 64 位无符号整数,无论 32/64 位 Intel 还是 ARM,无论 Delphi 还是 FPC,无论什么操作系统都能如此

function UInt64Mod(A, B: TUInt64): TUInt64;
begin
  Result := A mod B;
end;

function UInt64Div(A, B: TUInt64): TUInt64;
begin
  Result := A div B;
end;

{$ELSE}
{
  不支持 UInt64 的低版本 Delphi 下用 Int64 求 A mod/div B

  调用的入栈顺序是 A 的高位,A 的低位,B 的高位,B 的低位。挨个 push 完毕并进入函数后,
  ESP 是返回地址,ESP+4 是 B 的低位,ESP + 8 是 B 的高位,ESP + C 是 A 的低位,ESP + 10 是 A 的高位
  进入后 push esp 让 ESP 减了 4,然后 mov ebp esp,之后用 EBP 来寻址,全要多加 4

  而 System.@_llumod 要求在刚进入时,EAX <- A 的低位,EDX <- A 的高位,(System 源码注释中 EAX/EDX 写反了)
  [ESP + 8](也就是 EBP + C)<- B 的高位,[ESP + 4] (也就是 EBP + 8)<- B 的低位

  所以 CALL 前加了四句搬移代码。UInt64 Div 的也类似
}
function UInt64Mod(a, b: TUInt64): TUInt64;
begin
  {$asmmode intel}
asm
         // PUSH ESP 让 ESP 减了 4,要补上
         MOV     EAX, [EBP + $10]              // A Lo
         MOV     EDX, [EBP + $14]              // A Hi
         PUSH    DWORD PTR[EBP + $C]           // B Hi
         PUSH    DWORD PTR[EBP + $8]           // B Lo
         CALL    System.@_llumod;
end;
end;
function UInt64Div(a, b: TUInt64): TUInt64;
asm
         // PUSH ESP 让 ESP 减了 4,要补上
         MOV     EAX, [EBP + $10]              // A Lo
         MOV     EDX, [EBP + $14]              // A Hi
         PUSH    DWORD PTR[EBP + $C]           // B Hi
         PUSH    DWORD PTR[EBP + $8]           // B Lo
         CALL    System.@_lludiv;
end;

{$ENDIF}

{$IFDEF SUPPORT_UINT64}

// 只要支持 64 位无符号整数,无论 32/64 位 Intel 还是 ARM,无论 Delphi 还是 FPC,无论什么操作系统都能如此

function UInt64Mul(A, B: Cardinal): TUInt64;
begin
  Result := TUInt64(A) * B;
end;

{$ELSE}// 只有低版本 Delphi 会进这里,Win32 x86

{
  无符号 32 位整数相乘,如果结果直接使用 Int64 会溢出,模拟 64 位无符号运算

  调用寄存器约定是 A -> EAX,B -> EDX,不使用堆栈
  而 System.@_llmul 要求在刚进入时,EAX <- A 的低位,EDX <- A 的高位 0,
  [ESP + 8](也就是 EBP + C)<- B 的高位 0,[ESP + 4] (也就是 EBP + 8)<- B 的低位
}
function UInt64Mul(a, b: cardinal): TUInt64;
asm
         PUSH    0               // PUSH B 高位 0
         PUSH    EDX             // PUSH B 低位
         // EAX A 低位,已经是了
         XOR     EDX, EDX        // EDX A 高位 0
         CALL    System.@_llmul; // 返回 EAX 低 32 位、EDX 高 32 位
end;

{$ENDIF}

// 两个无符号 64 位整数相加,处理溢出的情况,结果放 ResLo 与 ResHi 中
procedure UInt64AddUInt64(a, b: TUInt64; var ResLo, ResHi: TUInt64);
var
  x, y, Z, T, R0L, R0H, R1L, R1H: cardinal;
  R0, R1, R01, R12: TUInt64;
begin
  // 基本思想:2^32 是系数 M,拆成 (xM+y) + (zM+t) = (x+z) M + (y+t)
  // y+t 是 R0 占 0、1,x+z 是 R1 占 1、2,把 R0, R1 再拆开相加成 R01, R12
  if IsUInt64AddOverflow(a, b) then
    begin
    x := Int64Rec(a).Hi;
    y := Int64Rec(a).Lo;
    Z := Int64Rec(b).Hi;
    T := Int64Rec(b).Lo;

    R0 := TUInt64(y) + TUInt64(T);
    R1 := TUInt64(x) + TUInt64(Z);

    R0L := Int64Rec(R0).Lo;
    R0H := Int64Rec(R0).Hi;
    R1L := Int64Rec(R1).Lo;
    R1H := Int64Rec(R1).Hi;

    R01 := TUInt64(R0H) + TUInt64(R1L);
    R12 := TUInt64(R1H) + TUInt64(Int64Rec(R01).Hi);

    Int64Rec(ResLo).Lo := R0L;
    Int64Rec(ResLo).Hi := Int64Rec(R01).Lo;
    Int64Rec(ResHi).Lo := Int64Rec(R12).Lo;
    Int64Rec(ResHi).Hi := Int64Rec(R12).Hi;
    end
  else
    begin
    ResLo := a + b;
    ResHi := 0;
    end;
end;

{$IFDEF WIN64}  // 注意 Linux 64 下不支持 ASM,只能 WIN64

// 64 位下两个无符号 64 位整数相乘,结果放 ResLo 与 ResHi 中,直接用汇编实现,比下面快了一倍以上
procedure UInt64MulUInt64(A, B: UInt64; var ResLo, ResHi: UInt64); assembler;
asm
  PUSH RAX
  MOV RAX, RCX
  MUL RDX         // 得用无符号,不能用有符号的 IMUL
  MOV [R8], RAX
  MOV [R9], RDX
  POP RAX
end;

{$ELSE}

// 两个无符号 64 位整数相乘,结果放 ResLo 与 ResHi 中
procedure UInt64MulUInt64(a, b: TUInt64; var ResLo, ResHi: TUInt64);
var
  x, y, Z, T: cardinal;
  YT, XT, ZY, ZX: TUInt64;
  P, R1Lo, R1Hi, R2Lo, R2Hi: TUInt64;
begin
  // 基本思想:2^32 是系数 M,拆成 (xM+y)*(zM+t) = xzM^2 + (xt+yz)M + yt
  // 各项系数都是 UInt64,xz 占 2、3、4,xt+yz 占 1、2、3,yt 占 0、1,然后累加
  x := Int64Rec(a).Hi;
  y := Int64Rec(a).Lo;
  Z := Int64Rec(b).Hi;
  T := Int64Rec(b).Lo;

  YT := UInt64Mul(y, T);
  XT := UInt64Mul(x, T);
  ZY := UInt64Mul(y, Z);
  ZX := UInt64Mul(x, Z);

  Int64Rec(ResLo).Lo := Int64Rec(YT).Lo;

  P := Int64Rec(YT).Hi;
  UInt64AddUInt64(P, XT, R1Lo, R1Hi);
  UInt64AddUInt64(ZY, R1Lo, R2Lo, R2Hi);

  Int64Rec(ResLo).Hi := Int64Rec(R2Lo).Lo;

  P := TUInt64(Int64Rec(R2Lo).Hi) + TUInt64(Int64Rec(ZX).Lo);

  Int64Rec(ResHi).Lo := Int64Rec(P).Lo;
  Int64Rec(ResHi).Hi := Int64Rec(R1Hi).Lo + Int64Rec(R2Hi).Lo +
    Int64Rec(ZX).Hi + Int64Rec(P).Hi;
end;

{$ENDIF}

{$HINTS OFF}

function _ValUInt64(const S: string; var Code: integer): TUInt64;
const
  FirstIndex = 1;
var
  i: integer;
  Dig: integer;
  Sign: boolean;
  Empty: boolean;
begin
  i      := FirstIndex;
  Dig    := 0;    // To avoid warning
  Result := 0;

  if S = '' then
    begin
    Code := 1;
    Exit;
    end;
  while S[i] = char(' ') do
    Inc(i);
  Sign := False;
  if S[i] = char('-') then
    begin
    Sign := True;
    Inc(i);
    end
  else if S[i] = char('+') then
      Inc(i);
  Empty := True;

  if (S[i] = char('$')) or (UpCase(S[i]) = char('X')) or
    ((S[i] = char('0')) and (i < Length(S)) and (UpCase(S[i + 1]) = char('X'))) then
    begin
    if S[i] = char('0') then
      Inc(i);
    Inc(i);
    while True do
      begin
      case char(S[i]) of
        char('0').. char('9'): Dig := Ord(S[i]) - Ord('0');
        char('A').. char('F'): Dig := Ord(S[i]) - (Ord('A') - 10);
        char('a').. char('f'): Dig := Ord(S[i]) - (Ord('a') - 10);
        else
          Break;
        end;
      if Result > (CN_MAX_TUINT64 shr 4) then
        Break;
      if Sign and (Dig <> 0) then
        Break;
      Result := Result shl 4 + TUInt64(Dig);
      Inc(i);
      Empty := False;
      end;
    end
  else
    begin
    while True do
      begin
      case char(S[i]) of
        char('0').. char('9'): Dig := Ord(S[i]) - Ord('0');
        else
          Break;
        end;

      if Result > UInt64Div(CN_MAX_TUINT64, 10) then
        Break;
      if Sign and (Dig <> 0) then
        Break;
      Result := Result * 10 + TUInt64(Dig);
      Inc(i);
      Empty := False;
      end;
    end;

  if (S[i] <> char(#0)) or Empty then
    Code := i + 1 - FirstIndex
  else
    Code := 0;
end;

{$HINTS ON}

function UInt64ToHex(N: TUInt64): string;
const
  Digits: array[0..15] of char = ('0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');

function HC(b: byte): string;
  begin
    Result := string(Digits[(b shr 4) and $0F] + Digits[b and $0F]);
  end;

begin
  Result :=
    HC(byte((N and $FF00000000000000) shr 56)) +
    HC(byte((N and $00FF000000000000) shr 48)) +
    HC(byte((N and $0000FF0000000000) shr 40)) +
    HC(byte((N and $000000FF00000000) shr 32)) +
    HC(byte((N and $00000000FF000000) shr 24)) +
    HC(byte((N and $0000000000FF0000) shr 16)) +
    HC(byte((N and $000000000000FF00) shr 8)) + HC(byte((N and $00000000000000FF)));
end;

function UInt64ToStr(N: TUInt64): string;
begin
  Result := format('%u', [N]);
end;

function StrToUInt64(const S: string): TUInt64;
{$IFNDEF DELPHIXE6_UP}
var
  e: integer;
{$ENDIF}
begin
{$IFDEF DELPHIXE6_UP}
  Result := SysUtils.StrToUInt64(S);  // StrToUInt64 only exists under XE6 or above
{$ELSE}
  Result := _ValUInt64(S, e);
  if e <> 0 then raise EConvertError.CreateResFmt(@SInvalidInteger, [S]);
{$ENDIF}
end;

function UInt64Compare(a, b: TUInt64): integer;
{$IFNDEF SUPPORT_UINT64}
var
  HiA, HiB, LoA, LoB: longword;
{$ENDIF}
begin
{$IFDEF SUPPORT_UINT64}
  if A > B then
    Result := 1
  else if A < B then
    Result := -1
  else
    Result := 0;
{$ELSE}
  HiA := (a and $FFFFFFFF00000000) shr 32;
  HiB := (b and $FFFFFFFF00000000) shr 32;
  if HiA > HiB then
    Result := 1
  else if HiA < HiB then
      Result := -1
    else
      begin
      LoA := longword(a and $00000000FFFFFFFF);
      LoB := longword(b and $00000000FFFFFFFF);
      if LoA > LoB then
        Result := 1
      else if LoA < LoB then
          Result := -1
        else
          Result := 0;
      end;
{$ENDIF}
end;

function UInt64Sqrt(N: TUInt64): TUInt64;
var
  Rem, Root: TUInt64;
  i: integer;
begin
  Result := 0;
  if N = 0 then
    Exit;

  if UInt64Compare(N, 4) < 0 then
    begin
    Result := 1;
    Exit;
    end;

  Rem  := 0;
  Root := 0;

  for i := 0 to 31 do
    begin
    Root := Root shl 1;
    Inc(Root);

    Rem := Rem shl 2;
    Rem := Rem or (N shr 62);
    N   := N shl 2;

    if UInt64Compare(Root, Rem) <= 0 then
      begin
      Rem := Rem - Root;
      Inc(Root);
      end
    else
      Dec(Root);
    end;
  Result := Root shr 1;
end;

function UInt32IsNegative(N: cardinal): boolean;
begin
  Result := (N and (1 shl 31)) <> 0;
end;

function UInt64IsNegative(N: TUInt64): boolean;
begin
{$IFDEF SUPPORT_UINT64}
  Result := (N and (UInt64(1) shl 63)) <> 0;
{$ELSE}
  Result := N < 0;
{$ENDIF}
end;

// 给 UInt64 的某一位置 1,位 Index 从 0 开始
procedure UInt64SetBit(var b: TUInt64; Index: integer);
begin
  b := b or (TUInt64(1) shl Index);
end;

// 给 UInt64 的某一位置 0,位 Index 从 0 开始
procedure UInt64ClearBit(var b: TUInt64; Index: integer);
begin
  b := b and not (TUInt64(1) shl Index);
end;

// 返回 UInt64 的第几位是否是 1,0 开始
function GetUInt64BitSet(b: TUInt64; Index: integer): boolean;
begin
  b      := b and (TUInt64(1) shl Index);
  Result := b <> 0;
end;

// 返回 UInt64 的是 1 的最高二进制位是第几位,最低位是 0,如果没有 1,返回 -1
function GetUInt64HighBits(b: TUInt64): integer;
begin
  if b = 0 then
    begin
    Result := -1;
    Exit;
    end;

  Result := 1;
  if b shr 32 = 0 then
    begin
    Inc(Result, 32);
    b := b shl 32;
    end;
  if b shr 48 = 0 then
    begin
    Inc(Result, 16);
    b := b shl 16;
    end;
  if b shr 56 = 0 then
    begin
    Inc(Result, 8);
    b := b shl 8;
    end;
  if b shr 60 = 0 then
    begin
    Inc(Result, 4);
    b := b shl 4;
    end;
  if b shr 62 = 0 then
    begin
    Inc(Result, 2);
    b := b shl 2;
    end;
  Result := Result - integer(b shr 63); // 得到前导 0 的数量
  Result := 63 - Result;
end;

// 返回 Cardinal 的是 1 的最高二进制位是第几位,最低位是 0,如果没有 1,返回 -1
function GetUInt32HighBits(b: cardinal): integer;
begin
  if b = 0 then
    begin
    Result := -1;
    Exit;
    end;

  Result := 1;
  if b shr 16 = 0 then
    begin
    Inc(Result, 16);
    b := b shl 16;
    end;
  if b shr 24 = 0 then
    begin
    Inc(Result, 8);
    b := b shl 8;
    end;
  if b shr 28 = 0 then
    begin
    Inc(Result, 4);
    b := b shl 4;
    end;
  if b shr 30 = 0 then
    begin
    Inc(Result, 2);
    b := b shl 2;
    end;
  Result := Result - integer(b shr 31); // 得到前导 0 的数量
  Result := 31 - Result;
end;

function GetUInt16HighBits(b: word): integer;
begin
  if b = 0 then
    begin
    Result := -1;
    Exit;
    end;

  Result := 1;
  if b shr 8 = 0 then
    begin
    Inc(Result, 8);
    b := b shl 8;
    end;
  if b shr 12 = 0 then
    begin
    Inc(Result, 4);
    b := b shl 4;
    end;
  if b shr 14 = 0 then
    begin
    Inc(Result, 2);
    b := b shl 2;
    end;
  Result := Result - integer(b shr 15); // 得到前导 0 的数量
  Result := 15 - Result;
end;

function GetUInt8HighBits(b: byte): integer;
begin
  if b = 0 then
    begin
    Result := -1;
    Exit;
    end;

  Result := 1;
  if b shr 4 = 0 then
    begin
    Inc(Result, 4);
    b := b shl 4;
    end;
  if b shr 6 = 0 then
    begin
    Inc(Result, 2);
    b := b shl 2;
    end;
  Result := Result - integer(b shr 7); // 得到前导 0 的数量
  Result := 7 - Result;
end;

// 返回 Int64 的是 1 的最低二进制位是第几位,最低位是 0,如果没有 1,返回 -1
function GetUInt64LowBits(b: TUInt64): integer;
var
  y: TUInt64;
  N: integer;
begin
  Result := -1;
  if b = 0 then
    Exit;

  N := 63;
  y := b shl 32;
  if y <> 0 then
    begin
    Dec(N, 32);
    b := y;
    end;
  y := b shl 16;
  if y <> 0 then
    begin
    Dec(N, 16);
    b := y;
    end;
  y := b shl 8;
  if y <> 0 then
    begin
    Dec(N, 8);
    b := y;
    end;
  y := b shl 4;
  if y <> 0 then
    begin
    Dec(N, 4);
    b := y;
    end;
  y := b shl 2;
  if y <> 0 then
    begin
    Dec(N, 2);
    b := y;
    end;
  b      := b shl 1;
  Result := N - integer(b shr 63);
end;

// 返回 Cardinal 的是 1 的最低二进制位是第几位,最低位是 0,如果没有 1,返回 -1
function GetUInt32LowBits(b: cardinal): integer;
var
  y, N: integer;
begin
  Result := -1;
  if b = 0 then
    Exit;

  N := 31;
  y := b shl 16;
  if y <> 0 then
    begin
    Dec(N, 16);
    b := y;
    end;
  y := b shl 8;
  if y <> 0 then
    begin
    Dec(N, 8);
    b := y;
    end;
  y := b shl 4;
  if y <> 0 then
    begin
    Dec(N, 4);
    b := y;
    end;
  y := b shl 2;
  if y <> 0 then
    begin
    Dec(N, 2);
    b := y;
    end;
  b      := b shl 1;
  Result := N - integer(b shr 31);
end;

// 返回 Word 的是 1 的最低二进制位是第几位,最低位是 0,基本等同于末尾几个 0。如果没有 1,返回 -1
function GetUInt16LowBits(b: word): integer;
var
  y, N: integer;
begin
  Result := -1;
  if b = 0 then
    Exit;

  N := 15;
  y := b shl 8;
  if y <> 0 then
    begin
    Dec(N, 8);
    b := y;
    end;
  y := b shl 4;
  if y <> 0 then
    begin
    Dec(N, 4);
    b := y;
    end;
  y := b shl 2;
  if y <> 0 then
    begin
    Dec(N, 2);
    b := y;
    end;
  b      := b shl 1;
  Result := N - integer(b shr 15);
end;

// 返回 Byte 的是 1 的最低二进制位是第几位,最低位是 0,基本等同于末尾几个 0。如果没有 1,返回 -1
function GetUInt8LowBits(b: byte): integer;
var
  N: integer;
begin
  Result := -1;
  if b = 0 then
    Exit;

  N := 7;
  if b shr 4 = 0 then
    begin
    Dec(N, 4);
    b := b shl 4;
    end;
  if b shr 6 = 0 then
    begin
    Dec(N, 2);
    b := b shl 2;
    end;
  b      := b shl 1;
  Result := N - integer(b shr 7);
end;

// 封装的 Int64 Mod,碰到负值时取反求模再模减
function Int64Mod(M, N: int64): int64;
begin
  if M > 0 then
    Result := M mod N
  else
    Result := N - ((-M) mod N);
end;

// 判断一 32 位无符号整数是否 2 的整数次幂
function IsUInt32PowerOf2(N: cardinal): boolean;
begin
  Result := (N and (N - 1)) = 0;
end;

// 判断一 64 位无符号整数是否 2 的整数次幂
function IsUInt64PowerOf2(N: TUInt64): boolean;
begin
  Result := (N and (N - 1)) = 0;
end;

// 得到一比指定 32 位无符号整数数大或等的 2 的整数次幂,如溢出则返回 0
function GetUInt32PowerOf2GreaterEqual(N: cardinal): cardinal;
begin
  Result := N - 1;
  Result := Result or (Result shr 1);
  Result := Result or (Result shr 2);
  Result := Result or (Result shr 4);
  Result := Result or (Result shr 8);
  Result := Result or (Result shr 16);
  Inc(Result);
end;

// 得到一比指定 64 位无符号整数数大的 2 的整数次幂,如溢出则返回 0
function GetUInt64PowerOf2GreaterEqual(N: TUInt64): TUInt64;
begin
  Result := N - 1;
  Result := Result or (Result shr 1);
  Result := Result or (Result shr 2);
  Result := Result or (Result shr 4);
  Result := Result or (Result shr 8);
  Result := Result or (Result shr 16);
  Result := Result or (Result shr 32);
  Inc(Result);
end;

// 判断两个 32 位有符号数相加是否溢出 32 位有符号上限
function IsInt32AddOverflow(a, b: integer): boolean;
var
  c: integer;
begin
  c      := a + b;
  Result := ((a > 0) and (b > 0) and (c < 0)) or
    // 同符号且结果换号了说明出现了溢出
    ((a < 0) and (b < 0) and (c > 0));
end;

// 判断两个 32 位无符号数相加是否溢出 32 位无符号上限
function IsUInt32AddOverflow(a, b: cardinal): boolean;
begin
  Result := (a + b) < a;
  // 无符号相加,结果只要小于任一个数就说明溢出了
end;

// 判断两个 64 位有符号数相加是否溢出 64 位有符号上限
function IsInt64AddOverflow(a, b: int64): boolean;
var
  c: int64;
begin
  c      := a + b;
  Result := ((a > 0) and (b > 0) and (c < 0)) or
    // 同符号且结果换号了说明出现了溢出
    ((a < 0) and (b < 0) and (c > 0));
end;

// 判断两个 64 位无符号数相加是否溢出 64 位无符号上限
function IsUInt64AddOverflow(a, b: TUInt64): boolean;
begin
  Result := UInt64Compare(a + b, a) < 0;
  // 无符号相加,结果只要小于任一个数就说明溢出了
end;

// 两个 64 位无符号数相加,A + B => R,如果有溢出,则溢出的 1 搁进位标记里,否则清零
procedure UInt64Add(var r: TUInt64; a, b: TUInt64; out Carry: integer);
begin
  r := a + b;
  if UInt64Compare(r, a) < 0 then
    // 无符号相加,结果只要小于任一个数就说明溢出了
    Carry := 1
  else
    Carry := 0;
end;

// 两个 64 位无符号数相减,A - B => R,如果不够减有借位,则借的 1 搁借位标记里,否则清零
procedure UInt64Sub(var r: TUInt64; a, b: TUInt64; out Carry: integer);
begin
  r := a - b;
  if UInt64Compare(r, a) > 0 then
    // 无符号相减,结果只要大于被减数就说明借位了
    Carry := 1
  else
    Carry := 0;
end;

// 判断两个 32 位有符号数相乘是否溢出 32 位有符号上限
function IsInt32MulOverflow(a, b: integer): boolean;
var
  T: integer;
begin
  T      := a * b;
  Result := (b <> 0) and ((T div b) <> a);
end;

// 判断两个 32 位无符号数相乘是否溢出 32 位无符号上限
function IsUInt32MulOverflow(a, b: cardinal): boolean;
var
  T: TUInt64;
begin
  T      := TUInt64(a) * TUInt64(b);
  Result := (T = cardinal(T));
end;

// 判断两个 32 位无符号数相乘是否溢出 64 位有符号数,如未溢出也即返回 False 时,R 中直接返回结果
function IsUInt32MulOverflowInt64(a, b: cardinal; out r: TUInt64): boolean;
var
  T: int64;
begin
  T      := int64(a) * int64(b);
  Result := T < 0; // 如果出现 Int64 负值则说明溢出
  if not Result then
    r := TUInt64(T);
end;

// 判断两个 64 位有符号数相乘是否溢出 64 位有符号上限
function IsInt64MulOverflow(a, b: int64): boolean;
var
  T: int64;
begin
  T      := a * b;
  Result := (b <> 0) and ((T div b) <> a);
end;

// 指针类型转换成整型,支持 32/64 位
function PointerToInteger(P: Pointer): integer;
begin
{$IFDEF CPU64BITS}
  // 先这么写,利用 Pointer 的低 32 位存 Integer
  Result := Integer(P);
{$ELSE}
  Result := integer(P);
{$ENDIF}
end;

// 整型转换成指针类型,支持 32/64 位
function IntegerToPointer(i: integer): Pointer;
begin
{$IFDEF CPU64BITS}
  // 先这么写,利用 Pointer 的低 32 位存 Integer
  Result := Pointer(I);
{$ELSE}
  Result := Pointer(i);
{$ENDIF}
end;

// 求 Int64 范围内俩加数的和求余,处理溢出的情况,要求 N 大于 0
function Int64NonNegativeAddMod(a, b, N: int64): int64;
begin
  if IsInt64AddOverflow(a, b) then // 如果加起来溢出 Int64
    begin
    if a > 0 then
      begin
      // A 和 B 都大于 0,采用 UInt64 相加取模(和未溢出 UInt64 上限),注意 N 未溢出 Int64 因此取模结果小于 Int64 上限,不会变成负值
      Result := UInt64NonNegativeAddMod(a, b, N);
      end
    else
      begin
      // A 和 B 都小于 0,取反后采用 UInt64 相加取模(反后的和未溢出 UInt64 上限),模再被除数减一下
{$IFDEF SUPPORT_UINT64}
      Result := UInt64(N) - UInt64NonNegativeAddMod(-A, -B, N);
{$ELSE}
      Result := N - UInt64NonNegativeAddMod(-a, -b, N);
{$ENDIF}
      end;
    end
  else // 不溢出,直接加起来求余
    Result := Int64NonNegativeMod(a + b, N);
end;

// 求 UInt64 范围内俩加数的和求余,处理溢出的情况,要求 N 大于 0
function UInt64NonNegativeAddMod(a, b, N: TUInt64): TUInt64;
var
  c, d: TUInt64;
begin
  if IsUInt64AddOverflow(a, b) then // 如果加起来溢出
    begin
    c := UInt64Mod(a, N);  // 就各自求模
    d := UInt64Mod(b, N);
    if IsUInt64AddOverflow(c, d) then
      begin
      // 如果还是溢出,说明模比两个加数都大,各自求模没用。
      // 至少有一个加数大于等于 2^63,N 至少是 2^63 + 1
      // 和 = 溢出结果 + 2^64
      // 和 mod N = 溢出结果 mod N + (2^64 - 1) mod N) + 1
      // 这里 N 至少是 2^63 + 1,溢出结果最多是 2^64 - 2,所以前两项相加不会溢出,可以直接相加后减一再求模
      Result := UInt64Mod(UInt64Mod(a + b, N) + UInt64Mod(CN_MAX_TUINT64, N) + 1, N);
      end
    else
      Result := UInt64Mod(c + d, N);
    end
  else
    begin
    Result := UInt64Mod(a + b, N);
    end;
end;

function Int64NonNegativeMulMod(a, b, N: int64): int64;
var
  Neg: boolean;
begin
  if N <= 0 then
    raise EDivByZero.Create(SDivByZero);

  // 范围小就直接算
  if not IsInt64MulOverflow(a, b) then
    begin
    Result := a * b mod N;
    if Result < 0 then
      Result := Result + N;
    Exit;
    end;

  // 调整符号到正
  Result := 0;
  if (a = 0) or (b = 0) then
    Exit;

  Neg := False;
  if (a < 0) and (b > 0) then
    begin
    a   := -a;
    Neg := True;
    end
  else if (a > 0) and (b < 0) then
      begin
      b   := -b;
      Neg := True;
      end
    else if (a < 0) and (b < 0) then
        begin
        a := -a;
        b := -b;
        end;

  // 移位循环算
  while b <> 0 do
    begin
    if (b and 1) <> 0 then
      Result := ((Result mod N) + (a mod N)) mod N;

    a := a shl 1;
    if a >= N then
      a := a mod N;

    b := b shr 1;
    end;

  if Neg then
    Result := N - Result;
end;

function UInt64NonNegativeMulMod(a, b, N: TUInt64): TUInt64;
begin
  Result := 0;
  if (UInt64Compare(a, CN_MAX_UINT32) <= 0) and
    (UInt64Compare(b, CN_MAX_UINT32) <= 0) then
    begin
    Result := UInt64Mod(a * b, N); // 足够小的话直接乘后求模
    end
  else
    begin
    while b <> 0 do
      begin
      if (b and 1) <> 0 then
        Result := UInt64NonNegativeAddMod(Result, a, N);

      a := UInt64NonNegativeAddMod(a, a, N);
      // 不能用传统算法里的 A := A shl 1,大于 N 后再 mod N,因为会溢出

      b := b shr 1;
      end;
    end;
end;

// 封装的非负求余函数,也就是余数为负时,加个除数变正,调用者需保证 P 大于 0
function Int64NonNegativeMod(N: int64; P: int64): int64;
begin
  if P <= 0 then
    raise EDivByZero.Create(SDivByZero);

  Result := N mod P;
  if Result < 0 then
    Inc(Result, P);
end;

// Int64 的非负整数指数幂
function Int64NonNegativPower(N: int64; Exp: integer): int64;
var
  T: int64;
begin
  if Exp < 0 then
    raise ERangeError.Create(SRangeError)
  else if Exp = 0 then
      begin
      if N <> 0 then
        Result := 1
      else
        raise EDivByZero.Create(SDivByZero);
      end
    else if Exp = 1 then
        Result := N
      else
        begin
        Result := 1;
        T      := N;

        while Exp > 0 do
          begin
          if (Exp and 1) <> 0 then
            Result := Result * T;

          Exp := Exp shr 1;
          T   := T * T;
          end;
        end;
end;

function Int64NonNegativeRoot(N: int64; Exp: integer): int64;
var
  i: integer;
  x: int64;
  X0, x1: extended;
begin
  if (Exp < 0) or (N < 0) then
    raise ERangeError.Create(SRangeError)
  else if Exp = 0 then
      raise EDivByZero.Create(SDivByZero)
    else if (N = 0) or (N = 1) then
        Result := N
      else if Exp = 2 then
          Result := UInt64Sqrt(N)
        else
          begin
          // 牛顿迭代法求根
          i := GetUInt64HighBits(N) + 1; // 得到大约 Log2 N 的值
          i := (i div Exp) + 1;
          x := 1 shl i;                  // 得到一个较大的 X0 值作为起始值

          X0 := x;
          x1 := X0 - (Power(X0, Exp) - N) / (Exp * Power(X0, Exp - 1));

          while True do
            begin
            if (Trunc(X0) = Trunc(x1)) and (Abs(X0 - x1) < 0.001) then
              begin
              Result := Trunc(x1); // Trunc 只支持 Int64,超界了会出错
              Exit;
              end;

            X0 := x1;
            x1 := X0 - (Power(X0, Exp) - N) / (Exp * Power(X0, Exp - 1));
            end;
          end;
end;

function UInt64NonNegativPower(N: TUInt64; Exp: integer): TUInt64;
var
  T, RL, RH: TUInt64;
begin
  if Exp < 0 then
    raise ERangeError.Create(SRangeError)
  else if Exp = 0 then
      begin
      if N <> 0 then
        Result := 1
      else
        raise EDivByZero.Create(SDivByZero);
      end
    else if Exp = 1 then
        Result := N
      else
        begin
        Result := 1;
        T      := N;

        while Exp > 0 do
          begin
          if (Exp and 1) <> 0 then
            begin
            UInt64MulUInt64(Result, T, RL, RH);
            Result := RL;
            end;

          Exp := Exp shr 1;
          UInt64MulUInt64(T, T, RL, RH);
          T := RL;
          end;
        end;
end;

function UInt64NonNegativeRoot(N: TUInt64; Exp: integer): TUInt64;
var
  i: integer;
  x: TUInt64;
  XN, X0, x1: extended;
begin
  if Exp < 0 then
    raise ERangeError.Create(SRangeError)
  else if Exp = 0 then
      raise EDivByZero.Create(SDivByZero)
    else if (N = 0) or (N = 1) then
        Result := N
      else if Exp = 2 then
          Result := UInt64Sqrt(N)
        else
          begin
          // 牛顿迭代法求根
          i := GetUInt64HighBits(N) + 1; // 得到大约 Log2 N 的值
          i := (i div Exp) + 1;
          x := 1 shl i;                  // 得到一个较大的 X0 值作为起始值

          X0 := UInt64ToExtended(x);
          XN := UInt64ToExtended(N);
          x1 := X0 - (Power(X0, Exp) - XN) / (Exp * Power(X0, Exp - 1));

          while True do
            begin
            if (ExtendedToUInt64(X0) = ExtendedToUInt64(x1)) and (Abs(X0 - x1) < 0.001) then
              begin
              Result := ExtendedToUInt64(x1);
              Exit;
              end;

            X0 := x1;
            x1 := X0 - (Power(X0, Exp) - XN) / (Exp * Power(X0, Exp - 1));
            end;
          end;
end;

function IsUInt128BitSet(Lo, Hi: TUInt64; N: integer): boolean;
begin
  if N < 64 then
    Result := (Lo and (TUInt64(1) shl N)) <> 0
  else
    begin
    Dec(N, 64);
    Result := (Hi and (TUInt64(1) shl N)) <> 0;
    end;
end;

procedure SetUInt128Bit(var Lo, Hi: TUInt64; N: integer);
begin
  if N < 64 then
    Lo := Lo or (TUInt64(1) shl N)
  else
    begin
    Dec(N, 64);
    Hi := Hi or (TUInt64(1) shl N);
    end;
end;

procedure ClearUInt128Bit(var Lo, Hi: TUInt64; N: integer);
begin
  if N < 64 then
    Lo := Lo and not (TUInt64(1) shl N)
  else
    begin
    Dec(N, 64);
    Hi := Hi and not (TUInt64(1) shl N);
    end;
end;

function UnsignedAddWithLimitRadix(a, b, c: cardinal; var r: cardinal;
L, H: cardinal): cardinal;
begin
  r := a + b + c;
  if r > H then         // 有进位
    begin
    a := H - L + 1;     // 得到进制
    b := r - L;         // 得到超出 L 的值

    Result := b div a;  // 超过进制的第几倍就进几
    r      := L + (b mod a); // 去掉进制后的余数,加上下限
    end
  else
    Result := 0;
end;

procedure InternalQuickSort(Mem: Pointer; L, r: integer; ElementByteSize: integer;
CompareProc: TCnMemSortCompareProc);
var
  i, j, P: integer;
begin
  repeat
    i := L;
    j := r;
    P := (L + r) shr 1;
    repeat
      while CompareProc(Pointer(TCnNativeInt(Mem) + i * ElementByteSize),
          Pointer(TCnNativeInt(Mem) + P * ElementByteSize), ElementByteSize) < 0 do
        Inc(i);
      while CompareProc(Pointer(TCnNativeInt(Mem) + j * ElementByteSize),
          Pointer(TCnNativeInt(Mem) + P * ElementByteSize), ElementByteSize) > 0 do
        Dec(j);

      if i <= j then
        begin
        MemorySwap(Pointer(TCnNativeInt(Mem) + i * ElementByteSize),
          Pointer(TCnNativeInt(Mem) + j * ElementByteSize), ElementByteSize);

        if P = i then
          P := j
        else if P = j then
            P := i;
        Inc(i);
        Dec(j);
        end;
    until i > j;

    if L < j then
      InternalQuickSort(Mem, L, j, ElementByteSize, CompareProc);
    L := i;
  until i >= r;
end;

function DefaultCompareProc(p1, p2: Pointer; ElementByteSize: integer): integer;
begin
  Result := MemoryCompare(p1, p2, ElementByteSize);
end;

procedure MemoryQuickSort(Mem: Pointer; ElementByteSize: integer;
ElementCount: integer; CompareProc: TCnMemSortCompareProc);
begin
  if (Mem <> nil) and (ElementCount > 0) and (ElementCount > 0) then
    begin
    if Assigned(CompareProc) then
      InternalQuickSort(Mem, 0, ElementCount - 1, ElementByteSize, CompareProc)
    else
      InternalQuickSort(Mem, 0, ElementCount - 1, ElementByteSize, @DefaultCompareProc);
    end;
end;

{$IFDEF COMPILER5}

function BoolToStr(Value: Boolean; UseBoolStrs: Boolean): string;
begin
  if UseBoolStrs then
  begin
    if Value then
      Result := 'True'
    else
      Result := 'False';
  end
  else
  begin
    if Value then
      Result := '-1'
    else
      Result := '0';
  end;
end;

{$ENDIF}

initialization
FByteOrderIsBigEndian := CurrentByteOrderIsBigEndian;

end.

  

标签:function,begin,end,PCardinal,CNSM4,lazarus,Result,CNvcl,integer
From: https://www.cnblogs.com/CatDo/p/18032100

相关文章

  • lazarus3.0 /fpc3.3.1编译某些控件会出现:Error: Forward declaration not solved xxx
    最近用lazarus3.0/fpc3.3.1时发现原来在lazarus2.2.6/fpc3.2.2能编译安装的控件出现类似下面的提示codebot.text.xml.pas(129,10)Error:Forwarddeclarationnotsolved"NewDocument:IDocument;"解决方法:本例子参照DocumentCreate:IDocument,在实现部分编写过程。{$i......
  • (17)Lazarus学习之StringGrid1
    01]下拉ComboBox1选择  参考:C:\lazarus\examples\gridexamples\gridcelleditorprocedureTForm1.StringGrid1SelectEditor(Sender:TObject;aCol,aRow:Integer;varEditor:TWinControl);beginif(aCol=3)and(aRow>0)thenbegin//哪些单元格显示Comb......
  • (14)lazarus:不安装任何驱动,直接使用csv文件作为数据集
    参考https://blog.csdn.net/bq_cui/article/details/134259372简介在某些数据操作需求特别简单的情况下,我们希望不要安装任何数据库,甚至连一个驱动dll都不要,因为这些驱动dll可能涉及到版本甚至跨平台问题。这种简单需求,可以直接使用csv文件作为数据集来进行增查删改操作。注意,这......
  • unidac在lazarus 3.0/fpc3.3.1遇到的问题
    近日和樵夫交流时发现unidac在aarch64linux交叉编译UniProvider.pas出错:UniProvider.pas(1040,1)Error:Compilationraisedexceptioninternally奇怪的是其他CPU类型是正常的。樵夫的解决办法:1、修改UniProvider.pas,添加{$ifFPC_FULLVERSION<30301}TEnumerator......
  • lazarus 3.0/fpc3.3.1写线程要注意的事项
    近日和高勇交流中发现,以下代码在delphi能编译及正确执行。procedureTForm1.Button2Click(Sender:TObject);vari:integer;beginTThread.CreateAnonymousThread(procedurebeginsleep(3000);TThread.Synchronize(TThread.CurrentThread,proced......
  • (11)lazarus带历史记忆,并模糊带出功能的Edit
    procedureTForm1.Edit1KeyDown(Sender:TObject;varKey:Word;Shift:TShiftState);beginif(key=40)and(listbox2.Items.Count>0)andlistbox2.Visiblethenbegin//defines.icnVK_DOWN=40;ListBox2.SetFocus;ListBox2.ItemIndex:=0;......
  • (10)Lazarus下ListBox自画
    参考:VCL下ListBox自画,Lazarus也一样usesLCLType; ListBox的两个设置:自画事件:ListBox1DrawItemprocedureTForm1.ListBox1DrawItem(Control:TWinControl;Index:Integer;ARect:TRect;State:TOwnerDrawState);beginWithListBox1.CanvasDoBeginBrus......
  • Lazarus windows远程交叉调试Linux
    一、在目标机上安装gdbserver服务。运行gdbserver:2345你要调试的程序二、编译gdb(重点)1、下载gdb源,下载msys2-x86_64-20220118.exe。2、安装msys23、到Msys2安装目录下运行mingw64.exe4、安装以下包pacman-Syupacman-Smingw-w64-x86_64-toolchainpacman-Stexinfo......
  • (11)lazarus带历史记忆,并模糊带出功能的Edit
    procedureTForm1.Edit1KeyDown(Sender:TObject;varKey:Word;Shift:TShiftState);beginif(key=40)and(listbox2.Items.Count>0)andlistbox2.Visiblethenbegin//defines.icnVK_DOWN=40;ListBox2.SetFocus;ListBox2.ItemIndex:=0;......
  • (10)Lazarus下ListBox自画
    参考:VCL下ListBox自画,Lazarus也一样usesLCLType; ListBox的两个设置:自画事件:ListBox1DrawItemprocedureTForm1.ListBox1DrawItem(Control:TWinControl;Index:Integer;ARect:TRect;State:TOwnerDrawState);beginWithListBox1.CanvasDo......