How to encode and decode any string into Base‑36 in Pascal

1 Answer

0 votes
program Base36Codec;

{$mode objfpc}{$H+}{$J-}

{
    Base‑36 encoding/decoding in Free Pascal
    --------------------------------------------------
    Base‑36 digits: 0–9, A–Z

    Encoding:
      - Convert string → big integer (base‑256)
      - Convert big integer → base‑36

    Decoding:
      - Convert base‑36 → big integer
      - Convert big integer → original bytes

    We store the big integer as an array of 32‑bit chunks (little‑endian).
}

uses
  SysUtils;

const
  Base36Digits: string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

{ Convert Base‑36 character → numeric value }
function Base36Value(c: Char): Integer;
begin
  if (c >= '0') and (c <= '9') then
    Result := Ord(c) - Ord('0')
  else
    Result := Ord(c) - Ord('A') + 10;
end;

{
    EncodeToBase36:
    ---------------
    Converts a Pascal string into Base‑36 text.

    Uses manual big‑integer arithmetic:
      number[i] holds a 32‑bit chunk of the integer.
}
function EncodeToBase36(const input: string): string;
var
  number: array of Cardinal;
  nsize: Integer;
  i, j: Integer;
  carry: Cardinal;
  value: QWord;
  remainder: Cardinal;
  encoded: string;
  c: Char;
begin
  SetLength(number, Length(input) + 4);
  number[0] := 0;
  nsize := 1;

  { Step 1: Convert string → big integer (base‑256) }
  for i := 1 to Length(input) do
  begin
    carry := Ord(input[i]);

    for j := 0 to nsize - 1 do
    begin
      value := QWord(number[j]) * 256 + carry;
      number[j] := Cardinal(value and $FFFFFFFF);
      carry := Cardinal(value shr 32);
    end;

    if carry > 0 then
    begin
      number[nsize] := carry;
      Inc(nsize);
    end;
  end;

  { Step 2: Convert big integer → Base‑36 }
  encoded := '';

  while not ((nsize = 1) and (number[0] = 0)) do
  begin
    remainder := 0;

    for j := nsize - 1 downto 0 do
    begin
      value := (QWord(remainder) shl 32) or number[j];
      number[j] := Cardinal(value div 36);
      remainder := Cardinal(value mod 36);
    end;

    while (nsize > 1) and (number[nsize - 1] = 0) do
      Dec(nsize);

    encoded := encoded + Base36Digits[remainder + 1];
  end;

  { Reverse the Base‑36 output }
  for i := 1 to Length(encoded) div 2 do
  begin
    c := encoded[i];
    encoded[i] := encoded[Length(encoded) - i + 1];
    encoded[Length(encoded) - i + 1] := c;
  end;

  Result := encoded;
end;

{
    DecodeFromBase36:
    -----------------
    Converts Base‑36 text back into the original string.

    Steps:
      1. Convert Base‑36 → big integer
      2. Convert big integer → bytes (base‑256)
}
function DecodeFromBase36(const encoded: string): string;
var
  number: array of Cardinal;
  nsize: Integer;
  i, j: Integer;
  digit: Integer;
  carry: Cardinal;
  value: QWord;
  remainder: Cardinal;
  output: string;
  c: Char;
begin
  SetLength(number, Length(encoded) + 4);
  number[0] := 0;
  nsize := 1;

  { Step 1: Base‑36 → big integer }
  for i := 1 to Length(encoded) do
  begin
    digit := Base36Value(encoded[i]);
    carry := digit;

    for j := 0 to nsize - 1 do
    begin
      value := QWord(number[j]) * 36 + carry;
      number[j] := Cardinal(value and $FFFFFFFF);
      carry := Cardinal(value shr 32);
    end;

    if carry > 0 then
    begin
      number[nsize] := carry;
      Inc(nsize);
    end;
  end;

  { Step 2: big integer → original bytes }
  output := '';

  while not ((nsize = 1) and (number[0] = 0)) do
  begin
    remainder := 0;

    for j := nsize - 1 downto 0 do
    begin
      value := (QWord(remainder) shl 32) or number[j];
      number[j] := Cardinal(value div 256);
      remainder := Cardinal(value mod 256);
    end;

    while (nsize > 1) and (number[nsize - 1] = 0) do
      Dec(nsize);

    output := output + Chr(remainder);
  end;

  { Reverse the output bytes }
  for i := 1 to Length(output) div 2 do
  begin
    c := output[i];
    output[i] := output[Length(output) - i + 1];
    output[Length(output) - i + 1] := c;
  end;

  Result := output;
end;

{ Main program }
var
  text, encoded, decoded: string;
begin
  text := 'Hello Universe!';
  encoded := EncodeToBase36(text);
  decoded := DecodeFromBase36(encoded);

  WriteLn('Original: ', text);
  WriteLn('Base-36 encoded: ', encoded);
  WriteLn('Decoded: ', decoded);
end.



(*
run:

Original: Hello Universe!
Base-36 encoded: LP4N024HJ1YVBVD84Y8TYQP
Decoded: Hello Universe!

*)

 



answered 3 days ago by avibootz
edited 3 days ago by avibootz
...