Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Binary Buffer Copy and Compare


  • Please log in to reply
No replies to this topic
Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Since AHK does not yet directly support binary buffers (assignment truncates the variable at the first NULL; a=b -type comparisons ignore bytes after the first NULL), we need DLL calls for the basic operations. Following Chris' advice, I wrote the necessary functions. Unfortunately, the C runtime library msvcrt.dll is needed (most systems have it anyway).

StrLen does not work for binary buffers, either (only tell where the first NULL is), so these functions need an extra parameter, the actual length of the buffers (which can be smaller than the allocated memory). Setting the length to 0 makes the functions use all the allocated space for the buffers.

Hex2Bin and Bin2Hex convert data between binary form and double-digit hex strings.
BufferCopy copies (part of) a binary buffer into another variable.
BufferCompare compares two binary variables. The result is 0 at equality, otherwise the first different bytes determine the sign of the returned value: negative if the first parameter is smaller, positive if it is larger than the second parameter.

First some calling examples are given, with listing all the variables. The actual functions follow.
hModule := DllCall("LoadLibrary","str","msvcrt.dll") ; Preloads C library

Hex2Bin(b0,"000102030405060708090a0b0c0d0e0f00")
Hex2Bin(b1,"000102030405060708090a0b0c0d0e0f01")
BufferCopy(a,b0,0,17)
BufferCopy(c,b0,2,15)
Bin2Hex(g,a,17)
Bin2Hex(h,c,15)
x := BufferCompare(a, b0,17)
y := BufferCompare(b1,b0,17)
MsgBox %g%`n%h%`n%x%`n%y%
GoSub $                                ; ListVars for debug

DllCall("FreeLibrary", "UInt", hModule)
ExitApp

BufferCompare(ByRef a, Byref b, n=0)   ; compare buffers
{                                      ; a <,=,> b: Return <,=,> 0
   u := VarSetCapacity(a)
   v := VarSetCapacity(b)
   IfLess n, 1,  SetEnv n,0xffffffff   ; n = 0: compare all allocated
   IfLess u,%n%, SetEnv n,%u%          ; use at most the capacity of a
   IfLess v,%n%, SetEnv n,%v%          ; use at most the capacity of b
   Return DllCall("msvcrt\memcmp", "UInt", &a, "UInt", &b, "UInt", n, "CDecl Int")
}

BufferCopy(ByRef a, Byref b, b0=0,n=0) ; copy buffer a <- b[b0 + 0..n-1]
{
   m := VarSetCapacity(b)-b0
   IfLess m,1, {                       ; nothing is left
      a =
      Return
   }
   IfLess n, 1,  SetEnv n,0xffffffff   ; n = 0: copy all allocated memory
   IfLess m,%n%, SetEnv n,%m%          ; copy at most the capacity
   Granted := VarSetCapacity(a, n, 33) ; make sure a is large enough
   IfLess Granted,%n%, {
      ErrorLevel = Mem=%Granted%       ; Error if not enough memory
      Return
   }
   StringLeft a, a, n                  ; trim to the right length
   DllCall("RtlMoveMemory", "UInt", &a, "UInt", &b + b0, "UInt", n)
}

Bin2Hex(ByRef h, ByRef b, n=0)      ; n bytes binary data -> stream of 2-digit hex
{                                   ; n = 0: all (SetCapacity can be larger than used!)
   format = %A_FormatInteger%       ; save original integer format
   SetFormat Integer, Hex           ; for converting bytes to hex

   m := VarSetCapacity(b)
   If (n < 1 or n > m)
       n := m
   Address := &b
   h =
   Loop %n%
   {
      x := *Address                 ; get byte in hex
      StringTrimLeft x, x, 2        ; remove 0x
      x = 0%x%                      ; pad left
      StringRight x, x, 2           ; 2 hex digits
      h = %h%%x%
      Address++
   }
   SetFormat Integer, %format%      ; restore original format
}

Hex2Bin(ByRef b, h, n=0)            ; n hex digit-pairs -> binary data
{                                   ; n = 0: all. (Only ByRef can handle binaries)
   m := Ceil(StrLen(h)/2)
   If (n < 1 or n > m)
       n := m
   Granted := VarSetCapacity(b, n, 33)
   IfLess Granted,%n%, {
      ErrorLevel = Mem=%Granted%
      Return
   }
   StringLeft b, b, n
   Address := &b
   Loop %n%
   {
      StringLeft  x, h, 2
      StringTrimLeft h, h, 2
      x = 0x%x%
      DllCall("RtlFillMemory", "UInt", Address, "UInt", 1, "UChar", x)
      Address++
   }
}

$:
ShowVars:            ; list vars, wait until closed (ESC, [x])
   ListVars          ; Subroutine shows function locals, too
   WinGet ID, ID, A  ; note ListVars window ID
   Loop
   {
      Sleep 30
      IfWinNotExist ahk_id %ID%
         break
   }
Return