PeekHexBytes() and PokeHexBytes()
Posted: Wed Aug 18, 2010 2:17 pm
Works also with PB 5.20
Hi all,
here are two basic cross-platform functions, that are useful for reading arbitrary content from memory, to store it into strings or text files, and to write it back to memory. PeekHexBytes() comes in handy for instance in context with PB's AESEncoder() (e.g. in order to store encrypted passwords in a preference file), since the encoded output may contain bytes with value 0, so that PB's PeekS() cannot be used safely for reading it. PokeHexBytes() is the complementary function, which allows to easily write arbitrary byte sequences to memory.
( Another option for getting only byte values that can be stored in strings, is e.g. conversion with PB's Base64Encoder(). )
I hope the code is useful for some of you. It is designed for flexibility, simplicity and safety. It's not optimized for speed, since that can easily result in bad readable code, and would mean premature optimization in probably most cases. So do speed optimization yourself if you need it, and only when you need it.
Regards, Little John
//edit 2010-08-24
PokeHexBytes() completely rewritten:
Thanks to Trond, the procedure now accepts hexadecimal strings in more different input formats, and is much simpler at the same time. On error, it now returns -1 instead of 0.
Hi all,
here are two basic cross-platform functions, that are useful for reading arbitrary content from memory, to store it into strings or text files, and to write it back to memory. PeekHexBytes() comes in handy for instance in context with PB's AESEncoder() (e.g. in order to store encrypted passwords in a preference file), since the encoded output may contain bytes with value 0, so that PB's PeekS() cannot be used safely for reading it. PokeHexBytes() is the complementary function, which allows to easily write arbitrary byte sequences to memory.
( Another option for getting only byte values that can be stored in strings, is e.g. conversion with PB's Base64Encoder(). )
I hope the code is useful for some of you. It is designed for flexibility, simplicity and safety. It's not optimized for speed, since that can easily result in bad readable code, and would mean premature optimization in probably most cases. So do speed optimization yourself if you need it, and only when you need it.
Regards, Little John
//edit 2010-08-24
PokeHexBytes() completely rewritten:
Thanks to Trond, the procedure now accepts hexadecimal strings in more different input formats, and is much simpler at the same time. On error, it now returns -1 instead of 0.
Code: Select all
; -- Tested with PB 4.51 RC 2 x86 on Windows XP and Ubuntu 10.04,
; compiled to Unicode-executable and to non-Unicode-executable
EnableExplicit
Procedure.s PeekHexBytes (*source.Ascii, numBytes=-1, separator$="", prefix$="")
; -- returns a hexadecimal representation of the bytes in a memory area
; ("Hex dump")
; in : *source : points to begin of the source memory area
; numBytes : number of bytes to read (< 0 reads up to next null byte)
; separator$: string that is written as separator in the output
; prefix$ : prefix for each hex byte in the output (e.g. "$" or "0x")
; out: returns a string of unsigned hexadecimal byte values;
; hex digits "A" to "F" are upper case.
Protected *eofSource, ret$=""
*eofSource = *source + numBytes
While *source < *eofSource Or (*source > *eofSource And *source\a <> 0)
ret$ + separator$ + prefix$ + RSet(Hex(*source\a), 2, "0")
*source + 1
Wend
ProcedureReturn Mid(ret$, Len(separator$)+1)
EndProcedure
Macro SkipString (_s_, _length_)
If CompareMemory(*source, @_s_, _length_)
*source + _length_
EndIf
EndMacro
Procedure.i IsHexDigit (*source.Character)
Select *source\c
Case '0' To '9', 'A' To 'F', 'a' To 'f'
ProcedureReturn #True
Default
ProcedureReturn #False
EndSelect
EndProcedure
Procedure.i PokeHexBytes (*dest.Ascii, hexList$, separator$="", prefix$="")
; -- writes bytes, that are given in the form of a string of hex
; values, to a memory area
; in : *dest : points to begin of the destination memory area
; hexList$ : list of unsigned hexadezimal byte values;
; Each byte must be represented by 2 hex digits, and
; can optionally have a prefix (such as "$" or "0x").
; Hex digits "A" to "F" can be upper or lower case.
; separator$: string that is used as separator between bytes in
; hexList$ (e.g. " ", ";" or "; ")
; prefix$ : prefix that is used for bytes in hexList$
; out: returns number of written bytes on success, or -1 on error
Protected *destStart, *source.Character, lenPre, lenSep
*destStart = *dest
*source = @hexList$
lenPre = StringByteLength(prefix$)
lenSep = StringByteLength(separator$)
While *source\c
SkipString(prefix$, lenPre)
If (Not IsHexDigit(*source)) Or (Not IsHexDigit(*source+SizeOf(Character)))
ProcedureReturn -1 ; error
EndIf
*dest\a = Val("$" + PeekS(*source, 2))
*source + 2*SizeOf(Character)
*dest + 1
SkipString(separator$, lenSep)
Wend
ProcedureReturn *dest - *destStart
EndProcedure
;=======================================================================
; B E G I N O F D E M O C O D E
;=======================================================================
Define numBytes, i, *source, *destination, hexList$
numBytes = 20
*source = AllocateMemory(100)
*destination = AllocateMemory(100)
; --- Fill the source buffer with some values, and with trailing null ---
For i = 0 To numBytes - 1
PokeA(*source+i, i+1)
Next
PokeA(*source+numBytes, 0)
; --- Get hexadecimal representation of the source, write the bytes to
; target, and compare source with target ==> should result in 1 ---
hexList$ = PeekHexBytes(*source)
Debug hexList$
Debug PokeHexBytes(*destination, hexList$)
Debug CompareMemory(*source, *destination, numBytes)
PokeA(*destination, 0) ; partially overwrite destination
hexList$ = PeekHexBytes(*source, numBytes, " ")
Debug hexList$
Debug PokeHexBytes(*destination, hexList$, " ")
Debug CompareMemory(*source, *destination, numBytes)
PokeA(*destination, 0) ; partially overwrite destination
hexList$ = PeekHexBytes(*source, -1, ",")
Debug hexList$
Debug PokeHexBytes(*destination, hexList$, ",")
Debug CompareMemory(*source, *destination, numBytes)
PokeA(*destination, 0) ; partially overwrite destination
hexList$ = PeekHexBytes(*source, -1, "", "$")
Debug hexList$
Debug PokeHexBytes(*destination, hexList$, "", "$")
Debug CompareMemory(*source, *destination, numBytes)
PokeA(*destination, 0) ; partially overwrite destination
hexList$ = PeekHexBytes(*source, numBytes, " ", "$")
Debug hexList$
Debug PokeHexBytes(*destination, hexList$, " ", "$")
Debug CompareMemory(*source, *destination, numBytes)
PokeA(*destination, 0) ; partially overwrite destination
hexList$ = PeekHexBytes(*source, -1, ",", "$")
Debug hexList$
Debug PokeHexBytes(*destination, hexList$, ",", "$")
Debug CompareMemory(*source, *destination, numBytes)
PokeA(*destination, 0) ; partially overwrite destination
hexList$ = PeekHexBytes(*source, -1, "", "0x")
Debug hexList$
Debug PokeHexBytes(*destination, hexList$, "", "0x")
Debug CompareMemory(*source, *destination, numBytes)
PokeA(*destination, 0) ; partially overwrite destination
hexList$ = PeekHexBytes(*source, numBytes, " ", "0x")
Debug hexList$
Debug PokeHexBytes(*destination, hexList$, " ", "0x")
Debug CompareMemory(*source, *destination, numBytes)
PokeA(*destination, 0) ; partially overwrite destination
hexList$ = PeekHexBytes(*source, -1, ", ", "0x")
Debug hexList$
Debug PokeHexBytes(*destination, hexList$, ", ", "0x")
Debug CompareMemory(*source, *destination, numBytes)
PokeA(*destination, 0) ; partially overwrite destination
Debug "----------------"
; --- Some hexadecimal strings that are NOT accepted by PokeHexBytes() ---
Debug PokeHexBytes(*destination, "123") ; odd number of hex digitis
Debug PokeHexBytes(*destination, "12 3 45", " ") ; only one digit between separators
Debug PokeHexBytes(*destination, "12FX") ; invalid hex digit "X"
Debug PokeHexBytes(*destination, "12,34") ; invalid separator
Debug PokeHexBytes(*destination, ",12", ",") ; separator at the beginning
Debug PokeHexBytes(*destination, "12,,34", ",") ; separator followed by separator
Debug PokeHexBytes(*destination, "12$,34", ",", "$") ; prefix followed by separator
FreeMemory(*source)
FreeMemory(*destination)