You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

311 lines
11 KiB
Plaintext

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

unit u_base64;
interface
uses SysUtils, Classes;
type
{$IFDEF UNICODE}
Base64String = AnsiString;
{$ELSE}
Base64String = string;
{$ENDIF}
// 按源长度SourceSize返回Base64编码所需缓冲区字节数
function Base64EncodeBufSize(SourceSize: Integer): Integer;
// 获取Sourec的Base64编码Base64Buf必须有足够长度。返回实际编码字节数
function Base64Encode(const Source; SourceSize: Integer; var Base64Buf): Integer; overload;
// 将Source编码为Base64字符串返回
function Base64Encode(const Source; SourceSize: Integer): Base64String; overload;
// 将Source从StartPos开始的Size长度的内容源编码为Base64写入流Dest。
// Size=0 表示一直编码到文件尾
procedure Base64Encode(Source, Dest: TStream; StartPos: Int64 = 0; Size: Int64 = 0); overload;
// 把字符串Str编码为Base64字符串返回
{$IFDEF UNICODE}
function StrToBase64(const Str: AnsiString): Base64String; overload;
function StrToBase64(const Str: string): Base64String; overload;
{$ELSE}
function StrToBase64(const Str: string): Base64String;
{$ENDIF}
// 按给定的编码源Source和长度SourceSize计算并返回解码缓冲区所需字节数
function Base64DecodeBufSize(const Base64Source; SourceSize: Integer): Integer;
// 将Base64编码源Base64Source解码Buf必须有足够长度。返回实际解码字节数
function Base64Decode(const Base64Source; SourceSize: Integer; var Buf): Integer; overload;
// 将Source从StartPos开始的Size长度的Base64编码内容解码写入流Dest。
// Size=0 表示一直解码到文件尾
procedure Base64Decode(Source, Dest: TStream; StartPos: Int64 = 0; Size: Int64 = 0); overload;
// 将Base64编码源Base64Source解码为字符串返回
function Base64Decode(const Base64Source; SourceSize: Integer): string; overload;
// 把Base64字符串Base64Str解码为字符串返回
function Base64ToStr(const Base64Str: Base64String): string;
implementation
const
Base64_Chars: array[0..63] of AnsiChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
Base64_Bytes: array[0..79] of Byte =
(
62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0,
0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
);
type
Base64Proc = function(const Source; SourceSize: Integer; var Buf): Integer;
procedure Base64Stream(Source, Dest: TStream; Proc: Base64Proc;
StartPos, Size: Int64; RBufSize, WBufSize: Integer);
var
RBuf: array of Byte;
WBuf: array of Byte;
RSize, WSize: Integer;
begin
if (StartPos < 0) or (StartPos >= Source.Size) then Exit;
Source.Position := StartPos;
if (Size <= 0) or (Size > Source.Size - Source.Position) then
Size := Source.Size
else
Size := Size + Source.Position;
SetLength(RBuf, RBufSize);
SetLength(WBuf, WBufSize);
while Size <> Source.Position do
begin
if RBufSize > Size - Source.Position then
RBufSize := Size - Source.Position;
RSize := Source.Read(RBuf[0], RBufSize);
WSize := Proc(RBuf[0], RSize, WBuf[0]);
Dest.Write(WBuf[0], WSize);
end;
end;
function Base64EncodeBufSize(SourceSize: Integer): Integer;
begin
Result := ((SourceSize + 2) div 3) shl 2;
end;
(****************************************************************************
* *
* BASE64 Encode hint: *
* *
* addr: (high) 4 byte 3 byte 2 byte 1 byte (low) *
* sourec ASCII(3 bytes): 33333333 22222222 11111111 *
* bswap: 11111111 22222222 33333333 00000000 *
* b4 = Base64_Chars[(source >> 8) & 63]: [00333333]->44444444 *
* b3 = Base64_Chars[(source >> 14) & 63]: [00222233]->33333333 *
* b2 = Base64_Chars[(source >> 20) & 63]: [00112222]->22222222 *
* b1 = Base64_Chars[source >> 26]: [00111111]->11111111 *
* b4 << 24 b3 << 16 b2 << 8 b1 *
* dest BASE64(4 bytes) 44444444 33333333 22222222 11111111 *
* *
****************************************************************************)
function Base64Encode(const Source; SourceSize: Integer; var Base64Buf): Integer;
asm
push ebp
push esi
push edi
push ebx
mov esi, eax // esi = Source
mov edi, ecx // edi = Buf
mov eax, edx
cdq
mov ecx, 3
div ecx // edx = SourceSize % 3
mov ecx, eax // ecx = SourceSize / 3
test edx, edx
jz @@1
inc eax // eax = (SourceSize + 2) / 3
@@1:
push eax
push edx
lea ebp, Base64_Chars
jecxz @Last
cld
@EncodeLoop: // while (ecx > 0){
mov edx, [esi] // edx = 00000000 33333333 22222222 11111111
bswap edx // edx = 11111111 22222222 33333333 00000000
push edx
push edx
push edx
pop ebx // ebx = edx
shr edx, 20
shr ebx, 26 // ebx = 00111111
and edx, 63 // edx = 00112222
mov ah, [ebp + edx] // *(word*)edi = (Base64_Chars[edx] << 8) |
mov al, [ebp + ebx] // Base64_Chars[ebx]
stosw // edi += 2
pop edx // edx = 11111111 22222222 33333333 00000000
pop ebx // ebx = edx
shr edx, 8
shr ebx, 14
and edx, 63 // edx = 00333333
and ebx, 63 // ebx = 00222233
mov ah, [ebp + edx] // *(word*)edi = (Base64_Chars[edx] << 8) |
mov al, [ebp + ebx] // Base64_Chars[ebx]
stosw // edi += 2
add esi, 3 // esi += 3
loop @EncodeLoop // }
@Last:
pop ecx // ecx = SourceSize % 3
jecxz @end // if (ecx == 0) return
mov eax, 3d3d0000h // preset 2 bytes '='
mov [edi], eax
test ecx, 2
jnz @@3
mov al, [esi] // if (ecx == 1)
shl eax, 4 // eax = *esi << 4
jmp @@4
@@3:
mov ax, [esi] // else
xchg al, ah // eax = ((*esi << 8) or *(esi + 1)) << 2
shl eax, 2
@@4:
add edi, ecx // edi += ecx
inc ecx // ecx = last encode bytes
@LastLoop:
mov edx, eax // for (; cex > 0; ecx --, edi --)
and edx, 63 // {
mov dl, [ebp + edx] // edx = eax & 63
mov [edi], dl // *edi = Base64_Chars[edx]
shr eax, 6 // eax >>= 6
dec edi // }
loop @LastLoop
@end:
pop eax
shl eax, 2 // return encode bytes
pop ebx
pop edi
pop esi
pop ebp
end;
function Base64Encode(const Source; SourceSize: Integer): Base64String;
begin
SetLength(Result, Base64EncodeBufSize(SourceSize));
Base64Encode(Source, SourceSize, Result[1]);
end;
procedure Base64Encode(Source, Dest: TStream; StartPos: Int64; Size: Int64);
begin
Base64Stream(Source, Dest, Base64Encode, StartPos, Size, 6144, 8192);
end;
{$IFDEF UNICODE}
function StrToBase64(const Str: AnsiString): Base64String;
begin
Result := Base64Encode(Str[1], Length(Str));
end;
function StrToBase64(const Str: string): Base64String;
begin
Result := StrToBase64(AnsiString(Str));
end;
{$ELSE}
function StrToBase64(const Str: string): Base64String;
begin
Result := Base64Encode(Str[1], Length(Str));
end;
{$ENDIF}
function Base64DecodeBufSize(const Base64Source; SourceSize: Integer): Integer;
asm
mov ecx, eax // ecx = Source + Size
add ecx, edx
mov eax, edx // eax = Size / 4 * 3
shr edx, 2
shr eax, 1
add eax, edx
mov edx, eax
jz @@2
@@1:
dec ecx
cmp byte ptr [ecx], 61
jne @@2 // if (*--ecx == '=')
dec eax // eax --
jmp @@1
@@2: // return eax: BufSize; edx: Size / 4 * 3
end;
function Base64Decode(const Base64Source; SourceSize: Integer; var Buf): Integer;
asm
push ebp
push esi
push edi
push ebx
mov esi, eax // esi = Source
mov edi, ecx // edi = Buf
mov ebx, edx
call Base64DecodeBufSize
push eax // eax = Base64DecodeBufSize(Source, SourceSize)
sub edx, eax // edx -= eax // edx: '=' count
lea ebp, Base64_Bytes
shr ebx, 2 // ebx = SourceSize / 4
test ebx, ebx
jz @end
push edx
cld
@DecodeLoop: // for (; ebx > 0; ebx --; edi += 3)
mov ecx, 4 // {
xor eax, eax
@xchgLoop: // for (ecx = 4, eax = 0; ecx > 0; ecx --)
movzx edx, [esi] // {
sub edx, 43 // edx = *(int*)esi - 43
shl eax, 6 // eax <<= 6
or al, [ebp + edx]// al |= Base64_Bytes[edx]
inc esi // esi ++
loop @xchgLoop // }
bswap eax // bswap(eax)
dec ebx // if (ebx == 1) break
jz @Last
shr eax, 8 // eax >>= 8
stosw // *edi = ax; edi += 2
shr eax, 16 // eax >>= 16
stosb // *edi++ = al
jmp @DecodeLoop // }
@Last:
pop ecx
xor ecx, 3 // ecx = last bytes
@LastLoop: // for (; ecx > 0; ecx --)
shr eax, 8 // {
stosb // eax >>= 8; *edi ++ = al
loop @LastLoop // }
@end:
pop eax // return eax
pop ebx
pop edi
pop esi
pop ebp
end;
procedure Base64Decode(Source, Dest: TStream; StartPos: Int64; Size: Int64);
begin
Base64Stream(Source, Dest, Base64Decode, StartPos, Size, 8192, 6144);
end;
{$IFDEF UNICODE}
function Base64Decode(const Base64Source; SourceSize: Integer): string;
var
s: AnsiString;
begin
SetLength(s, Base64DecodeBufSize(Base64Source, SourceSize));
Base64Decode(Base64Source, SourceSize, s[1]);
Result := string(s);
end;
{$ELSE}
function Base64Decode(const Base64Source; SourceSize: Integer): string;
begin
SetLength(Result, Base64DecodeBufSize(Base64Source, SourceSize));
Base64Decode(Base64Source, SourceSize, Result[1]);
end;
{$ENDIF}
function Base64ToStr(const Base64Str: Base64String): string;
begin
Result := Base64Decode(Base64Str[1], Length(Base64Str));
end;
end.