test2

program Project2;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  windows, System.SysUtils;

function memcmp(ptr1: PAnsiChar; ptr2: PAnsiChar; num: DWORD): Integer; cdecl;
  external 'Ntdll.dll' name 'memcmp';

function sprintf(S: PAnsiChar; const Format: PAnsiChar): Integer; cdecl; varargs;
  external 'msvcrt.dll';

function ZStartTime(var StartTime: Int64): Boolean;
begin
  Result := QueryPerformanceCounter(StartTime);
end;

function ZStopTime(const StartTime: Int64): AnsiString;
var
  iCounterPerSec, StopTime: Int64;
  time: Single;
begin
  if QueryPerformanceCounter(StopTime) then
  begin
    if QueryPerformanceFrequency(iCounterPerSec) then
    begin

      time := (0 - StartTime + StopTime) / iCounterPerSec;

      Result := '';
      SetLength(Result, 25);

      SetLength(Result, sprintf(PAnsiChar(Result), 'Result: %f sec.', time));
    end
    else
      Result := 'Error[ZStopTime(QueryPerformanceFrequency)]';
  end
  else
    Result := 'Error[ZStopTime(QueryPerformanceCounter)]';
end;

function base64_encode(const str: string): string;
const
  table: PWideChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
type
  PArrayChars = ^TArrayChars;
  TArrayChars = array of char;
var
  len, i, r: Integer;
  a, b: Byte;
  Sum: Cardinal;
begin
  len := Length(str);

  SetLength(Result, ((len + 2) div 3) * 4);

  i := 0;
  r := 0;
  while i <= len - 3 do
  begin
    Sum := WORD(str[i + 1]) shl 16 + WORD(str[i + 2]) shl 8 + WORD(str[i + 3]);

    PArrayChars(@Result)^[r + 0] := table[Sum shr 18 and $3F];
    PArrayChars(@Result)^[r + 1] := table[Sum shr 12 and $3F];
    PArrayChars(@Result)^[r + 2] := table[Sum shr 6 and $3F];
    PArrayChars(@Result)^[r + 3] := table[Sum and $3F];

    inc(i, 3);
    inc(r, 4);
  end;

  case len mod 3 of
    1:
      begin
        a := WORD(str[len]) and $FF;
        PArrayChars(@Result)^[r + 0] := table[a SHR 2];
        PArrayChars(@Result)^[r + 1] := table[a and 3 SHL 4];
        PArrayChars(@Result)^[r + 2] := '=';
        PArrayChars(@Result)^[r + 3] := '=';
      end;
    2:
      begin
        a := WORD(str[i + 1]) and $FF;
        b := WORD(str[i + 2]) and $FF;
        PArrayChars(@Result)^[r + 0] := table[a SHR 2];
        PArrayChars(@Result)^[r + 1] := table[((a and 3) SHL 4) or (b SHR 4)];
        PArrayChars(@Result)^[r + 2] := table[b and $0F SHL 2];
        PArrayChars(@Result)^[r + 3] := '=';
      end;
  end;
end;

var
  i: Cardinal;
  StartTime: Int64;

begin
  try
    Writeln(base64_encode('HytujkyHytujkyukHytujkyukuk'));
    if ZStartTime(StartTime) then
    begin
      for i := 0 to 10000000 do
      begin
        base64_encode('HytujkyHytujkyukHytujkyukuk');
      end;
      Writeln(ZStopTime(StartTime));
    end;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;

end.
View Code
function ELFHash(const Value: String): Integer;
var
  i, x: Integer;
begin
  Result := 0;
  for i := 1 to Length(Value) do
  begin
    Result := (Result shl 4) + Ord(Value[i]);
    x := Result and $F0000000;
    if (x <> 0) then
      Result := Result xor (x shr 24);
    Result := Result and (not x);
  end;
end;

 

program SpeedTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Winapi.Windows,
  System.Generics.Defaults,
  System.Generics.Collections,
  System.SysUtils,
  System.Classes;

type
  TStringEqualComparer = class(TInterfacedObject)
    function Equals(const Left, Right: string): Boolean; reintroduce;
    // важно! если сделать виртуальный метод - то скорость падает ~3 раза
    // function GetHashCode(const Value: string): Integer; reintroduce; virtual; abstract;
  end;
  TComparerClass = class of TStringEqualComparer;

  TSedgwicComparer = class(TStringEqualComparer, IEqualityComparer<string>)
    function GetHashCode(const Value: string): Integer; reintroduce;
  end;

  TShaPerfectComparer = class(TStringEqualComparer, IEqualityComparer<string>)
    function GetHashCode(const Value: string): Integer; reintroduce;
  end;

  TFastComparer = class(TStringEqualComparer, IEqualityComparer<string>)
    function GetHashCode(const Value: string): Integer; reintroduce;
  end;

{ TStringEqualComparer }

function TStringEqualComparer.Equals(const Left, Right: string): Boolean;
label
  cmp_ptrs;
var
  i, Count: NativeInt;
  S1, S2: PNativeUInt;
begin
  S1 := Pointer(Left);
  S2 := Pointer(Right);
  if (S1 <> S2) and (S1 <> nil) and (S2 <> nil) then
  begin
    Dec(NativeUInt(S1), SizeOf(Integer));
    Dec(NativeUInt(S2), SizeOf(Integer));
    Count := PInteger(S1)^;
    if (Integer(Count) <> PInteger(S2)^) then goto cmp_ptrs;
    Inc(NativeUInt(S1), SizeOf(Integer));
    Inc(NativeUInt(S2), SizeOf(Integer));

    for i := 1 to Count shr {$if SizeOf(NativeUInt) = 8}2{$else}1{$endif} do
    begin
      if (S1^ <> S2^) then goto cmp_ptrs;
      Inc(S1);
      Inc(S2);
    end;

    {$if SizeOf(NativeUInt) = 8}
    if (Count and 2 <> 0) then
    begin
      if (PCardinal(S1)^ <> PCardinal(S2)^) then goto cmp_ptrs;
      Inc(NativeUInt(S1), SizeOf(Cardinal));
      Inc(NativeUInt(S2), SizeOf(Cardinal));
    end;
    {$endif}

    if (PWord(S1)^ <> PWord(S2)^) then goto cmp_ptrs;
    Result := True;
    Exit;
  end else
  begin
  cmp_ptrs:
    Result := (S1 = S2);
  end;
end;

{ TSedgwicComparer }

function TSedgwicComparer.GetHashCode(const Value: string): Integer;
var
  a, i: Integer;
begin
  Result := 0;
  a := 63689;
  for i := 0 to Length(Value) - 1 do
  begin
    Result := Result * a + PWordArray(Value)[i];
    a := a * 378551;
  end;
end;

{ TShaPerfectComparer }

function TShaPerfectComparer.GetHashCode(const Value: string): Integer;
var
  i, j: integer;
begin;
  Result:=0;
  for i:=0 to Length(Value)-1 do begin;
    j:=ord(Value[i+1]);
    Result:=Result * 1041204193 + (j+1507220783);
    end;
  Result:=Result * -1866451833;
end;

{ TFastComparer }

function TFastComparer.GetHashCode(const Value: string): Integer;
var
  i, X, Count: Integer;
  S: PByte;
begin
  S := Pointer(Value);
  Result := 0;
  if (S <> nil) then
  begin
    Count := PInteger(NativeInt(Value) - 4)^;

    Result := Count;
    for i := 1 to (Count shr 1) do
    begin
      X := Result xor PInteger(S)^;
      Result := Result * i;
      Inc(Result, X shr 16);
      Inc(S, SizeOf(Integer));
      Inc(Result, X);
    end;

    Inc(Result, Ord(PChar(S)^));
    Result := Result * -1866451833;
  end;
end;


const
  ITERATIONS_COUNT = 100;
  COMPARERS: array[0..2] of TComparerClass = (TSedgwicComparer, TShaPerfectComparer, TFastComparer);

var
  i, c, k: NativeInt;
  VALUES: TArray<string>;
  List: TStringList;
  Time: Cardinal;
  Comparer: IEqualityComparer<string>;
  Dictionary: TDictionary<string, Integer>;

begin
  try
    // загрузка значений
    List := TStringList.Create;
    try
      List.LoadFromFile('StringBase.txt');
      SetLength(VALUES, List.Count);

      for i := 0 to List.Count - 1 do
      begin
        VALUES[i] := List[i];
      end;
    finally
      List.Free;
    end;

    // цикл по всем компарерам
    for c := Low(COMPARERS) to High(COMPARERS) do
    begin
      // компарер, конструктор
      Write(COMPARERS[c].ClassName, '...');
      case c of
        0: Comparer := TSedgwicComparer.Create;
        1: Comparer := TShaPerfectComparer.Create;
      else
        // 2:
        Comparer := TFastComparer.Create;
       end;
      Dictionary := TDictionary<string, Integer>.Create(Length(VALUES), Comparer);

      try
        // заполняем
        for k := Low(VALUES) to High(VALUES) do
          Dictionary.Add(VALUES[k], 0);

        // тестируем
        Time := GetTickCount;
        for i := 1 to ITERATIONS_COUNT do
        for k := Low(VALUES) to High(VALUES) do
        begin
          Dictionary.Items[VALUES[k]];
        end;
        Time := GetTickCount - Time;
        Writeln(' ', Time, 'мс');

      finally
        Comparer := nil;
        Dictionary.Free;
      end;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  // нажать Enter
  Writeln;
  Write('Press Enter');
  Readln;
end.

 

  1 program HashTest;
  2 
  3 {$APPTYPE CONSOLE}
  4 
  5 {$R *.res}
  6 
  7 uses
  8   Winapi.Windows,
  9   System.SysUtils,
 10   System.Classes,
 11   System.Generics.Collections,
 12   System.Generics.Defaults;
 13 
 14 type
 15   TStringEqualComparer = class(TInterfacedObject)
 16     function Equals(const Left, Right: string): Boolean; reintroduce;
 17     // важно! если сделать виртуальный метод - то скорость падает ~3 раза
 18     // function GetHashCode(const Value: string): Integer; reintroduce; virtual; abstract;
 19   end;
 20 
 21   TSedgwicComparer = class(TStringEqualComparer, IEqualityComparer<string>)
 22     function GetHashCode(const Value: string): Integer; reintroduce;
 23   end;
 24 
 25   TSimpleComparer = class(TStringEqualComparer, IEqualityComparer<string>)
 26     function GetHashCode(const Value: string): Integer; reintroduce;
 27   end;
 28 
 29   TShiftComparer = class(TStringEqualComparer, IEqualityComparer<string>)
 30     function GetHashCode(const Value: string): Integer; reintroduce;
 31   end;
 32 
 33   TFastComparer = class(TStringEqualComparer, IEqualityComparer<string>)
 34     function GetHashCode(const Value: string): Integer; reintroduce;
 35   end;
 36 
 37   TMurmur3Comparer = class(TStringEqualComparer, IEqualityComparer<string>)
 38     function GetHashCode(const Value: string): Integer; reintroduce;
 39   end;
 40 
 41 { TStringEqualComparer }
 42 
 43 function TStringEqualComparer.Equals(const Left, Right: string): Boolean;
 44 label
 45   cmp_ptrs;
 46 var
 47   i, Count: NativeInt;
 48   S1, S2: PNativeUInt;
 49 begin
 50   S1 := Pointer(Left);
 51   S2 := Pointer(Right);
 52   if (S1 <> S2) and (S1 <> nil) and (S2 <> nil) then
 53   begin
 54     Dec(NativeUInt(S1), SizeOf(Integer));
 55     Dec(NativeUInt(S2), SizeOf(Integer));
 56     Count := PInteger(S1)^;
 57     if (Integer(Count) <> PInteger(S2)^) then goto cmp_ptrs;
 58     Inc(NativeUInt(S1), SizeOf(Integer));
 59     Inc(NativeUInt(S2), SizeOf(Integer));
 60 
 61     for i := 1 to Count shr {$ifdef CPUX64}2{$else}1{$endif} do
 62     begin
 63       if (S1^ <> S2^) then goto cmp_ptrs;
 64       Inc(S1);
 65       Inc(S2);
 66     end;
 67 
 68     {$ifdef CPUX64}
 69     if (Count and 2 <> 0) then
 70     begin
 71       if (PCardinal(S1)^ <> PCardinal(S2)^) then goto cmp_ptrs;
 72       Inc(NativeUInt(S1), SizeOf(Cardinal));
 73       Inc(NativeUInt(S2), SizeOf(Cardinal));
 74     end;
 75     {$endif}
 76 
 77     if (PWord(S1)^ <> PWord(S2)^) then goto cmp_ptrs;
 78     Result := True;
 79     Exit;
 80   end else
 81   begin
 82   cmp_ptrs:
 83     Result := (S1 = S2);
 84   end;
 85 end;
 86 
 87 { TSedgwicComparer }
 88 
 89 function TSedgwicComparer.GetHashCode(const Value: string): Integer;
 90 var
 91   a, i: Integer;
 92 begin
 93   Result := 0;
 94   a := 63689;
 95   for i := 1 to Length(Value) - 1 do
 96   begin
 97     Result := Result * a + PWordArray(Value)[i];
 98     a := a * 378551;
 99   end;
100 end;
101 
102 { TSimpleComparer }
103 
104 function TSimpleComparer.GetHashCode(const Value: string): Integer;
105 var
106   i: NativeInt;
107   S: PChar;
108 begin
109   S := Pointer(Value);
110   Result := 0;
111   if (S <> nil) then
112   begin
113     Result := PInteger(NativeInt(Value) - 4)^;
114 
115     for i := 0 to NativeInt(Result) - 1 do
116     begin
117       Result := Result + Ord(S[i]);
118     end;
119   end;
120 end;
121 
122 { TShiftComparer }
123 
124 function TShiftComparer.GetHashCode(const Value: string): Integer;
125 var
126   i: NativeInt;
127   S: PChar;
128 begin
129   S := Pointer(Value);
130   Result := 0;
131   if (S <> nil) then
132   begin
133     Result := PInteger(NativeInt(Value) - 4)^;
134 
135     for i := 0 to NativeInt(Result) - 1 do
136     begin
137       Result := Result xor (Result shr (i and 7 + 1)) + Ord(S[i]);
138     end;
139   end;
140 end;
141 
142 { TFastComparer }
143 
144 function TFastComparer.GetHashCode(const Value: string): Integer;
145 var
146   i, X, Count: Integer;
147   S: PByte;
148 begin
149   S := Pointer(Value);
150   Result := 0;
151   if (S <> nil) then
152   begin
153     Count := PInteger(NativeInt(Value) - 4)^;
154 
155     Result := Count;
156     for i := 1 to (Count shr (SizeOf(Char) xor 3)) do
157     begin
158       X := Result xor PInteger(S)^;
159       Result := Result * i;
160       Inc(Result, X shr 19);
161       Inc(S, SizeOf(Integer));
162       Inc(Result, X);
163     end;
164 
165     for i := 1 to (Count and ((SizeOf(Char) xor 2) or 1)) do
166     begin
167       Result := Result * i;
168       Inc(Result, Ord(PChar(S)^));
169       Inc(S, SizeOf(Char));
170     end;
171   end;
172 end;
173 
174 { TMurmur3Comparer }
175 
176 type
177   uint32_t = cardinal;
178 {$IFNDEF fpc}
179 function RolDWord(x:uint32_t;r:integer):uint32_t;
180 {$IF Defined(CPUX86)}
181 asm
182   MOV  CL, DL
183   ROL  EAX, CL
184 end;
185 {$ELSEIF Defined(CPUX64)}
186 asm
187   MOV  EAX, ECX
188   MOV  CL, DL
189   ROL  EAX, CL
190 end;
191 {$ELSE}
192 begin
193   Result:=(x shl r) or (x shr (32 - r));
194 end;
195 {$IFEND}
196 {$ENDIF}
197 
198 function MurmurHash3_x86_32 ( const Akey; len:uint32_t): cardinal;
199 type
200   Puint32_t=^uint32_t;
201 
202 var data:Puint32_t;
203   nblocks,tail_len,i:uint32_t;
204   h1,k1:uint32_t;
205 const
206    c1:uint32_t = $cc9e2d51;
207    c2:uint32_t = $1b873593;
208 type
209  TTailRec=packed record
210   case Integer of
211    1:(b:byte);
212    2:(w:word);
213    3:(uint24_d:uint32_t);
214  end;
215 var
216  tail:^TTailRec;
217 begin
218   data := @Akey;
219   nblocks := len div 4;
220 
221   h1 := 0;
222   //----------
223   // body
224   for i:=1 to nblocks do begin
225     k1 := data^;
226 
227     k1 := k1 * c1;
228     k1 := RolDWord(k1,15);
229     k1 := k1*c2;
230 
231     h1 := h1 xor k1;
232     h1 := RolDWord(h1,13);
233     h1 := h1*5+$e6546b64;
234 
235     inc(data);
236   end;
237 
238   //----------
239   // tail
240 
241   tail :=pointer(data);
242 
243   k1 := 0;
244   tail_len:=len and 3;
245   if tail_len>0 then begin
246     case tail_len of
247     3: k1 :=k1 xor (tail^.uint24_d and $FFFFFF);
248     2: k1 :=k1 xor tail^.w;
249     1: k1 :=k1 xor tail^.b;
250     end;
251 
252     k1 :=k1* c1;
253     k1 := RolDWord(k1,15);
254     k1 :=k1 * c2;
255     h1 :=h1 xor k1;
256   end;
257   //----------
258   // finalization
259 
260   h1 :=h1 xor len;
261 
262   //h1 := fmix32(h1);  Result := h1;
263 
264   h1 :=h1 xor (h1 shr 16);
265   h1 :=h1* $85ebca6b;
266   h1 :=h1 xor (h1 shr 13);
267   h1 :=h1 * $c2b2ae35;
268 
269   Result:=h1 xor (h1 shr 16);
270 end;
271 function TMurmur3Comparer.GetHashCode(const Value: string): Integer;
272 begin
273   Result := Integer(MurmurHash3_x86_32(PChar(Value)^, Length(Value) * SizeOf(Char)));
274 end;
275 
276 
277 
278 procedure TestDict(Title: string; L: TStringList; C: IEqualityComparer<string> = nil);
279 var
280   t1, t2: Cardinal;
281   D: TDictionary<string, Integer>;
282   i: Integer;
283 begin
284   Write(Title);
285   //
286   D := TDictionary<string, Integer>.Create(L.Count, C);
287   try
288    t1 := GetTickCount();
289     for i := L.Count - 1 downto 0 do
290       D.AddOrSetValue(L[i], i);
291 
292     t2 := GetTickCount();
293     Writeln(t2 - t1);
294   finally
295     D.Free;
296   end;
297   //
298 end;
299 
300 function GenStr(): string;
301 var
302   l: Integer;
303 begin
304   l := Random(1024);
305   SetLength(Result, l);
306   for l := l downto 1 do
307   begin
308     Result[l] := Char(Random(256));
309   end;
310 end;
311 
312 procedure GenList(L: TStringList);
313 var
314   i: Integer;
315 begin
316   RandSeed := 0;
317   for i := 1 to 1000000 do
318     L.Add(GenStr)
319 end;
320 
321 var
322   L: TStringList;
323 
324 begin
325   try
326     L:= TStringList.Create;
327     try
328       GenList(L);
329       TestDict( 'std: ', L);
330 
331       TestDict( 'TSedgwicComparer: ', L, TSedgwicComparer.Create);
332       TestDict( 'TMurmur3Comparer: ', L, TMurmur3Comparer.Create);
333       TestDict( 'TFastComparer: ', L, TFastComparer.Create);
334       TestDict( 'TShiftComparer: ', L, TShiftComparer.Create);
335       TestDict( 'TSimpleComparer: ', L, TSimpleComparer.Create);
336     finally
337       L.Free;
338     end;
339     { TODO -oUser -cConsole Main : Insert code here }
340   except
341     on E: Exception do
342       Writeln(E.ClassName, ': ', E.Message);
343   end;
344 end.
View Code
program SpeedTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Winapi.Windows,
  System.Generics.Defaults,
  System.Generics.Collections,
  System.SysUtils,
  System.Classes;

type
  TStringEqualComparer = class(TInterfacedObject)
    function Equals(const Left, Right: string): Boolean; reintroduce;
    // важно! если сделать виртуальный метод - то скорость падает ~3 раза
    // function GetHashCode(const Value: string): Integer; reintroduce; virtual; abstract;
  end;
  TComparerClass = class of TStringEqualComparer;

  TSedgwicComparer = class(TStringEqualComparer, IEqualityComparer<string>)
    function GetHashCode(const Value: string): Integer; reintroduce;
  end;

  TSimpleComparer = class(TStringEqualComparer, IEqualityComparer<string>)
    function GetHashCode(const Value: string): Integer; reintroduce;
  end;

  TShiftComparer = class(TStringEqualComparer, IEqualityComparer<string>)
    function GetHashCode(const Value: string): Integer; reintroduce;
  end;

  TFastComparer = class(TStringEqualComparer, IEqualityComparer<string>)
    function GetHashCode(const Value: string): Integer; reintroduce;
  end;

{ TStringEqualComparer }

function TStringEqualComparer.Equals(const Left, Right: string): Boolean;
label
  cmp_ptrs;
var
  i, Count: NativeInt;
  S1, S2: PNativeUInt;
begin
  S1 := Pointer(Left);
  S2 := Pointer(Right);
  if (S1 <> S2) and (S1 <> nil) and (S2 <> nil) then
  begin
    Dec(NativeUInt(S1), SizeOf(Integer));
    Dec(NativeUInt(S2), SizeOf(Integer));
    Count := PInteger(S1)^;
    if (Integer(Count) <> PInteger(S2)^) then goto cmp_ptrs;
    Inc(NativeUInt(S1), SizeOf(Integer));
    Inc(NativeUInt(S2), SizeOf(Integer));

    for i := 1 to Count shr {$if SizeOf(NativeUInt) = 8}2{$else}1{$endif} do
    begin
      if (S1^ <> S2^) then goto cmp_ptrs;
      Inc(S1);
      Inc(S2);
    end;

    {$if SizeOf(NativeUInt) = 8}
    if (Count and 2 <> 0) then
    begin
      if (PCardinal(S1)^ <> PCardinal(S2)^) then goto cmp_ptrs;
      Inc(NativeUInt(S1), SizeOf(Cardinal));
      Inc(NativeUInt(S2), SizeOf(Cardinal));
    end;
    {$endif}

    if (PWord(S1)^ <> PWord(S2)^) then goto cmp_ptrs;
    Result := True;
    Exit;
  end else
  begin
  cmp_ptrs:
    Result := (S1 = S2);
  end;
end;

{ TSedgwicComparer }

function TSedgwicComparer.GetHashCode(const Value: string): Integer;
var
  a, i: Integer;
begin
  Result := 0;
  a := 63689;
  for i := 1 to Length(Value) - 1 do
  begin
    Result := Result * a + PWordArray(Value)[i];
    a := a * 378551;
  end;
end;

{ TSimpleComparer }

function TSimpleComparer.GetHashCode(const Value: string): Integer;
var
  i: NativeInt;
  S: PChar;
begin
  S := Pointer(Value);
  Result := 0;
  if (S <> nil) then
  begin
    Result := PInteger(NativeInt(Value) - 4)^;

    for i := 0 to NativeInt(Result) - 1 do
    begin
      Result := Result + Ord(S[i]);
    end;
  end;
end;

{ TShiftComparer }

function TShiftComparer.GetHashCode(const Value: string): Integer;
var
  i: NativeInt;
  S: PChar;
begin
  S := Pointer(Value);
  Result := 0;
  if (S <> nil) then
  begin
    Result := PInteger(NativeInt(Value) - 4)^;

    for i := 0 to NativeInt(Result) - 1 do
    begin
      Result := Result xor (Result shr (i and 7 + 1)) + Ord(S[i]);
    end;
  end;
end;

{ TFastComparer }

function TFastComparer.GetHashCode(const Value: string): Integer;
var
  i, X, Count: Integer;
  S: PByte;
begin
  S := Pointer(Value);
  Result := 0;
  if (S <> nil) then
  begin
    Count := PInteger(NativeInt(Value) - 4)^;

    Result := Count;
    for i := 1 to (Count shr (SizeOf(Char) xor 3)) do
    begin
      X := Result xor PInteger(S)^;
      Result := Result * i;
      Inc(Result, X shr 19);
      Inc(S, SizeOf(Integer));
      Inc(Result, X);
    end;

    for i := 1 to (Count and ((SizeOf(Char) xor 2) or 1)) do
    begin
      Result := Result * i;
      Inc(Result, Ord(PChar(S)^));
      Inc(S, SizeOf(Char));
    end;
  end;
end;


const
  ITERATIONS_COUNT = 1000000;
  COMPARERS: array[0..4] of TComparerClass = (nil, TSedgwicComparer, TSimpleComparer, TShiftComparer, TFastComparer);

var
  i, c, k: NativeInt;
  VALUES: TArray<string>;
  Time: Cardinal;
  Dictionary: TDictionary<string, Integer>;

begin
  VALUES := [
    'div',
    'span',
    'table',
    'td',
    'tr',
    'th',
    'li',
    'meta',
    'script',
    'style',
    'ol',
    'ul',
    'h1',
    'h2',
    'h3',
    'h4',
    'h5',
    'thead',
    'tbody',
    'html',
    'body',
    'blockquote',
    'address',
    'frame',
    'frameset',
    'pre',
    'button',
    'textarea',
    'strike',
    'del',
    'menu',
    'small',
    'sub',
    'sup',
    'img',
    'fieldset',
    'legend'
  ];

  try
    // цикл по всем компарерам
    for c := Low(COMPARERS) to High(COMPARERS) do
    begin
      // конструктор, инфо
      if (COMPARERS[c] = nil) then
      begin
        Write('Default', '...');
        Dictionary := TDictionary<string, Integer>.Create;
      end else
      begin
        Write(COMPARERS[c].ClassName, '...');
        case c of
          1: Dictionary := TDictionary<string, Integer>.Create(TSedgwicComparer.Create);
          2: Dictionary := TDictionary<string, Integer>.Create(TSimpleComparer.Create);
          3: Dictionary := TDictionary<string, Integer>.Create(TShiftComparer.Create);
        else
          // 4:
          Dictionary := TDictionary<string, Integer>.Create(TFastComparer.Create);
        end;
      end;

      try
        // заполняем
        for k := Low(VALUES) to High(VALUES) do
          Dictionary.Add(VALUES[k], 0);

        // тестируем
        Time := GetTickCount;
        for i := 1 to ITERATIONS_COUNT do
        for k := Low(VALUES) to High(VALUES) do
        begin
          Dictionary.Items[VALUES[k]];
        end;
        Time := GetTickCount - Time;
        Writeln(' ', Time, 'мс');

      finally
        Dictionary.Free;
      end;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  // нажать Enter
  Writeln;
  Write('Press Enter');
  Readln;
end.

 

function StringHash(const Value: string): Integer;
var
  i: Integer;
  S: PChar;
begin
  S := Pointer(Value);
  Result := 0;
  if (S <> nil) then
  begin
    Result := PInteger(NativeInt(Value) - 4)^;

    for i := 1 to Result do
    begin
      Result := Result * i + Ord(S^);
      Inc(S);
    end;
  end;
end;

 

program Hash;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Winapi.Windows,
  System.Generics.Collections,
  System.SysUtils,
  System.Classes;

const
  KEYS_COUNT = 1000000;

var
  i: NativeInt;
  HashValue: Integer;
  Keys: TArray<string>;
  Dictionary: TDictionary<Integer, Integer>;

function StringHash(const Value: string): Integer;
var
  i, X, Count: Integer;
  S: PByte;
begin
  S := Pointer(Value);
  Result := 0;
  if (S <> nil) then
  begin
    Count := PInteger(NativeInt(Value) - 4)^;

    Result := Count;
    for i := 1 to (Count shr (SizeOf(Char) xor 3)) do
    begin
      X := Result xor PInteger(S)^;
      Result := Result * i;
      Inc(Result, X shr 19);
      Inc(S, SizeOf(Integer));
      Inc(Result, X);
    end;

    for i := 1 to (Count and ((SizeOf(Char) xor 2) or 1)) do
    begin
      Result := Result * i;
      Inc(Result, Ord(PChar(S)^));
      Inc(S, SizeOf(Char));
    end;
  end;
end;

function GetHash(const Key: string): Integer;
var
  a: Integer;
  i: Integer;
begin
  Result := 0;
  a := 63689;
  for i := 1 to Length(Key) - 1 do
  begin
    Result := Result * a + PWordArray(Key)[i];
    a := a * 378551;
  end;
end;

begin
  try
    // заполнение
    SetLength(Keys, KEYS_COUNT);
    for i := Low(Keys) to High(Keys) do
      Keys[i] := TGuid.NewGuid.ToString;

    // уникальные
    Dictionary := TDictionary<Integer, Integer>.Create;
    try
      for i := Low(Keys) to High(Keys) do
      begin
        HashValue := StringHash{GetHash}(Keys[i]) and (1024 * 1024 - 1);
        Dictionary.AddOrSetValue(HashValue, 0);
      end;

      Writeln('Уникальных: ', Dictionary.Count, ', коллизий: ', KEYS_COUNT - Dictionary.Count);
    finally
      Dictionary.Free
    end;

    // нажать Enter
    Write('Press Enter');
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
View Code
function StringHash(const Value: string): Integer;
var
  i, X, Count: Integer;
  S: PByte;
begin
  S := Pointer(Value);
  Result := 0;
  if (S <> nil) then
  begin
    Count := PInteger(NativeInt(Value) - 4)^;

    Result := Count;
    for i := 1 to (Count shr (SizeOf(Char) xor 3)) do
    begin
      X := Result xor PInteger(S)^;
      Result := Result * i;
      Inc(Result, X shr 19);
      Inc(S, SizeOf(Integer));
      Inc(Result, X);
    end;

    for i := 1 to (Count and ((SizeOf(Char) xor 2) or 1)) do
    begin
      Result := Result * i;
      Inc(Result, Ord(PChar(S)^));
      Inc(S, SizeOf(Char));
    end;
  end;
end;
View Code
type
 TStringComparer = Class(TEqualityComparer<String>)
   function Equals(const Left, Right: String): Boolean; Override;
   function GetHashCode(const Value: String): Integer;Override;
 End;

{ TStringComparer }

function TStringComparer.Equals(const Left, Right: String): Boolean;
begin
 result := Left = Right;
end;

function TStringComparer.GetHashCode(const Value: String): Integer;
begin
 result := StringHash(value);
end;
View Code
{$OverFlowChecks OFF}
function GetHash(const Key: String): integer;
var
  a : Integer;
  i : Integer;
begin
  Result:=0;
  a:=63689;
  for i:=1 To Length(Key)-1 do begin
    Result:=Result*a+PWordArray(Key)[i];
    a:=a*378551;
  end;
end;
View Code
function StringHash(const Value: string): Integer;
var
  i: NativeInt;
  S: PChar;
begin
  S := Pointer(Value);
  Result := 0;
  if (S <> nil) then
  begin
    Result := PInteger(NativeInt(Value) - 4)^;

    for i := 0 to NativeInt(Result) - 1 do
    begin
      Result := Result + Ord(S[i]);
    end;
  end;
end;
View Code
function StringHash(const Value: string): Integer;
var
  i: NativeInt;
  S: PChar;
begin
  S := Pointer(Value);
  Result := 0;
  if (S <> nil) then
  begin
    Result := PInteger(NativeInt(Value) - 4)^;

    for i := 0 to NativeInt(Result) - 1 do
    begin
      Result := Result xor (Result shr (i and 7)) + Ord(S[i]);
    end;
  end;
end;
View Code
{$OverFlowChecks OFF}
function GetHash(const Key: String): integer;
var
  a : Integer;
  i : Integer;
begin
  Result:=0;
  a:=63689;
  for i:=1 To Length(Key)-1 do begin
    Result:=Result*a+PWordArray(Key)[i];
    a:=a*378551;
  end;
end;
View Code
program Test;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Winapi.Windows,
  System.Generics.Collections,
  System.SysUtils,
  System.Classes;

const
  ITERATIONS_COUNT = 1000000 div 10;

var
  i, k: NativeInt;
  VALUES: TArray<string>;
  Time: Cardinal;
  StringList: TStringList;
  Dictionary: TDictionary<string, Integer>;

type
  THtmlTag = (tagUnknown, tagAddress, tagBlockquote, tagBody, tagButton, tagDel, tagDiv,
    tagFieldset, tagFrame, tagFrameset, tagH1, tagH2, tagH3, tagH4, tagH5, tagHtml,
    tagImg, tagLegend, tagLi, tagMenu, tagMeta, tagOl, tagPre, tagScript, tagSmall,
    tagSpan, tagStrike, tagStyle, tagSub, tagSup, tagTable, tagTbody, tagTd, tagTextarea,
    tagTh, tagThead, tagTr, tagUl);

function StrToHtmlTag(const S: UnicodeString): THtmlTag;
type
  HugeByteArray = array[0..High(Integer) div SizeOf(Byte) - 1] of Byte;
  HugeWordArray = array[0..High(Integer) div SizeOf(Word) - 1] of Word;
  HugeCardinalArray = array[0..High(Integer) div SizeOf(Cardinal) - 1] of Cardinal;
  HugeNativeUIntArray = array[0..High(Integer) div SizeOf(NativeUInt) - 1] of NativeUInt;

  PMemoryItems = ^TMemoryItems;
  TMemoryItems = packed record
  case Integer of
    0: (Bytes: HugeByteArray);
    1: (Words: HugeWordArray);
    2: (Cardinals: HugeCardinalArray);
    3: (NativeUInts: HugeNativeUIntArray);
    4: (A1: array[1..1] of Byte;
        case Integer of
          0: (Words1: HugeWordArray);
          1: (Cardinals1: HugeCardinalArray);
          2: (NativeUInts1: HugeNativeUIntArray);
        );
    5: (A2: array[1..2] of Byte;
        case Integer of
          0: (Cardinals2: HugeCardinalArray);
          1: (NativeUInts2: HugeNativeUIntArray);
        );
    6: (A3: array[1..3] of Byte;
        case Integer of
          0: (Cardinals3: HugeCardinalArray);
          1: (NativeUInts3: HugeNativeUIntArray);
        );
  end;
var
  Len: Integer;
begin
  // default value
  Result := tagUnknown;
  if (Pointer(S) = nil) then Exit;
  Len := PInteger(NativeInt(S) - 4)^;

  // utf16 ascii
  with PMemoryItems(S)^ do
  if (Len >= 2) then
  case (Words[0]) of // "address", "blockquote", "body", "button", "del", "div", ...
    $0061: if (Len = 7) and (Cardinals2[0] = $00640064) and (Cardinals2[1] = $00650072) and
           (Cardinals2[2] = $00730073) then Result := tagAddress; // "address"
    $0062: case Len of // "body", "button", "blockquote"
             4: if (Cardinals2[0] = $0064006F) and (Words[3] = $0079) then
                Result := tagBody; // "body"
             6: if (Cardinals2[0] = $00740075) and (Cardinals2[1] = $006F0074) and
                (Words[5] = $006E) then Result := tagButton; // "button"
             10: if (Cardinals2[0] = $006F006C) and (Cardinals2[1] = $006B0063) and
                 (Cardinals2[2] = $00750071) and (Cardinals2[3] = $0074006F) and
                 (Words[9] = $0065) then Result := tagBlockquote; // "blockquote"
           end;
    $0064: if (Len = 3) then
           case (Cardinals2[0]) of // "del", "div"
             $006C0065: Result := tagDel; // "del"
             $00760069: Result := tagDiv; // "div"
           end;
    $0066: case Len of // "frame", "fieldset", "frameset"
             5: if (Cardinals2[0] = $00610072) and (Cardinals2[1] = $0065006D) then
                Result := tagFrame; // "frame"
             8: case (Cardinals2[0]) of // "fieldset", "frameset"
                  $00650069: if (Cardinals2[1] = $0064006C) and (Cardinals2[2] = $00650073) and
                             (Words[7] = $0074) then Result := tagFieldset; // "fieldset"
                  $00610072: if (Cardinals2[1] = $0065006D) and (Cardinals2[2] = $00650073) and
                             (Words[7] = $0074) then Result := tagFrameset; // "frameset"
                end;
           end;
    $0068: case Len of // "h1", "h2", "h3", "h4", "h5", "html"
             2: case (Words[1]) of // "h1", "h2", "h3", "h4", "h5"
                  $0031: Result := tagH1; // "h1"
                  $0032: Result := tagH2; // "h2"
                  $0033: Result := tagH3; // "h3"
                  $0034: Result := tagH4; // "h4"
                  $0035: Result := tagH5; // "h5"
                end;
             4: if (Cardinals2[0] = $006D0074) and (Words[3] = $006C) then
                Result := tagHtml; // "html"
           end;
    $0069: if (Len = 3) and (Cardinals2[0] = $0067006D) then Result := tagImg; // "img"
    $006C: case Len of // "li", "legend"
             2: if (Words[1] = $0069) then Result := tagLi; // "li"
             6: if (Cardinals2[0] = $00670065) and (Cardinals2[1] = $006E0065) and
                (Words[5] = $0064) then Result := tagLegend; // "legend"
           end;
    $006D: if (Len = 4) then
           if (Words[1] = $0065) then
           case (Cardinals[1]) of // "menu", "meta"
             $0075006E: Result := tagMenu; // "menu"
             $00610074: Result := tagMeta; // "meta"
           end;
    $006F: if (Len = 2) and (Words[1] = $006C) then Result := tagOl; // "ol"
    $0070: if (Len = 3) and (Cardinals2[0] = $00650072) then Result := tagPre; // "pre"
    $0073: case Len of // "sub", "sup", "span", "small", "style", "script", "strike"
             3: if (Words[1] = $0075) then
                case (Words[2]) of // "sub", "sup"
                  $0062: Result := tagSub; // "sub"
                  $0070: Result := tagSup; // "sup"
                end;
             4: if (Cardinals2[0] = $00610070) and (Words[3] = $006E) then
                Result := tagSpan; // "span"
             5: case (Cardinals2[0]) of // "small", "style"
                  $0061006D: if (Cardinals2[1] = $006C006C) then Result := tagSmall; // "small"
                  $00790074: if (Cardinals2[1] = $0065006C) then Result := tagStyle; // "style"
                end;
             6: case (Cardinals2[0]) of // "script", "strike"
                  $00720063: if (Cardinals2[1] = $00700069) and (Words[5] = $0074) then
                             Result := tagScript; // "script"
                  $00720074: if (Cardinals2[1] = $006B0069) and (Words[5] = $0065) then
                             Result := tagStrike; // "strike"
                end;
           end;
    $0074: case Len of // "td", "th", "tr", "table", "tbody", "thead", "textarea"
             2: case (Words[1]) of // "td", "th", "tr"
                  $0064: Result := tagTd; // "td"
                  $0068: Result := tagTh; // "th"
                  $0072: Result := tagTr; // "tr"
                end;
             5: case (Cardinals2[0]) of // "table", "tbody", "thead"
                  $00620061: if (Cardinals2[1] = $0065006C) then Result := tagTable; // "table"
                  $006F0062: if (Cardinals2[1] = $00790064) then Result := tagTbody; // "tbody"
                  $00650068: if (Cardinals2[1] = $00640061) then Result := tagThead; // "thead"
                end;
             8: if (Cardinals2[0] = $00780065) and (Cardinals2[1] = $00610074) and
                (Cardinals2[2] = $00650072) and (Words[7] = $0061) then
                Result := tagTextarea; // "textarea"
           end;
    $0075: if (Len = 2) and (Words[1] = $006C) then Result := tagUl; // "ul"
  end;
end;

begin
  VALUES := [
    'div',
    'span',
    'table',
    'td',
    'tr',
    'th',
    'li',
    'meta',
    'script',
    'style',
    'ol',
    'ul',
    'h1',
    'h2',
    'h3',
    'h4',
    'h5',
    'thead',
    'tbody',
    'html',
    'body',
    'blockquote',
    'address',
    'frame',
    'frameset',
    'pre',
    'button',
    'textarea',
    'strike',
    'del',
    'menu',
    'small',
    'sub',
    'sup',
    'img',
    'fieldset',
    'legend'
  ];

  try
    // бинарный поиск
    begin
      Write('Бинарный поиск(TStringList)...');
      StringList := TStringList.Create;
      try
        for k := Low(VALUES) to High(VALUES) do StringList.Add(VALUES[k]);
        StringList.Sorted := True;

        Time := GetTickCount;
        for i := 1 to ITERATIONS_COUNT do
        for k := Low(VALUES) to High(VALUES) do
        begin
          StringList.IndexOf(VALUES[k]);
        end;
        Time := GetTickCount - Time;
        Writeln(' ', Time, 'мс');
      finally
        StringList.Free;
      end;
    end;

    // хеш
    begin
      Write('Хеш поиск(Dictionary)...');
      Dictionary := TDictionary<string, Integer>.Create;
      try
        for k := Low(VALUES) to High(VALUES) do
          Dictionary.Add(VALUES[k], 0);

        Time := GetTickCount;
        for i := 1 to ITERATIONS_COUNT do
        for k := Low(VALUES) to High(VALUES) do
        begin
          Dictionary.Items[VALUES[k]];
        end;
        Time := GetTickCount - Time;
        Writeln(' ', Time, 'мс');
      finally
        Dictionary.Free;
      end;
    end;

    // кодогенерация
    begin
      Write('Кодогенерация(CachedSerializer)...');

      Time := GetTickCount;
      for i := 1 to ITERATIONS_COUNT do
      for k := Low(VALUES) to High(VALUES) do
      begin
        StrToHtmlTag(VALUES[k]);
      end;
      Time := GetTickCount - Time;
      Writeln(' ', Time, 'мс');
    end;

    // нажать Enter
    Writeln;
    Write('Press Enter');
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
View Code
function StringHash(const Value: string): Integer;
var
  i: NativeInt;
  S: PChar;
begin
  S := Pointer(Value);
  Result := 0;
  if (S <> nil) then
  begin
    Result := PInteger(NativeInt(Value) - 4)^;

    for i := 0 to NativeInt(Result) - 1 do
    begin
      Result := Result xor (Result shr (i and 7 + 1)) + Ord(S[i]);
    end;
  end;
end;
View Code
procedure TForm23.Button1Click(Sender: TObject);
var i, k, T: integer;
    L: TStringList;
    s: string;
    D: TDictionary<string, TObject>;
    //ST: THtStringTrie;
    O: TObject;
begin
  L:=TStringList.Create;
  L.Add('div');
  L.Add('span');
  L.Add('table');
  L.Add('td');
  L.Add('tr');
  L.Add('th');
  L.Add('li');
  L.Add('meta');
  L.Add('script');
  L.Add('style');
  L.Add('ol');
  L.Add('ul');
  L.Add('h1');
  L.Add('h2');
  L.Add('h3');
  L.Add('h4');
  L.Add('h5');
  L.Add('thead');
  L.Add('tbody');
  L.Add('html');
  L.Add('body');
  L.Add('blockquote');
  L.Add('address');
  L.Add('frame');
  L.Add('frameset');
  L.Add('pre');
  L.Add('button');
  L.Add('textarea');
  L.Add('strike');
  L.Add('del');
  L.Add('menu');
  L.Add('small');
  L.Add('sub');
  L.Add('sup');
  L.Add('img');
  L.Add('fieldset');
  L.Add('legend');
  D := TDictionary<string, TObject>.Create;
  for i := 0 to L.Count - 1 do
    D.Add(L[i], TObject.Create);
  //ST := THtStringTrie.Create();
  //for i := 0 to L.Count - 1 do
  //  ST.Add(L[i], TObject.Create);
  T := GetTickCount;
  for i := 1 to 1000000 do
  begin
    for k := 0 to L.Count -1 do
      D.TryGetValue(L[k], O)
      //ST.Find(L[k], O);
  end;
  Caption := inttostr(GetTickCount - T);
end;
View Code
{$OverFlowChecks OFF}
function GetHash(const Key: String): integer;
var
  a : Integer;
  i : Integer;
begin
  Result:=0;
  a:=63689;
  for i:=1 To Length(Key)-1 do begin
    Result:=Result*a+PWordArray(Key)[i];
    a:=a*378551;
  end;
end;
View Code
function CalcStrCRC32(const s: string): cardinal;
var p: pchar;
begin
  Result := $FFFFFFFF;
  p := pointer(s);
  if p <> nil then
    while p^ <> #0 do
    begin
      Result := (((Result shr 8) and $00FFFFFF) xor (Ccitt32Table[(Result xor byte(p^)) and $FF]));
      inc(p);
    end;
end;
View Code
procedure TMain.Button3Click(Sender: TObject);
const
  CRC32_POLYNOMIAL = $EDB88320;
var
  i, k, T      : integer;
  L            : TStringList;
  Ccitt32Table : array[0..255] of longint;

  function crc32(crc : longint; const c : byte) : longint;
  begin
    crc32 := (((crc shr 8) and $00FFFFFF) xor (Ccitt32Table[(crc xor c) and $FF]));
  end;

  procedure BuildCRCTable;
  var
  i, j, value : DWORD;
  begin
  for i := 0 to 255 do
  begin
  value := i;
  for j := 8 downto 1 do
  begin
  if ((value and 1) <> 0) then
  value := (value shr 1) xor CRC32_POLYNOMIAL
  else
  value := value shr 1;
  end;
  Ccitt32Table[i] := value;
  end
  end;


function GetHash1(const Key: String): integer;
var p: pchar;
begin
  Result := $FFFFFFFF;
  p := pointer(Key);
  if p <> nil then
    while p^ <> #0 do
    begin
      Result := (((Result shr 8) and $00FFFFFF) xor (Ccitt32Table[(Result xor byte(p^)) and $FF]));
      inc(p);
    end;
end;

{$OverFlowChecks OFF}
function GetHash2(const Key: String): integer;
var
  a : Integer;
  i : Integer;
begin
  Result:=0;
  a:=63689;
  for i:=1 To Length(Key)-1 do begin
    Result:=Result*a+PWordArray(Key)[i];
    a:=a*378551;
  end;
end;
{$OverFlowChecks On}


begin
  BuildCRCTable;

  L:=TStringList.Create;
  L.Add('div');
  L.Add('span');
  L.Add('table');
  L.Add('td');
  L.Add('tr');
  L.Add('th');
  L.Add('li');
  L.Add('meta');
  L.Add('script');
  L.Add('style');
  L.Add('ol');
  L.Add('ul');
  L.Add('h1');
  L.Add('h2');
  L.Add('h3');
  L.Add('h4');
  L.Add('h5');
  L.Add('thead');
  L.Add('tbody');
  L.Add('html');
  L.Add('body');
  L.Add('blockquote');
  L.Add('address');
  L.Add('frame');
  L.Add('frameset');
  L.Add('pre');
  L.Add('button');
  L.Add('textarea');
  L.Add('strike');
  L.Add('del');
  L.Add('menu');
  L.Add('small');
  L.Add('sub');
  L.Add('sup');
  L.Add('img');
  L.Add('fieldset');
  L.Add('legend');

  T := GetTickCount;
  for i := 1 to 1000000 do
  begin
    for k := 0 to L.Count -1 do
      GetHash1(L[k]);
  end;
  Edit1.Text := inttostr(GetTickCount - T);

  T := GetTickCount;
  for i := 1 to 1000000 do
  begin
    for k := 0 to L.Count -1 do
      GetHash2(L[k]);
  end;
  Edit2.Text := inttostr(GetTickCount - T);
end;
View Code
var
  stream: TBytesStream;
  s1: string;
  s2: string;
begin
  stream := TBytesStream.Create;

  stream.WriteData(TEncoding.ANSI.GetBytes('test'), 4);
  s1 := TEncoding.ANSI.GetString(stream.Bytes); // заполнит 8192 символов вместо 4
  s2 := TEncoding.ANSI.GetString(stream.Bytes, 0, 4);

  ShowMessageFmt('stream: %d' + #13#10 + 's1: %d' + #13#10 + 's2: %d', [stream.Size, s1.Length, s2.Length]);
  FreeAndNil(stream);
end;
View Code

 

转载于:https://www.cnblogs.com/marklove/p/9746485.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值