Base91Legacy encoding/decoding procedures

Started by Theo Gottwald, November 26, 2023, 06:18:53 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Theo Gottwald

Base91 encoding is a method for converting binary data into a text format, specifically designed to be more efficient than traditional Base64 encoding. It's particularly useful in contexts where space is at a premium, such as in network protocols or data storage. Base91 achieves higher efficiency by using a larger set of characters for encoding, thus representing more data per character. 🚀$crlf$crlf
Key Features of Base91: 🗝�$crlf
- Efficiency: Base91 uses 91 characters for encoding, compared to Base64's 64 characters, allowing it to encode data more compactly. 📊$crlf
- Binary-Safe: It can encode any binary data, including images and executable files. 🖼�💾$crlf
- No Padding Required: Unlike Base64, Base91 doesn't require padding characters at the end of the encoded string. ✂️$crlf$crlf
How It Works: 🔍$crlf
Encoding Process: 🔄$crlf
1. It takes binary data and groups the bits. 🧬$crlf
2. These groups of bits are then mapped to one of the 91 characters in the encoding table. 🗺�$crlf
3. This process continues until all binary data is converted into a string of Base91 characters. 🔤$crlf$crlf
Decoding Process: 🔙$crlf
1. The reverse happens. Each Base91 character is mapped back to its binary representation. 🔢$crlf
2. These bits are then reassembled into the original binary data. 🛠�$crlf$crlf
#Base91 #Encoding #Efficiency #DataStorage #NetworkProtocols #BinaryData #TechInnovation #NoPaddingRequired #CompactData 🌐🔐💻📡🔢🛠�🚀🗝�📊🖼�💾✂️🔄🗺�🔤🔍🔙


Needed Shift-Functions:
#compile exe  "demo_ShiftFunct.exe"
#dim all
#if %def(%pb_cc32) 'Either PBWin or PBCC, without unneeded console in PBCC.
  #console off
#endif

fastproc ShiftLeft (byval ToShift as long, byval ShiftBits as long) as long
  ! mov ecx, ShiftBits
  ! mov ebx, ToShift
  ! shl ebx, cl
  ! mov ToShift, ebx
end fastproc = ToShift
fastproc ShiftRight (byval ToShift as long, byval ShiftBits as long) as long
  ! mov ecx, ShiftBits
  ! mov ebx, ToShift
  ! shr ebx, cl
  ! mov ToShift, ebx
end fastproc = ToShift
fastproc ShiftLeftSigned (byval ToShift as long, byval ShiftBits as long) as long
  ! mov ecx, ShiftBits
  ! mov ebx, ToShift
  ! sar ebx, cl
  ! mov ToShift, ebx
end fastproc = ToShift
fastproc ShiftRightSigned (byval ToShift as long, byval ShiftBits as long) as long
  ! mov ecx, ShiftBits
  ! mov ebx, ToShift
  ! sal ebx, cl
  ! mov ToShift, ebx
end fastproc = ToShift
'
'PBMain is only to demonstrate the "functions".
function pbmain () as long
  local hTWin as dword

  txt.window "Shift Demo", 50, 50, 12, 45 to hTWin
  txt.print str$(ShiftLeft (14, 1))  + " should be 28"
  txt.print str$(ShiftRight (14, 1))  + " should be 7"
  txt.print str$(ShiftLeftSigned (-14, 1)) + " should be -7"
  txt.print str$(ShiftRightSigned (-14, 1)) + " should be -28"
  txt.print "Shows negatives unsigned shifted"
  txt.print str$(ShiftLeft (-14, 1))
  txt.print str$(ShiftRight (-14, 1)) + " <--- 0 bit shifted into sign"

  txt.waitkey$
  txt.end
end function '



' Base91Legacy encoding/decoding procedures. Not much to say about it
' and probably not very useful (?), but a question was asked and I found
' some fun code examples to translate - so here are PB versions of it.
' Public Domain, as usual, by Borje Hagsten 27 Sep, 2023
' Modified by Stuart McLachlan 10 Oct 2023 - (Shift Macros and Bitwise conditional)
'--------------------------------------------------------------------
#COMPILE EXE
#DIM ALL
#INCLUDE "WIN32API.INC"
%IDC_LABEL1    = 101
%IDC_LABEL2    = 102
%IDC_BUTTON1   = 121
%IDC_BUTTON2   = 122
%IDC_TEXTBOX1  = 131
%IDC_TEXTBOX2  = 132
GLOBAL arrB91() AS STRING

MACRO SHIFTL(v,n) = v * (2^n)
MACRO SHIFTR(v,n) = v \ (2^n)

'======================================================================
FUNCTION PBMAIN () AS LONG
  LOCAL hDlg AS DWORD

  DIALOG NEW 0, "Base91 encryption",,, 185, 115, %WS_CAPTION OR _
             %WS_SYSMENU OR %WS_MINIMIZEBOX, 0 TO hDlg

  '------------------------------------------------------------------
  CONTROL ADD LABEL, hDlg, %IDC_LABEL1, "Plain text", 5, 1, 60, 9
  CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, "Hello World!", 5, 10, 120, 45, _
            %WS_CHILD OR %WS_VISIBLE OR %WS_VSCROLL OR %ES_MULTILINE OR _
            %ES_WANTRETURN, %WS_EX_CLIENTEDGE

  CONTROL ADD LABEL, hDlg, %IDC_LABEL2, "Encrypted text", 5, 56, 60, 9
  CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX2, "", 5, 65, 120, 45, _
            %WS_CHILD OR %WS_VISIBLE OR %WS_VSCROLL OR %ES_MULTILINE OR _
            %ES_WANTRETURN, %WS_EX_CLIENTEDGE

  CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "Encode", 130, 10, 50, 14
  CONTROL ADD BUTTON, hDlg, %IDC_BUTTON2, "Decode", 130, 65, 50, 14

  '-------------------------------------------------------------------
  DIALOG SHOW MODAL hDlg, CALL DlgProc

END FUNCTION

'======================================================================
CALLBACK FUNCTION DlgProc() AS LONG
  LOCAL sBuf AS STRING

  SELECT CASE CB.MSG
  CASE %WM_INITDIALOG
      REDIM arrB91(90)  ' build zero-based (0-90) global Base91Legacy array
      ARRAY ASSIGN arrB91() = "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", _
                              "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", _
                              "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", _
                              "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", _
                              "4", "5", "6", "7", "8", "9", "!", "#", "$", "%", "&", "(", ")", "*", _
                              "+", ",", ".", "/", ":", ";", "<", "=", ">", "?", "@", "[", "]", "^", _
                              "_", "`", "{", "|", "}", "~", CHR$(34)

  CASE %WM_COMMAND
      SELECT CASE CB.CTL
      CASE %IDC_BUTTON1  'Encode
          IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
              CONTROL GET TEXT CB.HNDL, %IDC_TEXTBOX1 TO sBuf
              CONTROL SET TEXT CB.HNDL, %IDC_TEXTBOX1, ""
              CONTROL SET TEXT CB.HNDL, %IDC_LABEL1, ""
              CONTROL SET TEXT CB.HNDL, %IDC_LABEL2, sBuf
              CONTROL SET TEXT CB.HNDL, %IDC_TEXTBOX2, Base91Encode(sBuf)
          END IF

      CASE %IDC_BUTTON2 ' Decode
          IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
              CONTROL GET TEXT CB.HNDL, %IDC_TEXTBOX2 TO sBuf
              CONTROL SET TEXT CB.HNDL, %IDC_TEXTBOX2, ""
              CONTROL SET TEXT CB.HNDL, %IDC_LABEL1, sBuf
              CONTROL SET TEXT CB.HNDL, %IDC_LABEL2, ""
              CONTROL SET TEXT CB.HNDL, %IDC_TEXTBOX1, Base91Decode(sBuf)
          END IF

      CASE %IDCANCEL
          IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
              DIALOG END CB.HNDL, 0
          END IF
      END SELECT

  END SELECT

END FUNCTION

'==============================================
FUNCTION Base91Encode(sBuf AS STRING) AS STRING
  LOCAL b, i, j, n, v AS LONG
  LOCAL sOut AS STRING

  FOR i = 1 TO LEN(sBuf)
      j = ASC(sBuf, i)
      SHIFT LEFT j, n
      b OR= j
      n += 8

      IF n > 13 THEN
          v = b AND 8191
          IF v > 88 THEN
            SHIFT RIGHT b, 13
            n -= 13
          ELSE
              v = b AND 16383
              SHIFT RIGHT b, 14
              n -= 14
          END IF
          sOut += arrB91(INT((v MOD 91))) & arrB91(INT((v / 91)))
      END IF
  NEXT i

  IF n THEN
      sOut += arrB91(INT((b MOD 91)))
      IF n > 7 OR b > 90 THEN
          sOut += arrB91(INT((b / 91)))
      END IF
  END IF

  FUNCTION = sOut
END FUNCTION

'==============================================
FUNCTION Base91Decode(sBuf AS STRING) AS STRING
  LOCAL b, i, j, n, v AS LONG
  LOCAL sOut AS STRING
  v = -1

  FOR i = 1 TO LEN(sBuf)
      ARRAY SCAN arrB91(), = MID$(sBuf, i, 1), TO j
      IF j = 0 THEN ITERATE FOR
      DECR j
      IF v < 0 THEN
          v = j
      ELSE
          v += j * 91
          b OR= SHIFTL(v,n)
          n += IIF((v AND 8191) > 88, 13, 14)
          DO
              sOut += CHR$(b AND &HFF)
              SHIFT RIGHT b, 8
              n -= 8
          LOOP WHILE (n > 7)
          v = -1
      END IF
  NEXT
  IF (v + 1) THEN
      SHIFT LEFT v, n
      sOut += CHR$((b OR v) AND &HFF)
  END IF

  FUNCTION = sOut
END FUNCTION
'


Version 2, slightly improved:

' Base91Legacy encoding/decoding procedures improved for robustness and readability.
' Original by Borje Hagsten and Stuart McLachlan, enhanced by OpenAI.
'--------------------------------------------------------------------
#COMPILE EXE
#DIM ALL
#INCLUDE "WIN32API.INC"

%IDC_LABEL1    = 101
%IDC_LABEL2    = 102
%IDC_BUTTON1   = 121
%IDC_BUTTON2   = 122
%IDC_TEXTBOX1  = 131
%IDC_TEXTBOX2  = 132
GLOBAL arrB91() AS STRING

MACRO SHIFTL(v,n) = v * (2^n)
MACRO SHIFTR(v,n) = v \ (2^n)

'======================================================================
FUNCTION PBMAIN () AS LONG
  LOCAL hDlg AS DWORD

  DIALOG NEW 0, "Base91 encryption",,, 185, 115, %WS_CAPTION OR _
             %WS_SYSMENU OR %WS_MINIMIZEBOX, 0 TO hDlg

  CONTROL ADD LABEL, hDlg, %IDC_LABEL1, "Plain text", 5, 1, 60, 9
  CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, "Hello World!", 5, 10, 120, 45, _
            %WS_CHILD OR %WS_VISIBLE OR %WS_VSCROLL OR %ES_MULTILINE OR _
            %ES_WANTRETURN, %WS_EX_CLIENTEDGE

  CONTROL ADD LABEL, hDlg, %IDC_LABEL2, "Encrypted text", 5, 56, 60, 9
  CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX2, "", 5, 65, 120, 45, _
            %WS_CHILD OR %WS_VISIBLE OR %WS_VSCROLL OR %ES_MULTILINE OR _
            %ES_WANTRETURN, %WS_EX_CLIENTEDGE

  CONTROL ADD BUTTON, hDlg, %IDC_BUTTON1, "Encode", 130, 10, 50, 14
  CONTROL ADD BUTTON, hDlg, %IDC_BUTTON2, "Decode", 130, 65, 50, 14

  DIALOG SHOW MODAL hDlg, CALL DlgProc

END FUNCTION

'======================================================================
CALLBACK FUNCTION DlgProc() AS LONG
  LOCAL sInput, sOutput AS STRING

  SELECT CASE CB.MSG
  CASE %WM_INITDIALOG
      REDIM arrB91(90)  ' build zero-based (0-90) global Base91Legacy array
      ARRAY ASSIGN arrB91() = "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", _
                              "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", _
                              "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", _
                              "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", _
                              "4", "5", "6", "7", "8", "9", "!", "#", "$", "%", "&", "(", ")", "*", _
                              "+", ",", ".", "/", ":", ";", "<", "=", ">", "?", "@", "[", "]", "^", _
                              "_", "`", "{", "|", "}", "~", CHR$(34)

  CASE %WM_COMMAND
      SELECT CASE CB.CTL
      CASE %IDC_BUTTON1  'Encode
          IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
              CONTROL GET TEXT CB.HNDL, %IDC_TEXTBOX1 TO sInput
              sOutput = Base91Encode(sInput)
              CONTROL SET TEXT CB.HNDL, %IDC_TEXTBOX2, sOutput
          END IF

      CASE %IDC_BUTTON2 'Decode
          IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
              CONTROL GET TEXT CB.HNDL, %IDC_TEXTBOX2 TO sInput
              sOutput = Base91Decode(sInput)
              CONTROL SET TEXT CB.HNDL, %IDC_TEXTBOX1, sOutput