MultiHash
Posted: Tue Nov 17, 2015 11:36 am
This program calculates the hash (CRC32, MD5, SHA 1, SHA2 and SHA3) of the selected file. It is optimized for multicore processors, and may use up to 12 cores, upon activation of all hash functions.
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