Code: Select all
Enumeration FormWindow
#Window_Main
EndEnumeration
Enumeration FormGadget
#String_0
#Button_0
#Text_0
#ProgressBar_0
#Button_1
#ListIcon_0
EndEnumeration
Declare ResizeGadgetsWindow_Main()
Procedure OpenWindow_Main(x = 0, y = 0, width = 548, height = 300)
OpenWindow(#Window_Main, x, y, width, height, "", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
StringGadget(#String_0, 4, 8, 460, 20, "", #PB_String_ReadOnly)
ButtonGadget(#Button_0, 472, 4, 72, 24, "Browse")
TextGadget(#Text_0, 4, 36, 460, 16, "")
ProgressBarGadget(#ProgressBar_0, 4, 52, 460, 16, 0, 100, #PB_ProgressBar_Smooth)
ButtonGadget(#Button_1, 472, 44, 72, 24, "Start")
ListIconGadget(#ListIcon_0, -2, 72, 550, 228, "Function", 130, #PB_ListIcon_CheckBoxes | #PB_ListIcon_MultiSelect | #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
AddGadgetColumn(#ListIcon_0, 1, "Result", 410)
AddGadgetItem(#ListIcon_0, -1, "CRC32")
AddGadgetItem(#ListIcon_0, -1, "MD5")
AddGadgetItem(#ListIcon_0, -1, "SHA1")
AddGadgetItem(#ListIcon_0, -1, "SHA2 - 224")
AddGadgetItem(#ListIcon_0, -1, "SHA2 - 256")
AddGadgetItem(#ListIcon_0, -1, "SHA2 - 384")
AddGadgetItem(#ListIcon_0, -1, "SHA2 - 512")
AddGadgetItem(#ListIcon_0, -1, "SHA3 - 224")
AddGadgetItem(#ListIcon_0, -1, "SHA3 - 256")
AddGadgetItem(#ListIcon_0, -1, "SHA3 - 384")
AddGadgetItem(#ListIcon_0, -1, "SHA3 - 512")
EndProcedure
Procedure ResizeGadgetsWindow_Main()
Protected FormWindowWidth, FormWindowHeight
FormWindowWidth = WindowWidth(#Window_Main)
FormWindowHeight = WindowHeight(#Window_Main)
ResizeGadget(#String_0, 4, 8, FormWindowWidth - 88, 20)
ResizeGadget(#Button_0, FormWindowWidth - 76, 4, 72, 24)
ResizeGadget(#Text_0, 4, 36, FormWindowWidth - 88, 16)
ResizeGadget(#ProgressBar_0, 4, 52, FormWindowWidth - 88, 16)
ResizeGadget(#Button_1, FormWindowWidth - 76, 44, 72, 24)
ResizeGadget(#ListIcon_0, -2, 72, FormWindowWidth - -2, FormWindowHeight - 72)
EndProcedure
UseCRC32Fingerprint()
UseMD5Fingerprint()
UseSHA1Fingerprint()
UseSHA2Fingerprint()
UseSHA3Fingerprint()
CompilerIf #PB_Compiler_Thread=0
CompilerError "Please, enable option 'threadsafe'"
CompilerEndIf
EnableExplicit
#ProgName = "MultiHash 1.0"
#FileBuffSize = 1024*1024
#FileBuffCount = 50
Enumeration
#CRC32
#MD5
#SHA1
#SHA2_224
#SHA2_256
#SHA2_384
#SHA2_512
#SHA3_224
#SHA3_256
#SHA3_384
#SHA3_512
#CountFunct ; Число хеш-функций.
EndEnumeration
#Mask = (1<<#CountFunct)-1
Structure ThInfo_FileBuff ; Буфер прочитанных данных с диска.
*Point ; Буфер с данными.
Size.l ; Число байт в буфере.
FinishFlag.l ; Флаги завершения использования блока данных хеш-функциями.
NumBlock.q ; Номер блока данных.
EndStructure
Structure ThInfo_File
List Buff.ThInfo_FileBuff()
*ID ; Идентификатор файла.
Size.q ; Размер файла.
Pos.q ; Текущая позиция от начала файла.
NumCounter.q ; Счетчик для нумерации блоков данных.
Eof.a ; #True - файл прочитан полностью.
EndStructure
Structure ThInfo_Hash
*Fingerprint ; Идентификатор хеш-функции.
Result.s ; Результат работы функции.
CurrentBlock.q
Type.a ; Тип хеш-функции.
Work.a
EndStructure
Structure ThreadInfo
List Hash.ThInfo_Hash()
Array ThreadID.i(0) ; Массив идентификаторов потоков хеш-функций.
FileThreadID.i ; Идентификатор потока чтения данных из файла.
File.ThInfo_File
Mutex.i ; Мьютекс...
Semaphore.i
HashMask.l ; Маска используемых хеш-функций.
FinishStatus.a
EndStructure
Define Event, Gadget, NewList SpeedList.q()
Global gThread.ThreadInfo, gBreakThread=#False, gSpeed=0
Macro SetBit(Var, Bit) ; Установка бита.
Var | (Bit)
EndMacro
Macro ClearBit(Var, Bit) ; Обнуление бита.
Var & (~(Bit))
EndMacro
Macro TestBit(Var, Bit) ; Проверка бита (#True - установлен; #False - обнулен).
Bool(Var & (Bit))
EndMacro
Macro NumToBit(Num) ; Позиция бита по его номеру.
(1<<(Num))
EndMacro
Procedure.q Add_AverageList_Quad(List MyList.q(), Add.q, MaxList.l)
Protected Result.q, x.l, i, Size
Size = ListSize(MyList())
If Size>MaxList
LastElement(MyList())
For i=Size-1 To MaxList Step -1
DeleteElement(MyList())
Next i
Size = ListSize(MyList())
EndIf
x=#False
If Size<MaxList
FirstElement(MyList())
If InsertElement(MyList())
x=#True
Size + 1
EndIf
Else
x=#True
If Size=MaxList
LastElement(MyList())
MyList()=0
MoveElement(MyList(), #PB_List_First)
EndIf
EndIf
If x=#True
FirstElement(MyList())
MyList() = Add
EndIf
Result=0 : x=0
ForEach MyList()
Result + MyList()
x + 1
Next
Result / x
ProcedureReturn Result
EndProcedure
Procedure FileThread(*x) ; Процедура потока чтения данных из файла.
Protected i, x, Eof=#False
Protected *Point, Bytes
Repeat
WaitSemaphore(gThread\Semaphore) ; Ожидание сигнала чтения данных из файла.
If gBreakThread=#True
Break
EndIf
Repeat
If Eof(gThread\File\ID)
Eof=#True
Break 2
EndIf
x=#False
LockMutex(gThread\Mutex)
If ListSize(gThread\File\Buff())<#FileBuffCount
x = #True ; Нужно загрузить данные из файла в буфер.
EndIf
UnlockMutex(gThread\Mutex)
If x = #False
Break
EndIf
*Point = AllocateMemory(#FileBuffSize, #PB_Memory_NoClear)
If *Point
Bytes = ReadData(gThread\File\ID, *Point, #FileBuffSize)
If Bytes<=0 Or Bytes>#FileBuffSize
Break 2
EndIf
LockMutex(gThread\Mutex)
gThread\File\Pos + Bytes
LastElement(gThread\File\Buff())
If AddElement(gThread\File\Buff())
With gThread\File\Buff()
\Point = *Point
\Size = Bytes
\FinishFlag=0
\NumBlock = gThread\File\NumCounter
EndWith
gThread\File\NumCounter+1
Else
FreeMemory(*Point)
UnlockMutex(gThread\Mutex)
Break 2
EndIf
UnlockMutex(gThread\Mutex)
Else
Delay(2)
EndIf
ForEver
Until gBreakThread=#True
LockMutex(gThread\Mutex)
If Eof
gThread\File\Eof = #True
EndIf
x=gThread\File\ID
gThread\File\ID=0
UnlockMutex(gThread\Mutex)
If x And IsFile(x)
CloseFile(x)
EndIf
EndProcedure
Procedure HashThread(*x) ; Процедура потока выполнения хеш-функций.
Protected i, NumFunct, NoData
Protected *Point, Size, NumBlock.q
Protected *Fingerprint
Repeat
*Point=0 : *Fingerprint=0
NumFunct=-1 : NumBlock=-1
NoData = #True
LockMutex(gThread\Mutex)
ForEach gThread\Hash() ; Ищем хеш-функцию которая сейчас не выполняется в других потоках.
If gThread\Hash()\Work = #False And gThread\Hash()\Fingerprint
ForEach gThread\File\Buff() ; Ищем блок данных, который следующим нужно прохешировать.
If gThread\File\Buff()\NumBlock = gThread\Hash()\CurrentBlock
NumBlock = gThread\Hash()\CurrentBlock
gThread\Hash()\CurrentBlock+1
*Fingerprint=gThread\Hash()\Fingerprint
*Point=gThread\File\Buff()\Point
Size=gThread\File\Buff()\Size
NumFunct = gThread\Hash()\Type
gThread\Hash()\Work=#True
NoData = #False
Break 2
EndIf
Next
EndIf
Next
UnlockMutex(gThread\Mutex)
If *Point And *Fingerprint And Size>0 And NumBlock>=0 And (NumFunct>=0 And NumFunct<#CountFunct)
AddFingerprintBuffer(*Fingerprint, *Point, Size) ; Вычисляем хеш...
LockMutex(gThread\Mutex)
ForEach gThread\File\Buff()
If gThread\File\Buff()\NumBlock = NumBlock
SetBit(gThread\File\Buff()\FinishFlag, NumToBit(NumFunct)) ; Отмечаем блок данных обработаным текущей функцией.
ForEach gThread\Hash() ; Ищем идентификатор хеш-функции.
If gThread\Hash()\Type = NumFunct
gThread\Hash()\Work=#False
Break
EndIf
Next
; Блок данных обработан всеми активными хеш-функциями.
If (gThread\File\Buff()\FinishFlag ! gThread\HashMask) & #Mask = 0
FreeMemory(gThread\File\Buff()\Point)
DeleteElement(gThread\File\Buff())
If gThread\File\Eof = #False
SignalSemaphore(gThread\Semaphore) ; Загрузка следующего блока данных.
EndIf
EndIf
Break
EndIf
Next
UnlockMutex(gThread\Mutex)
Else
i=#False
If NoData = #True
LockMutex(gThread\Mutex)
If gThread\File\Eof = #True
If gThread\FinishStatus=#False And ListSize(gThread\File\Buff())<=0
ForEach gThread\Hash()
gThread\Hash()\Result=FinishFingerprint(gThread\Hash()\Fingerprint)
gThread\Hash()\Fingerprint=0
Next
gThread\FinishStatus=#True
EndIf
i=#True
EndIf
UnlockMutex(gThread\Mutex)
EndIf
If i=#True
Break
EndIf
Delay(2)
EndIf
Until gBreakThread=#True
EndProcedure
Procedure Timer()
Shared SpeedList()
Protected Pos.f, Speed, x, Count, i
LockMutex(gThread\Mutex)
If gThread\File\Pos>0
Pos=100/(gThread\File\Size / gThread\File\Pos)
Else
Pos=0
EndIf
Speed = gThread\File\Pos-gSpeed
gSpeed=gThread\File\Pos
x=gThread\FinishStatus
UnlockMutex(gThread\Mutex)
If x=#False
Speed = Add_AverageList_Quad(SpeedList(), Speed*4, 10)
SetGadgetState(#ProgressBar_0, Pos)
SetGadgetText(#Text_0, "Processed "+Str(Pos)+"% Speed "+StrF((Speed/1024/1024), 1)+" MB/s")
Else
x=#False
Count=ArraySize(gThread\ThreadID())
For i=0 To Count
If gThread\ThreadID(i)<>0 And IsThread(gThread\ThreadID(i))
x=#True
Break
EndIf
Next i
If x=#False
ForEach gThread\Hash()
SetGadgetItemText(#ListIcon_0, gThread\Hash()\Type, gThread\Hash()\Result, 1)
Next
SetGadgetText(#Text_0, "Done")
SetGadgetText(#Button_1, "Start")
SetGadgetState(#ProgressBar_0, 0)
UnbindEvent(#PB_Event_Timer, @Timer(), #Window_Main, 2)
RemoveWindowTimer(#Window_Main, 2)
Else
SetGadgetText(#Text_0, "Waiting for completion threads")
EndIf
EndIf
EndProcedure
Macro ThreadWait(Thread)
If Thread<>0 And IsThread(Thread) ; Нашли активный поток.
WaitThread(Thread, 4000) ; Даем потоку время на завершение (4 секунды).
If IsThread(Thread) ; Если поток не завершился,
KillThread(Thread) ; тогда поможем ему это сделать.
x=#True
EndIf
EndIf
Thread=0
EndMacro
Procedure StopAndFree()
Shared SpeedList()
Protected i, Count, x
gBreakThread=#True ; Флаг прерывания потоков.
x=#False
UnbindEvent(#PB_Event_Timer, @Timer(), #Window_Main, 2)
RemoveWindowTimer(#Window_Main, 2)
SetGadgetText(#Text_0, "")
SetGadgetState(#ProgressBar_0, 0)
SetGadgetText(#Button_1, "Start")
gSpeed=0
ClearList(SpeedList())
SignalSemaphore(gThread\Semaphore)
ThreadWait(gThread\FileThreadID)
Count=ArraySize(gThread\ThreadID())
For i=0 To Count
ThreadWait(gThread\ThreadID(i))
Next i
If x=#True ; Если потоки были принудительно завершены, нужно пересоздать мьютекс.
If gThread\Mutex
FreeMutex(gThread\Mutex)
EndIf
gThread\Mutex = CreateMutex()
EndIf
If gThread\File\ID<>0 And IsFile(gThread\File\ID)
CloseFile(gThread\File\ID)
gThread\File\ID=0
EndIf
ForEach gThread\File\Buff()
If gThread\File\Buff()\Point
FreeMemory(gThread\File\Buff()\Point)
gThread\File\Buff()\Point=0
EndIf
Next
ForEach gThread\Hash()
If gThread\Hash()\Fingerprint And IsFingerprint(gThread\Hash()\Fingerprint)
FinishFingerprint(gThread\Hash()\Fingerprint)
gThread\Hash()\Fingerprint=0
EndIf
Next
ClearList(gThread\File\Buff()) ; Очистка списков.
ClearList(gThread\Hash())
gThread\HashMask=0
EndProcedure
Procedure StartStop()
Protected i, x, String.s, CountFunct
Protected Plugin, Bits, Err=#False
If IsThread(gThread\FileThreadID)
If MessageRequester(#ProgName, "Cancel calculating hash sum?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
StopAndFree() ; Освобождение всех ресурсов, используемых в потоках.
EndIf
ProcedureReturn
EndIf
StopAndFree() ; Освобождение всех ресурсов, используемых в потоках.
gBreakThread=#False
gThread\HashMask=0
gThread\File\Eof=#False
gThread\FinishStatus=#False
gThread\File\NumCounter=0
CountFunct=0
x=CountGadgetItems(#ListIcon_0)-1 ; Очистка колонки "Результат" таблицы.
For i=0 To x
SetGadgetItemText(#ListIcon_0, i, "", 1)
If GetGadgetItemState(#ListIcon_0, i) & #PB_ListIcon_Checked
If AddElement(gThread\Hash())
Bits=0
Select i
Case #CRC32
Plugin=#PB_Cipher_CRC32
Case #MD5
Plugin=#PB_Cipher_MD5
Case #SHA1
Plugin=#PB_Cipher_SHA1
Case #SHA2_224
Plugin=#PB_Cipher_SHA2
Bits=224
Case #SHA2_256
Plugin=#PB_Cipher_SHA2
Bits=256
Case #SHA2_384
Plugin=#PB_Cipher_SHA2
Bits=384
Case #SHA2_512
Plugin=#PB_Cipher_SHA2
Bits=512
Case #SHA3_224
Plugin=#PB_Cipher_SHA3
Bits=224
Case #SHA3_256
Plugin=#PB_Cipher_SHA3
Bits=256
Case #SHA3_384
Plugin=#PB_Cipher_SHA3
Bits=384
Case #SHA3_512
Plugin=#PB_Cipher_SHA3
Bits=512
EndSelect
gThread\Hash()\Type=i
If i<=#SHA1
gThread\Hash()\Fingerprint = StartFingerprint(#PB_Any, Plugin)
Else
gThread\Hash()\Fingerprint = StartFingerprint(#PB_Any, Plugin, Bits)
EndIf
If gThread\Hash()\Fingerprint
SetBit(gThread\HashMask, NumToBit(i))
CountFunct+1
Else
DeleteElement(gThread\Hash())
Goto m1
EndIf
Else
m1:
SetGadgetItemText(#ListIcon_0, i, "Error function", 1)
EndIf
EndIf
Next i
If gThread\HashMask=0
MessageRequester("", "Select hash-functions")
ProcedureReturn
EndIf
String = GetGadgetText(#String_0)
If String<>"" And FileSize(String)>0
gThread\File\ID = ReadFile(#PB_Any, String, #PB_File_SharedRead|#PB_File_SharedWrite)
If gThread\File\ID
gThread\File\Size = Lof(gThread\File\ID)
gThread\File\Pos = 0
BindEvent(#PB_Event_Timer, @Timer(), #Window_Main, 2)
AddWindowTimer(#Window_Main, 2, 250)
SetGadgetText(#Button_1, "Stop")
SignalSemaphore(gThread\Semaphore)
gThread\FileThreadID = CreateThread(@FileThread(), 0)
If gThread\FileThreadID
ThreadPriority(gThread\FileThreadID, 30) ; Высокий приоритет.
x=CountCPUs(#PB_System_ProcessCPUs)-1 ; Количество доступных ядер процессора.
CountFunct-1
If CountFunct<x ; Число активных функций меньше чем доступных ядер процессора.
x=CountFunct
EndIf
If ArraySize(gThread\ThreadID())<>x
ReDim gThread\ThreadID(x)
EndIf
For i=0 To x
gThread\ThreadID(i) = CreateThread(@HashThread(), 0)
If gThread\ThreadID(i) = 0
Goto m2
EndIf
Next i
Else
m2:
Err=#True
MessageRequester(#ProgName, "Failed to start thread")
EndIf
Else
MessageRequester(#ProgName, "Could not open file")
EndIf
Else
MessageRequester(#ProgName, "Select the file path")
EndIf
If Err=#True ; Произошла ошибка.
StopAndFree() ; Освобождение всех ресурсов, используемых в потоках.
EndIf
EndProcedure
Procedure SelectFile()
Protected File.s
File = OpenFileRequester("","","All files (*.*)|*.*", 0)
If File<>"" And FileSize(File)>0
SetGadgetText(#String_0, File)
EndIf
EndProcedure
Procedure CopyToClipboard()
Protected i, Count
Protected String.s="", Result.s
Count=CountGadgetItems(#ListIcon_0)-1
For i=0 To Count
String = GetGadgetItemText(#ListIcon_0, i, 1)
If String<>""
If Result<>"" : Result+#CRLF$ : EndIf
Result+GetGadgetItemText(#ListIcon_0, i, 0)+" "+String
EndIf
Next i
If Result<>""
SetClipboardText(Result)
EndIf
EndProcedure
Procedure ResizeWin()
ResizeGadgetsWindow_Main()
SetGadgetItemAttribute(#ListIcon_0, 0, #PB_ListIcon_ColumnWidth,
GadgetWidth(#ListIcon_0)-10-GetGadgetItemAttribute(#ListIcon_0, 0,
#PB_ListIcon_ColumnWidth, 0), 1)
EndProcedure
gThread\Mutex = CreateMutex()
gThread\Semaphore = CreateSemaphore()
ReDim gThread\ThreadID(CountCPUs(#PB_System_ProcessCPUs)-1) ; Количество доступных ядер процессора.
If CreatePopupMenu(0)
MenuItem(0, "Copy")
EndIf
OpenWindow_Main()
SetWindowTitle(#Window_Main, #ProgName)
SmartWindowRefresh(#Window_Main, #True)
WindowBounds(#Window_Main, 300, 200, #PB_Ignore, #PB_Ignore)
BindEvent(#PB_Event_Gadget, @SelectFile(), #Window_Main, #Button_0)
BindEvent(#PB_Event_Gadget, @StartStop(), #Window_Main, #Button_1)
BindEvent(#PB_Event_SizeWindow, @ResizeWin(), #Window_Main)
BindEvent(#PB_Event_Menu, @CopyToClipboard(), #Window_Main, 0)
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Gadget
If EventGadget()=#ListIcon_0 And EventType()=#PB_EventType_RightClick
DisplayPopupMenu(0, WindowID(#Window_Main))
EndIf
EndIf
Until Event = #PB_Event_CloseWindow
StopAndFree()
If gThread\Mutex
FreeMutex(gThread\Mutex)
EndIf
If gThread\Semaphore
FreeSemaphore(gThread\Semaphore)
EndIf
End