|
|
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.
|