Ich habe beim Umstieg von Xojo eine ähnliche Prozedur, wie die hier im Thread, direkt nach PureBasic übernommen. Es gibt zusätzlich Parameter für die Formatierung der Ausgabe.
Eine zweite Version davon kommt jetzt ohne Strings aus, und ist in der kompilierten Exe um einiges schneller. Beide Prozeduren funktionieren mit Ascii und Unicode, habe sie aber nur mit x86 Versionen getestet.
Code: Alles auswählen
EnableExplicit
Procedure.s MemoryFormatHexALT(*Memory, iLength.i, iColumns.i = 16, iOffset.i = 0, fWithAscii.i = #True)
; Gibt die Daten formatiert als Hexadezimal und ASCII-Zeichen aus.
; -> *Memory: Die Adresse des Speicherbereichs.
; -> iLength: Die Länge des Speicherbereichs.
; -> iColumns: Die Anzahl der Bytes die pro Zeile formatiert werden.
; -> iOffset: Der Offset ab der die Daten verwendet werden sollen.
; -> fWithAscii: Bei False wird nur die Hexanzeige ohne der ASCII-Darstellung formatiert.
; Rückgabe: Die Zeichenfolge mit den formatierten Daten.
; z.B. bei 16 Zeichen pro Zeile:
; 00000| 37 BA 17 CC 28 F9 E3 F0 46 79 15 C2 4B 94 13 ED |7º.Ì(ùãðFy.ÂK..í|
Protected sReturn.s, fResult.i
Protected iDataSize.i, iChar.i, c.i, sChar.s, sHex.s, sAscii.s, sRow.s
#iColumnsMin = 1
#iColumnsMax = 64
; Übergebene Parameter prüfen.
fResult = Bool(*Memory And iLength)
If (Not fResult)
;DebugView::Error("Der Zeiger oder die Länge des Speichers ist Null. *Memory = " + *Memory + ", iLength = " + iLength)
EndIf
If fResult
iDataSize = iLength - 1
; Den Offset prüfen.
If ((iOffset > iDataSize) Or (iOffset < 0))
fResult = #False
;DebugView::Error("Der Offset ist ungültig. iOffset = " + iOffset + ", iLength = " + iLength)
EndIf
EndIf
If fResult
If ((iColumns < #iColumnsMin) Or (iColumns > #iColumnsMax))
fResult = #False
;DebugView::Error("Die Anzahl der Spalten ist ungültig. iColumns = " + iColumns)
EndIf
EndIf
If fResult
; Daten formatieren.
For c = iOffset To iDataSize
; iColumns-Bytes pro Zeile.
If ((c - iOffset) % iColumns) = 0 And ((c - iOffset) > 0)
sRow = Hex(c - iColumns)
sRow = Mid("0000", Len(sRow)) + sRow
If fWithAscii
sReturn + sRow + "|" + sHex + "|" + sAscii + "|" + #CRLF$
Else
sReturn + sRow + "|" + sHex + "|" + #CRLF$
EndIf
sHex = #Empty$
sAscii = #Empty$
EndIf
; Byte einlesen.
iChar = PeekA(*Memory + c)
sChar = Hex(iChar)
; Hex-Ausgabe des Bytes.
If Len(sChar) = 2
sHex + sChar + " "
Else
sHex + "0" + sChar + " "
EndIf
; ASCII-Ausgabe.
If fWithAscii
If (((iChar > 31) And (iChar < 128)) Or ((iChar > 160) And Not (iChar = 173)))
sAscii + Chr(iChar)
Else
sAscii + "."
EndIf
EndIf
Next c
; Letzte Zeile mit restlichen Bytes formatieren.
; Nur wenn überhaupt restliche Bytes vorhanden sind.
If (((c - iOffset) % iColumns) > 0)
sRow = Hex((c / iColumns) * iColumns)
sRow = Mid("0000", Len(sRow)) + sRow
If fWithAscii
sReturn + sRow + "|" + sHex + Space((iColumns - (c % iColumns)) * 3) + "|" + sAscii +
Space(iColumns - (c % iColumns)) + "|"
Else
sReturn + sRow + "|" + sHex + Space((iColumns - (c % iColumns)) * 3) + "|" + "|"
EndIf
; Es ist nur noch eine Zeile mit genau der Anzahl an Bytes wie 'iColumnChars' vorhanden.
Else
sRow = Hex(c - iColumns)
sRow = Mid("0000", Len(sRow)) + sRow
If fWithAscii
sReturn + sRow + "|" + sHex + "|" + sAscii + "|"
Else
sReturn + sRow + "|" + sHex + "|"
EndIf
EndIf
EndIf
ProcedureReturn sReturn
EndProcedure
Procedure.s MemoryFormatHex(*Memory, iLength.i, iColumns.i = 16, iOffset.i = 0, fWithAscii.i = #True)
; Gibt die Daten formatiert als Hexadezimal und ASCII-Zeichen aus.
; -> *Memory: Die Adresse des Speicherbereichs.
; -> iLength: Die Länge des Speicherbereichs.
; -> iColumns: Die Anzahl der Bytes die pro Zeile formatiert werden.
; -> iOffset: Der Offset ab der die Daten verwendet werden.
; -> fWithAscii: Bei False wird nur die Hexanzeige ohne der ASCII-Darstellung formatiert.
; Rückgabe: Die Zeichenfolge mit den formatierten Daten.
; z.B. bei 16 Zeichen pro Zeile:
; 00000| 37 BA 17 CC 28 F9 E3 F0 46 79 15 C2 4B 94 13 ED |7º.Ì(ùãðFy.ÂK..í|
Protected sReturn.s, fResult.i, iDataSize.i, iDataPos.i, c.i, d.i, iChar.a, iPos.i
Protected *sBuffer, iBufferLength.i, iBufferPos.i, iColumnChars.i, iColumn.i, iRow.i
#iAddressLength = 5
#iColumnsMin = 1
#iColumnsMax = 64
#iCharPeriod = 46
#iCharSpace = 32
#iCharPipe = 124
; Übergebene Parameter prüfen.
fResult = Bool(*Memory And iLength)
If (Not fResult)
;DebugView::Error("Der Zeiger oder die Länge des Speichers ist Null. *Memory = " + *Memory + ", iLength = " + iLength)
EndIf
If fResult
iDataSize = iLength - 1
; Den Offset prüfen.
If ((iOffset > iDataSize) Or (iOffset < 0))
fResult = #False
;DebugView::Error("Der Offset ist ungültig. iOffset = " + iOffset + ", iLength = " + iLength)
EndIf
EndIf
If fResult
If ((iColumns < #iColumnsMin) Or (iColumns > #iColumnsMax))
fResult = #False
;DebugView::Error("Die Anzahl der Spalten ist ungültig. iColumns = " + iColumns)
EndIf
EndIf
; Speicher für den formatierten String reservieren.
If fResult
; Die Anzahl der Zeichen pro Zeile berechnen. Plus 2 Zeichen für den Zeilenumbruch.
If fWithAscii
iColumnChars = (#iAddressLength + 1 + 1 + (iColumns * 3) + 1 + iColumns + 1 + 2) * SizeOf(Character)
Else
iColumnChars = (#iAddressLength + 1 + 1 + (iColumns * 3) + 2) * SizeOf(Character)
EndIf
iBufferLength = (iLength - iOffset) / iColumns ; Anzahl der vollständigen Zeilen.
iBufferLength + Bool((iLength - iOffset) % iColumns) ; Plus 1 für letzte unvollständige Zeile wenn vorhanden.
iBufferLength * iColumnChars + SizeOf(Character) ; Zeilen mal der Anzahl der formatierten Zeichen plus #Null.
*sBuffer = AllocateMemory(iBufferLength)
If (Not *sBuffer)
fResult = #False
;DebugView::Error("Der Speicher konnte nicht alloziert werden.")
EndIf
EndIf
; Die Daten formatieren.
If fResult
iRow = 1
For iDataPos = iOffset To iDataSize
; Zähler für die Zeilen, Spalten und Schreibposition berechnen.
iColumn + 1
If (iColumn > iColumns)
iColumn = 1
iRow + 1
; Schreibposition anpassen für eine neue Zeile.
If fWithAscii
iBufferPos + ((1 + iColumns + 1) * SizeOf(Character))
EndIf
iBufferPos + (2 * SizeOf(Character)) ; Den Zeilenumbruch berücksichtigen.
EndIf
; Die Adresse am Anfang der Zeile ausgeben. Die Adresse ist iDataPos von der For-Next Schleife.
If (iColumn = 1)
For c = (#iAddressLength - 1) To 0 Step -1
iChar = (iDataPos >> (c * 4)) & $0F
If (iChar < $0A)
iChar + 48 ; plus Ascii-Wert von '0'.
Else
iChar + 55 ; plus Ascii-Wert von 'A' - 10.
EndIf
PokeA(*sBuffer + iBufferPos, iChar)
iBufferPos + SizeOf(Character)
Next c
; Pipe und ein Leerzeichen ausgeben.
PokeA(*sBuffer + iBufferPos, #iCharPipe)
iBufferPos + SizeOf(Character)
PokeA(*sBuffer + iBufferPos, #iCharSpace)
iBufferPos + SizeOf(Character)
EndIf
; Die Hexzahl für ein Byte ausgeben.
For d = 1 To 0 Step -1
iChar = (PeekA(*Memory + iDataPos) >> (d * 4)) & $0F
If (iChar < $0A)
iChar + 48 ; plus Ascii-Wert von '0'.
Else
iChar + 55 ; plus Ascii-Wert von 'A' - 10.
EndIf
PokeA(*sBuffer + iBufferPos, iChar)
iBufferPos + SizeOf(Character)
Next d
; Anschließend ein Leerzeichen ausgeben.
PokeA(*sBuffer + iBufferPos, #iCharSpace)
iBufferPos + SizeOf(Character)
; Das Ascii-Zeichen ausgeben.
If fWithAscii
iChar = PeekA(*Memory + iDataPos)
If Not (((iChar > 31) And (iChar < 128)) Or ((iChar > 160) And Not (iChar = 173)))
iChar = #iCharPeriod
EndIf
; Position des Zeichen im Buffer berechnen.
iPos = iBufferPos + ((((iColumns - iColumn) * 3) + iColumn) * SizeOf(Character))
PokeA(*sBuffer + iPos, iChar)
EndIf
; Die Pipezeichen für die Ascii-Spalte ausgeben.
If fWithAscii
If ((iColumn = iColumns) Or (iDataPos = iDataSize))
; In der letzten Zeile die Nullzeichen mit Leerzeichen auffüllen.
If ((iDataPos = iDataSize) And (iColumn < iColumns))
For d = iBufferPos To (iBufferLength - (SizeOf(Character) * 2)) Step SizeOf(Character)
If (PeekA(*sBuffer + d) = #Null)
PokeA(*sBuffer + d, #iCharSpace)
EndIf
Next d
EndIf
; Position des Zeichen im Buffer berechnen.
iPos = iBufferPos + (((iColumns - iColumn) * 3) * SizeOf(Character))
PokeA(*sBuffer + iPos, #iCharPipe)
iPos + ((iColumns + 1) * SizeOf(Character))
PokeA(*sBuffer + iPos, #iCharPipe)
EndIf
EndIf
; Den Zeilenumbruch ausgeben.
If ((iColumn = iColumns) Or (iDataPos = iDataSize))
iPos = (iRow * iColumnChars) - (2 * SizeOf(Character))
PokeA(*sBuffer + iPos, #CR)
PokeA(*sBuffer + iPos + SizeOf(Character), #LF)
EndIf
Next iDataPos
EndIf
; Den formatierten String zurückgeben und den Speicher freigeben.
If fResult
sReturn = PeekS(*sBuffer, iBufferLength)
FreeMemory(*sBuffer)
EndIf
ProcedureReturn sReturn
EndProcedure
; ------ TEST ------
Define c.i, sTest.s, *sBuffer, iTime.q
If OpenWindow(0, #PB_Ignore, #PB_Ignore, 600, 600, "MemoryFormatHex", #PB_Window_SystemMenu)
EditorGadget(1, 0, 0, 600, 600)
SetGadgetFont(1, FontID(LoadFont(#PB_Any, "Courier New", 9)))
*sBuffer = AllocateMemory(10000)
RandomData(*sBuffer, 10000)
sTest = "123456789012345678901234567890"
AddGadgetItem(1, -1, sTest)
AddGadgetItem(1, -1, MemoryFormatHex(@sTest, StringByteLength(sTest)))
AddGadgetItem(1, -1, MemoryFormatHex(@sTest, StringByteLength(sTest), 22, 4, #False))
AddGadgetItem(1, -1, #Null$)
sTest = "AZaz" + #CRLF$
AddGadgetItem(1, -1, "AZaz + #CRLF$")
AddGadgetItem(1, -1, MemoryFormatHex(@sTest, StringByteLength(sTest), 6))
sTest = #Null$
AddGadgetItem(1, -1, "#Null$")
AddGadgetItem(1, -1, MemoryFormatHex(@sTest, StringByteLength(sTest)))
AddGadgetItem(1, -1, "RandomData")
AddGadgetItem(1, -1, MemoryFormatHex(*sBuffer, 71))
AddGadgetItem(1, -1, "Speedtest")
AddGadgetItem(1, -1, #Null$)
For c = 1 To 5
iTime = ElapsedMilliseconds()
sTest = MemoryFormatHexALT(*sBuffer, c * 1000)
iTime = ElapsedMilliseconds() - iTime
AddGadgetItem(1, -1, "String " + Str(c * 1000) + " - " + iTime)
Next c
AddGadgetItem(1, -1, #Null$)
For c = 1 To 5
iTime = ElapsedMilliseconds()
sTest = MemoryFormatHex(*sBuffer, c * 1000)
iTime = ElapsedMilliseconds() - iTime
AddGadgetItem(1, -1, "Memory " + Str(c * 1000) + " - " + iTime)
Next c
AddGadgetItem(1, -1, #Null$)
AddGadgetItem(1, -1, MemoryFormatHex(*sBuffer, 10000))
FreeMemory(*sBuffer)
Repeat : Until (#PB_Event_CloseWindow = WaitWindowEvent())
EndIf