{ ****************************************************************************** } { * Status IO writen by QQ 600585@qq.com * } { * https://zpascal.net * } { * https://github.com/PassByYou888/zAI * } { * https://github.com/PassByYou888/ZServer4D * } { * https://github.com/PassByYou888/PascalString * } { * https://github.com/PassByYou888/zRasterization * } { * https://github.com/PassByYou888/CoreCipher * } { * https://github.com/PassByYou888/zSound * } { * https://github.com/PassByYou888/zChinese * } { * https://github.com/PassByYou888/zExpression * } { * https://github.com/PassByYou888/zGameWare * } { * https://github.com/PassByYou888/zAnalysis * } { * https://github.com/PassByYou888/FFMPEG-Header * } { * https://github.com/PassByYou888/zTranslate * } { * https://github.com/PassByYou888/InfiniteIoT * } { * https://github.com/PassByYou888/FastMD5 * } { ****************************************************************************** } unit DoStatusIO; {$INCLUDE zDefine.inc} interface uses {$IFNDEF FPC} {$IF Defined(WIN32) or Defined(WIN64)} Windows, {$ELSEIF not Defined(Linux)} FMX.Types, {$IFEND} {$IFEND FPC} SysUtils, Classes, SyncObjs, {$IFDEF FPC} FPCGenericStructlist, fgl, {$ELSE FPC} System.Generics.Collections, {$ENDIF FPC} PascalStrings, UPascalStrings, UnicodeMixedLib, CoreClasses; type {$IFDEF FPC} TDoStatusProc = procedure(Text_: SystemString; const ID: Integer) is nested; {$ELSE FPC} TDoStatusProc = reference to procedure(Text_: SystemString; const ID: Integer); {$ENDIF FPC} TDoStatusMethod = procedure(Text_: SystemString; const ID: Integer) of object; TDoStatusCall = procedure(Text_: SystemString; const ID: Integer); procedure AddDoStatusHook(TokenObj: TCoreClassObject; CallProc: TDoStatusMethod); procedure AddDoStatusHookM(TokenObj: TCoreClassObject; CallProc: TDoStatusMethod); procedure AddDoStatusHookC(TokenObj: TCoreClassObject; CallProc: TDoStatusCall); procedure AddDoStatusHookP(TokenObj: TCoreClassObject; CallProc: TDoStatusProc); procedure DeleteDoStatusHook(TokenObj: TCoreClassObject); procedure DisableStatus; procedure EnabledStatus; procedure DoStatus(Text_: SystemString; const ID: Integer); overload; procedure DoStatus(const v: Pointer; siz, width: NativeInt); overload; procedure DoStatus(prefix: SystemString; v: Pointer; siz, width: NativeInt); overload; procedure DoStatus(const v: TCoreClassStrings); overload; procedure DoStatus(const v: Int64); overload; procedure DoStatus(const v: Integer); overload; procedure DoStatus(const v: Single); overload; procedure DoStatus(const v: Double); overload; procedure DoStatus(const v: Pointer); overload; procedure DoStatus(const v: SystemString; const Args: array of const); overload; procedure DoError(v: SystemString; const Args: array of const); overload; procedure DoStatus(const v: SystemString); overload; procedure DoStatus(const v: TPascalString); overload; procedure DoStatus(const v: TUPascalString); overload; procedure DoStatus(const v: TMD5); overload; procedure DoStatus; overload; procedure DoStatusNoLn(const v: TPascalString); overload; procedure DoStatusNoLn(const v: SystemString; const Args: array of const); overload; procedure DoStatusNoLn; overload; function StrInfo(s: TPascalString): string; overload; function StrInfo(s: TUPascalString): string; overload; function BytesInfo(s: TBytes): string; overload; var LastDoStatus: SystemString; IDEOutput: Boolean; ConsoleOutput: Boolean; OnDoStatusHook: TDoStatusCall; implementation procedure bufHashToString(hash: Pointer; Size: NativeInt; var output: TPascalString); const HexArr: array [0 .. 15] of SystemChar = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); var i: Integer; begin output.Len := Size * 2; for i := 0 to Size - 1 do begin output.buff[i * 2] := HexArr[(PByte(nativeUInt(hash) + i)^ shr 4) and $0F]; output.buff[i * 2 + 1] := HexArr[PByte(nativeUInt(hash) + i)^ and $0F]; end; end; procedure DoStatus(Text_: SystemString; const ID: Integer); begin try OnDoStatusHook(Text_, ID); except end; end; procedure DoStatus(const v: Pointer; siz, width: NativeInt); var s: TPascalString; i: Integer; n: SystemString; begin bufHashToString(v, siz, s); n := ''; for i := 1 to s.Len div 2 do begin if n <> '' then n := n + #32 + s[i * 2 - 1] + s[i * 2] else n := s[i * 2 - 1] + s[i * 2]; if i mod (width div 2) = 0 then begin DoStatus(n); n := ''; end; end; if n <> '' then DoStatus(n); end; procedure DoStatus(prefix: SystemString; v: Pointer; siz, width: NativeInt); var s: TPascalString; i: Integer; n: SystemString; begin bufHashToString(v, siz, s); n := ''; for i := 1 to s.Len div 2 do begin if n <> '' then n := n + #32 + s[i * 2 - 1] + s[i * 2] else n := s[i * 2 - 1] + s[i * 2]; if i mod (width div 2) = 0 then begin DoStatus(prefix + n); n := ''; end; end; if n <> '' then DoStatus(prefix + n); end; procedure DoStatus(const v: TCoreClassStrings); var i: Integer; o: TCoreClassObject; begin for i := 0 to v.Count - 1 do begin o := v.Objects[i]; if o <> nil then DoStatus('%s<%s>', [v[i], o.ClassName]) else DoStatus(v[i]); end; end; procedure DoStatus(const v: Int64); begin DoStatus(IntToStr(v)); end; procedure DoStatus(const v: Integer); begin DoStatus(IntToStr(v)); end; procedure DoStatus(const v: Single); begin DoStatus(FloatToStr(v)); end; procedure DoStatus(const v: Double); begin DoStatus(FloatToStr(v)); end; procedure DoStatus(const v: Pointer); begin DoStatus(Format('0x%p', [v])); end; procedure DoStatus(const v: SystemString; const Args: array of const); begin DoStatus(Format(v, Args)); end; procedure DoError(v: SystemString; const Args: array of const); begin DoStatus(Format(v, Args), 2); end; procedure DoStatus(const v: SystemString); begin DoStatus(v, 0); end; procedure DoStatus(const v: TPascalString); begin DoStatus(v.Text, 0); end; procedure DoStatus(const v: TUPascalString); begin DoStatus(v.Text, 0); end; procedure DoStatus(const v: TMD5); begin DoStatus(umlMD5ToString(v).Text); end; type TStatusProcStruct = record TokenObj: TCoreClassObject; OnStatusM: TDoStatusMethod; OnStatusC: TDoStatusCall; OnStatusP: TDoStatusProc; end; PStatusProcStruct = ^TStatusProcStruct; TStatusStruct = record s: SystemString; th: TCoreClassThread; TriggerTime: TTimeTick; end; PStatusStruct = ^TStatusStruct; TStatusNoLnStruct = record s: TPascalString; th: TCoreClassThread; TriggerTime: TTimeTick; end; PStatusNoLnStruct = ^TStatusNoLnStruct; TStatusProcList = {$IFDEF FPC}specialize {$ENDIF FPC} TGenericsList; TStatusStructList = {$IFDEF FPC}specialize {$ENDIF FPC} TGenericsList; TStatusNoLnStructList = {$IFDEF FPC}specialize {$ENDIF FPC} TGenericsList; var StatusActive: Boolean; HookStatusProcs: TStatusProcList; StatusStructList: TStatusStructList; StatusCritical: TCriticalSection; StatusNoLnStructList: TStatusNoLnStructList; Hooked_OnCheckThreadSynchronize: TCheckThreadSynchronize; function GetOrCreateStatusNoLnData_(th_: TCoreClassThread): PStatusNoLnStruct; var tk: TTimeTick; i: Integer; begin tk := GetTimeTick(); Result := nil; i := 0; while i < StatusNoLnStructList.Count do begin if StatusNoLnStructList[i]^.th = th_ then begin Result := StatusNoLnStructList[i]; Result^.TriggerTime := tk; if i > 0 then StatusNoLnStructList.Exchange(i, 0); inc(i); end else if tk - StatusNoLnStructList[i]^.TriggerTime > C_Tick_Minute then begin Dispose(StatusNoLnStructList[i]); StatusNoLnStructList.Delete(i); end else inc(i); end; if Result = nil then begin new(Result); Result^.s := ''; Result^.th := th_; Result^.TriggerTime := tk; StatusNoLnStructList.Add(Result); end; end; function GetOrCreateStatusNoLnData(): PStatusNoLnStruct; begin Result := GetOrCreateStatusNoLnData_(TCoreClassThread.CurrentThread); end; procedure DoStatusNoLn(const v: TPascalString); var L, i: Integer; StatusNoLnData: PStatusNoLnStruct; pSS: PStatusStruct; begin StatusCritical.Acquire; StatusNoLnData := GetOrCreateStatusNoLnData(); try L := v.Len; i := 1; while i <= L do begin if CharIn(v[i], [#13, #10]) then begin if StatusNoLnData^.s.Len > 0 then begin new(pSS); pSS^.s := StatusNoLnData^.s.Text; pSS^.th := TCoreClassThread.CurrentThread; pSS^.TriggerTime := GetTimeTick; StatusStructList.Add(pSS); StatusNoLnData^.s := ''; end; repeat inc(i); until (i > L) or (not CharIn(v[i], [#13, #10])); end else begin StatusNoLnData^.s.Append(v[i]); inc(i); end; end; finally StatusCritical.Release; end; end; procedure DoStatusNoLn(const v: SystemString; const Args: array of const); begin DoStatusNoLn(Format(v, Args)); end; procedure DoStatusNoLn; var StatusNoLnData: PStatusNoLnStruct; a: SystemString; begin StatusCritical.Acquire; StatusNoLnData := GetOrCreateStatusNoLnData(); a := StatusNoLnData^.s; StatusNoLnData^.s := ''; StatusCritical.Release; if Length(a) > 0 then DoStatus(a); end; function StrInfo(s: TPascalString): string; begin Result := BytesInfo(s.Bytes); end; function StrInfo(s: TUPascalString): string; begin Result := BytesInfo(s.Bytes); end; function BytesInfo(s: TBytes): string; begin Result := umlStringOf(s); end; procedure _InternalOutput(const Text_: SystemString; const ID: Integer); var i: Integer; p: PStatusProcStruct; begin if (StatusActive) and (HookStatusProcs.Count > 0) then begin LastDoStatus := Text_; for i := HookStatusProcs.Count - 1 downto 0 do begin p := HookStatusProcs[i]; try if Assigned(p^.OnStatusM) then p^.OnStatusM(Text_, ID); if Assigned(p^.OnStatusC) then p^.OnStatusC(Text_, ID); if Assigned(p^.OnStatusP) then p^.OnStatusP(Text_, ID); except end; end; end; {$IFNDEF FPC} if ((IDEOutput) or (ID = 2)) and (DebugHook <> 0) then begin {$IF Defined(WIN32) or Defined(WIN64)} OutputDebugString(PWideChar('"' + Text_ + '"')); {$ELSEIF not Defined(Linux)} FMX.Types.Log.d('"' + Text_ + '"'); {$IFEND} end; {$IFEND FPC} if ((ConsoleOutput) or (ID = 2)) and (IsConsole) then Writeln(Text_); end; procedure CheckDoStatus(th: TCoreClassThread); var i: Integer; pSS: PStatusStruct; begin if StatusCritical = nil then exit; if (th = nil) or (th.ThreadID <> MainThreadID) then exit; StatusCritical.Acquire; try if StatusStructList.Count > 0 then begin for i := 0 to StatusStructList.Count - 1 do begin pSS := StatusStructList[i]; _InternalOutput(pSS^.s, 0); pSS^.s := ''; Dispose(pSS); end; StatusStructList.Clear; end; finally StatusCritical.Release; end; end; procedure DoStatus; begin CheckDoStatus(TCoreClassThread.CurrentThread); end; procedure InternalDoStatus(Text_: SystemString; const ID: Integer); var th: TCoreClassThread; pSS: PStatusStruct; begin th := TCoreClassThread.CurrentThread; if (th = nil) or (th.ThreadID <> MainThreadID) then begin new(pSS); pSS^.s := '[' + IntToStr(th.ThreadID) + '] ' + Text_;; pSS^.th := th; pSS^.TriggerTime := GetTimeTick(); StatusCritical.Acquire; StatusStructList.Add(pSS); StatusCritical.Release; exit; end; CheckDoStatus(th); _InternalOutput(Text_, ID); end; procedure AddDoStatusHook(TokenObj: TCoreClassObject; CallProc: TDoStatusMethod); begin AddDoStatusHookM(TokenObj, CallProc); end; procedure AddDoStatusHookM(TokenObj: TCoreClassObject; CallProc: TDoStatusMethod); var p: PStatusProcStruct; begin new(p); p^.TokenObj := TokenObj; p^.OnStatusM := CallProc; p^.OnStatusC := nil; p^.OnStatusP := nil; HookStatusProcs.Add(p); end; procedure AddDoStatusHookC(TokenObj: TCoreClassObject; CallProc: TDoStatusCall); var p: PStatusProcStruct; begin new(p); p^.TokenObj := TokenObj; p^.OnStatusM := nil; p^.OnStatusC := CallProc; p^.OnStatusP := nil; HookStatusProcs.Add(p); end; procedure AddDoStatusHookP(TokenObj: TCoreClassObject; CallProc: TDoStatusProc); var p: PStatusProcStruct; begin new(p); p^.TokenObj := TokenObj; p^.OnStatusM := nil; p^.OnStatusC := nil; p^.OnStatusP := CallProc; HookStatusProcs.Add(p); end; procedure DeleteDoStatusHook(TokenObj: TCoreClassObject); var i: Integer; p: PStatusProcStruct; begin i := 0; while i < HookStatusProcs.Count do begin p := HookStatusProcs[i]; if p^.TokenObj = TokenObj then begin Dispose(p); HookStatusProcs.Delete(i); end else inc(i); end; end; procedure DisableStatus; begin StatusActive := False; end; procedure EnabledStatus; begin StatusActive := True; end; procedure DoCheckThreadSynchronize; begin DoStatus(); if Assigned(Hooked_OnCheckThreadSynchronize) then Hooked_OnCheckThreadSynchronize(); end; procedure _DoInit; begin HookStatusProcs := TStatusProcList.Create; StatusStructList := TStatusStructList.Create; StatusCritical := TCriticalSection.Create; StatusNoLnStructList := TStatusNoLnStructList.Create; StatusActive := True; LastDoStatus := ''; IDEOutput := False; ConsoleOutput := True; OnDoStatusHook := {$IFDEF FPC}@{$ENDIF FPC}InternalDoStatus; Hooked_OnCheckThreadSynchronize := CoreClasses.OnCheckThreadSynchronize; CoreClasses.OnCheckThreadSynchronize := {$IFDEF FPC}@{$ENDIF FPC}DoCheckThreadSynchronize; end; procedure _DoFree; var i: Integer; pSS: PStatusStruct; begin for i := 0 to HookStatusProcs.Count - 1 do Dispose(PStatusProcStruct(HookStatusProcs[i])); DisposeObject(HookStatusProcs); for i := 0 to StatusStructList.Count - 1 do begin pSS := StatusStructList[i]; pSS^.s := ''; Dispose(pSS); end; DisposeObject(StatusStructList); for i := 0 to StatusNoLnStructList.Count - 1 do Dispose(StatusNoLnStructList[i]); DisposeObject(StatusNoLnStructList); DisposeObject(StatusCritical); StatusActive := True; StatusCritical := nil; end; initialization _DoInit; finalization _DoFree; end.