Search code examples
hexdecodeforthgforth

Decode a message encoded in hexadecimal, with Forth


I was trying to convert a message encoded in hexadecimal, in Forth. The following three words came out. It works, but it seems ugly, and unnecessarily complicated.

Also, this is not a real solution, as I wanted to have the result stored in another string for further manipulations ( and not typed on screen as it is now), but I don't know how to add a number (representing a char) to an address...

So, how do you think this must be done, even with very long messages? What is the best way?

\ This work on Gforth 0.7.3

: print ( addr u -- ) hex evaluate decimal emit ;
\ s" 48" print -> H

: hex-decode-char ( addr u n -- ) >r drop r> 2 * + 2 print ;
\ s" 48656C6C6F20776F726C6421" 0 hex-decode-char -> H

: hex-decode-string ( addr u -- ) 2dup dup 2 / 0 ?do i hex-decode-char 2dup loop 2drop 2drop ;
\ s" 48656C6C6F20776F726C6421" hex-decode-string -> Hello world!

Solution

  • A reusable unit for the give problem is decode-hex-string ( a1 u1 a2 u2 -- a2 u ) word that converts string ( a1 u1 ) into buffer ( a2 u2 ) and returns string (or binary data in general case) ( a2 u ). Actually, string and buffer are almost the same: string is a buffer that contains textual data. Also we assume that char size and address unit size is 1 byte.

    : b! c! ; \ store byte in case of 1 char size is 1 bite
    
    : s-to-n ( addr u base -- x ) \ convert string into number with the given radix
      base @ >r base !
      0. 2swap >number ( d a2 u2 )
      r> base !
      nip or if -11 throw then
    ;
    : hex-to-number ( a1 u1 -- x ) 16 s-to-n ;
    
    : decode-hex-string ( a1 u1 a2 u2 -- a2 u )
      rot 2/ umin 2dup 2>r ( a1 a2 u )
      over + swap ?do dup 2 hex-to-number i b! 2 + loop drop 2r>
    ;
    
    \ test
    s" 48656C6C6F20776F726C6421" here 100 decode-hex-string cr type cr