lazarus使用CNvcl 中的CNSM4
参考了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 }
{ 电子邮件:master@cnpack.org }
{ }
{******************************************************************************}
unit CnSM4;
{* |<PRE>
================================================================================
* 软件名称:开发包基础库
* 单元名称:国产分组密码算法 SM4 单元
* 单元作者:刘啸(liuxiao@cnpack.org)
* 备 注:参考国密算法公开文档 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 }
{ 电子邮件:master@cnpack.org }
{******************************************************************************}
unit CnNative;
{* |<PRE>
================================================================================
* 软件名称:CnPack 组件包
* 单元名称:32 位和 64 位的一些统一声明以及一堆基础实现
* 单元作者:刘啸 (liuxiao@cnpack.org)
* 备 注: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.
浙公网安备 33010602011771号