03.04.2025, 11:55 PM 
(This post was last modified: 03.04.2025, 11:58 PM by Andy Dee.
 Edit Reason: Added one test DOS outputline.
)
		
	
	
		Hello everyone, 
I developed a method to compress short texts by reducing 8-bit characters to 7-bit. While the compression ratio is low for large files, it efficiently reduces small strings.
Key Steps:
Here is the code, including some testing:
This is the DOS-output:
 
Perhaps the code will also be of use to others...
	
	
	
	
I developed a method to compress short texts by reducing 8-bit characters to 7-bit. While the compression ratio is low for large files, it efficiently reduces small strings.
Key Steps:
- Normalization: The input text is restricted to 7-bit ASCII (32–127).
 
- Offset Adjustment: Each character is decremented by 32 to fit within 0–95. 
 
- Compression: 
 For every 8 bytes, extract bits 0–6 (discarding bit 7).Pack these 7-bit segments into 7 bytes, using all 8 bits per byte.
 
- Remaining Bytes: If the text length isn’t a multiple of 8, the last 1–7 bytes are copied uncompressed.
 
- Decompression: 
 Reverse the process: Expand 7-bit segments to 8 bits, then increment by 32.
 
Here is the code, including some testing:
Code:
$Compile exe
$Dim All
$Lib All Off
$Error All Off
$String 2
$Com 0
$Float Procedure
$Optimize Size'Speed
$Cpu 80386
$Sound 1
$Stack 1536
'----------------------------------------------------------------------------
' Packe limitierten Text von 8 auf 7 Bit.
' Nutze dafür Zeiger zum Erhöhen der Geschwindigkeit
Declare _
FUNCTION PackTxt87 (sQuelle AS STRING) AS STRING
FUNCTION PackTxt87 (sQuelle AS STRING) AS STRING
  ' lokale Variablen
  DIM iQlen  AS LOCAL INTEGER          ' Länge von sQuelle
  DIM Q_ptr  AS LOCAL BYTE PTR         ' Zeichen-Pointer auf sQuelle
  Dim sZiel  As Local String           ' Komprimiertes sZiel
  DIM iZlen  AS LOCAL INTEGER          ' Länge von sZiel
  DIM Z_ptr  AS LOCAL BYTE PTR         ' Zeichen-Pointer auf sZiel
  DIM iZPos  AS LOCAL INTEGER          ' Position in sZiel
  DIM iQPos  AS LOCAL INTEGER          ' Position in sQuelle
  DIM iQLoop AS LOCAL INTEGER          ' Anzahl KompressionsSchleifen
  DIM iKompS AS LOCAL INTEGER          ' Anzahl komprimierbarer Zeichen
  Dim iRest  As Local Integer          ' Anzahl unkomprimierbarer Zeichen
  Dim bWert  As Local Byte             ' ASCII-32-Wert eines Zeichens.
  Dim iBitQ  As Local Integer          ' Aktuelle Bitbosition in sQuelle
  Dim iBitZ  As Local Integer          ' Aktuelle Bitbosition in sZiel
  Dim sTemp  As Local String
                                       '
  iQlen = LEN(sQuelle)                 ' Hole Länge der Quelle
  '--------------------------------------------------------------------------
  ' Fehler 1 abfangen
  If iQlen < 8 Then _                  ' String zu kurz?
    Function = sQuelle: _              ' Gib Original zurück
    Exit Function                      ' und raus
  iQLoop  = Fix(iQlen/8)               ' Hole Anzahl KompressionsSchleifen
  iRest  = iQlen Mod 8                 ' Hole Anzahl unkomprimierbarer Zeichen
  iKompS = iQLoop * 8                  ' Hole Anzahl komprimierbarer Zeichen
  iZlen  = iQLoop * 7 + iRest          ' Hole Länge komprimierten Strings
                                       '
  DECR iQlen                           ' Null-basiert für Pointer
  Q_ptr = STRPTR32( sQuelle )          ' Setze Pointer auf sQuelle
  '--------------------------------------------------------------------------
  ' Fehler 2 abfangen
  For iQPos = 0 To iQlen               ' Überprüfe auf ungültige Zeichen
    IF @Q_ptr[iQPos] < 32 _
      Or @Q_ptr[iQPos] > 159 _
      THEN
      sZiel = sZiel _                  ' sammle diese 
            + Chr$(@Q_ptr[iQPos])
    End If
  Next iQPos
  If Len(sZiel) Then _
    Function = "Falsche Quelle: "+sZiel : _
    Exit Function                      ' und gib sie zurück
  sZiel = Repeat$(iZlen,Chr$(0))       ' Platz für Kompression in sZiel
  Z_ptr = STRPTR32( sZiel  )           ' Setze Pointer auf sZiel
  iZPos = 0                            ' Position im Pointer = 0
  iBitQ = 0                            ' Bitbosition in sQuelle auf 0
  iBitZ = 0                            ' Bitbosition in sZiel  auf 0
  DECR iKompS                          ' Null-basiert für Pointer
  '--------------------------------------------------------------------------
  ' Kompression:
  For iQPos = 0 To iKompS              ' Für komprimierbare Zeichen in sQuelle:
                                       ' 1. Schritt:
    bWert = @Q_ptr[iQPos] - 32         ' ASCII-Wert um 32 vermindern, dass
                                       ' ASCII zwischen 0-159 ist
    For iBitQ = 0 To 6                 ' Lies aus sQuelle Bit 0-6
      If Bit(bWert,iBitQ) = 1 Then     ' Schreibe Ziel-Bits 0-7
        Bit Set @Z_ptr[iZPos], iBitZ   '
      Else                             '
        Bit ReSet @Z_ptr[iZPos], iBitZ '
      End If
      If iBitZ < 7 Then                ' Solange in Ziel zwischen Bit 0 und 7
        Incr iBitZ                     ' Nächstes Bit
      Else                             ' sonst
        iBitZ = 0                      ' Bit 0 in
        Incr iZPos                     ' nächstem Zeichen
      End If
    Next iBitQ
  Next iQPos
  iZPos = iZlen-iRest                  ' Setze Zeiger auf erstes unpackbares
                                       ' Zeichen
  For iQPos = iQlen-iRest+1 To iQlen   ' Für unpackbare Zeichen in sQuelle
    @Z_ptr[iZPos] = @Q_ptr[iQPos]      ' Schreibe unpackbare Zeichen
    Incr iZPos
  Next iQPos
  Function = sZiel
END FUNCTION
'----------------------------------------------------------------------------
' EntPacke limitierten Text von 8 auf 7 Bit.
' Nutze dafür Zeiger zum Erhöhen der Geschwindigkeit
Declare _
FUNCTION DePackTxt87 (sQuelle AS STRING) AS STRING
FUNCTION DePackTxt87 (sQuelle AS STRING) AS STRING
  ' lokale Variablen
  DIM iQlen  AS LOCAL INTEGER          ' Länge von sQuelle
  DIM Q_ptr  AS LOCAL BYTE PTR         ' Zeichen-Pointer auf sQuelle
  Dim sZiel  As Local String           ' Komprimiertes sZiel
  DIM iZlen  AS LOCAL INTEGER          ' Länge von sZiel
  DIM Z_ptr  AS LOCAL BYTE PTR         ' Zeichen-Pointer auf sZiel
  DIM iZPos  AS LOCAL INTEGER          ' Position in sZiel
  DIM iQPos  AS LOCAL INTEGER          ' Position in sQuelle
  DIM iQLoop AS LOCAL INTEGER          ' Anzahl KompressionsSchleifen
  DIM iKompS AS LOCAL INTEGER          ' Anzahl komprimierbarer Zeichen
  Dim iRest  As Local Integer          ' Anzahl unkomprimierbarer Zeichen
  Dim bWert  As Local Byte             ' ASCII-32-Wert eines Zeichens.
  Dim iBitQ  As Local Integer          ' Aktuelle Bitbosition in sQuelle
  Dim iBitZ  As Local Integer          ' Aktuelle Bitbosition in sZiel
  Dim sTemp  As Local String
  Dim bPack  As Local Byte             ' Gepackte Bytes an sQuelleEnde
  iQlen = LEN(sQuelle)                 ' Hole Länge der Quelle
  '--------------------------------------------------------------------------
  ' Fehler 1 abfangen
  If iQlen < 7 Then _                  ' String zu kurz?
    Function = sQuelle: _              ' Gib Original zurück
    Exit Function                      ' und raus
  Q_ptr = STRPTR32( sQuelle )          ' Setze Pointer auf sQuelle
  iQLoop  = Fix(iQlen/7)               ' Hole Anzahl EntPacksSchleifen
  iRest  = iQlen Mod 7                 ' Anzahl ungepackte Zeichen?
  '--------------------------------------------------------------------------
  ' Fehler 2 abfangen
If iRest = 0 Then                      ' Scheint alles gepackt zu sein, dann
  bPack = 0                            ' GePacktes = 0
  For iQPos = iQlen - 7 To iQlen-1     ' Überprfe letzte 7 Zeichen auf
    IF @Q_ptr[iQPos] < 32 _            ' komprimierte Zeichen
      Or @Q_ptr[iQPos] > 127 _         ' Wenn welche da, dann
      THEN Incr bPack                  ' Erhöhe Gepacktes (bPack)
  Next iQPos
End If
  If bPack = 0? _                      ' Wenn keine gepackten Zeichen (bPack)
    And iRest = 0 _                    ' und keine ungepackten Zeichen (iRest)
    Then                               ' dann
    iRest = 7                          ' ungepackte Zeichen (iRest) = 7
    iKompS = iQlen - iRest             ' Setze Anzahl entpackbarer Zeichen
  Else                                 ' sonst
    iKompS = iQLoop * 7                ' Setze Anzahl entpackbarer Zeichen
  End If
  If IsFalse(bPack) _                  ' Sind die iRest-lichen Zeichen
    And iRest = 7 _                    ' ungepackt, dann reduziere
    Then Decr iQLoop                   ' die Anzahl der Entpackaufrufe
  iZlen  = iQLoop * 8 + iRest          ' Hole Länge komprimierten Strings
                                       '
  DECR iQlen                           ' Null-basiert für Pointer
  sZiel = Repeat$(iZlen,Chr$(0))       ' Platz für Kompression in sZiel
  Z_ptr = STRPTR32( sZiel  )           ' Setze Pointer auf sZiel
  iZPos = 0                            ' Position im Pointer = 0
  iBitQ = 0                            ' Bitbosition in sQuelle auf 0
  iBitZ = 0                            ' Bitbosition in sZiel  auf 0
  '--------------------------------------------------------------------------
  ' DeKompression: Rechne Zeichen in sQuelle auf ASCII 32-159 um
  For iQPos = 0 To iKompS              ' Fr entkomprimierbare Zeichen in sQuelle:
                                       ' 1. Schritt:
    For iBitQ = 0 To 7                 ' Lies aus sQuelle Bit 0-7
      If Bit(@Q_ptr[iQPos],iBitQ) = 1 Then ' Schreibe Ziel-Bits 0-7
        Bit Set @Z_ptr[iZPos], iBitZ   '
      Else                             '
        Bit ReSet @Z_ptr[iZPos], iBitZ '
      End If
      If iBitZ < 6 Then                ' Solange in Ziel zwischen Bit 0 und 6
        Incr iBitZ                     ' Nächstes Bit
      Else                             ' sonst
        iBitZ = 0                      ' Bit 0
        Incr @Z_ptr[iZPos], 32         ' Erstelle das richtige ZielZeichen
        Incr iZPos                     ' und nächstes ZielZeichen
      End If
    Next iBitQ
  Next iQPos
  iZPos = iZlen-iRest                  ' Setze Zeiger auf erstes unpackbares
                                       ' Zeichen
  For iQPos = iQlen-iRest+1 To iQlen   ' Für ungepackte Zeichen in sQuelle
    @Z_ptr[iZPos] = @Q_ptr[iQPos]      ' Schreibe ungepackte Zeichen
    Incr iZPos
  Next iQPos
  Function = sZiel
END FUNCTION
' Test-Code'
DIM originalText AS STRING, _
    compressedText AS STRING, _
    decompressedText AS STRING, _
    iIndex As Integer, _
    iLOrg  As Integer, _
    iLPck  As Integer, _
    iLEpk  As Integer
For iIndex = 07 To 19
  '?
  originalText = Left$("PowerBASIC 3.5 is a wonderful tool for writing fast and usefull programs.",iIndex)
  iLOrg = Len(originalText)
  compressedText = PackTxt87(originalText)
  iLPck = Len(compressedText)
  If Instr(compressedText, "Falsche Quelle: ") = 1 Then ?compressedText:end
  decompressedText = DePackTxt87(compressedText)
  iLEpk = Len(decompressedText)
  'PRINT "Original:      ";
  PRINT "Original: ";
  Print originalText;
  $If 0
  Print " ist =";iLOrg;" Byte lang"
  PRINT "Komprimiert:  ";
  Print compressedText;
  Print " ist =";iLPck;" Byte lang"
  PRINT "Dekomprimiert: ";
  Print decompressedText;
  Print " ist =";iLEpk;" Byte lang"
  $EndIf
  ? " --> Test verlief: [erfolg";
  If originalText = decompressedText Then
    Print "reich] :-)  :";iLOrg;iLPck;iLEpk
  Else
  Print "los] ;-(";iLOrg;iLPck;iLEpk
  ?"Original und wieder Entpacktes unterscheiden sich an folgenden Stellen:"
  For iIndex = 1 To iLOrg
    If ASCII(originalText,iIndex) <> ASCII(decompressedText,iIndex) Then
        ?"Position";iIndex;" - Zeichen Original: ";Chr$(ASCII(originalText,iIndex)); _
        " - Zeichen Entpackt: ";Chr$(ASCII(decompressedText,iIndex))
      End If
    Next iIndex
  End If
Next iIndex
Sleep
originalText = "PowerBASIC 3.5 is a wonderful tool for writing fast, small and usefull programs under DOS."
iLOrg = Len(originalText)
compressedText = PackTxt87(originalText)
iLPck = Len(compressedText)
If Instr(compressedText, "Falsche Quelle: ") = 1 Then ?compressedText:end
decompressedText = DePackTxt87(compressedText)
iLEpk = Len(decompressedText)
  
  PRINT "Original:"
  Print originalText;
  ? " --> Test verlief: [erfolg";
  If originalText = decompressedText Then
    Print "reich] :-)  :";iLOrg;iLPck;iLEpk
  Else
  Print "los] ;-(";iLOrg;iLPck;iLEpk
  ?"Original und wieder Entpacktes unterscheiden sich an folgenden Stellen:"
  For iIndex = 1 To iLOrg
    If ASCII(originalText,iIndex) <> ASCII(decompressedText,iIndex) Then
        ?"Position";iIndex;" - Zeichen Original: ";Chr$(ASCII(originalText,iIndex)); _
        " - Zeichen Entpackt: ";Chr$(ASCII(decompressedText,iIndex))
      End If
    Next iIndex
  End If
  
  PRINT "Komprimiert:"
  Print compressedText
  Print "ist =";iLPck;" Byte lang"
  PRINT "Dekomprimiert:"
  Print decompressedText
  Print "ist =";iLEpk;" Byte lang"
originalText = "PowerBASIC 3.5 ist ein wundervolles Werkzeug fr schnelle, kleine bis groáe und vor allen ¯ntzliche® Programme unter DOS."
?originalText
iLOrg = Len(originalText)
compressedText = PackTxt87(originalText)
iLPck = Len(compressedText)
If Instr(compressedText, "Falsche Quelle: ") = 1 Then ?compressedText:end
decompressedText = DePackTxt87(compressedText)
iLEpk = Len(decompressedText)
  
  PRINT "Original:"
  Print originalText;
  ? " --> Test verlief: [erfolg";
  If originalText = decompressedText Then
    Print "reich] :-)  :";iLOrg;iLPck;iLEpk
  Else
  Print "los] ;-(";iLOrg;iLPck;iLEpk
  ?"Original und wieder Entpacktes unterscheiden sich an folgenden Stellen:"
  For iIndex = 1 To iLOrg
    If ASCII(originalText,iIndex) <> ASCII(decompressedText,iIndex) Then
        ?"Position";iIndex;" - Zeichen Original: ";Chr$(ASCII(originalText,iIndex)); _
        " - Zeichen Entpackt: ";Chr$(ASCII(decompressedText,iIndex))
      End If
    Next iIndex
  End If
  
  PRINT "Komprimiert:"
  Print compressedText
  Print "ist =";iLPck;" Byte lang"
  PRINT "Dekomprimiert:"
  Print decompressedText
  Print "ist =";iLEpk;" Byte lang"This is the DOS-output:
Code:
Original: PowerBA --> Test verlief: [erfolgreich] :-)  : 7  7  7
Original: PowerBAS --> Test verlief: [erfolgreich] :-)  : 8  7  8
Original: PowerBASI --> Test verlief: [erfolgreich] :-)  : 9  8  9
Original: PowerBASIC --> Test verlief: [erfolgreich] :-)  : 10  9  10
Original: PowerBASIC  --> Test verlief: [erfolgreich] :-)  : 11  10  11
Original: PowerBASIC 3 --> Test verlief: [erfolgreich] :-)  : 12  11  12
Original: PowerBASIC 3. --> Test verlief: [erfolgreich] :-)  : 13  12  13
Original: PowerBASIC 3.5 --> Test verlief: [erfolgreich] :-)  : 14  13  14
Original: PowerBASIC 3.5  --> Test verlief: [erfolgreich] :-)  : 15  14  15
Original: PowerBASIC 3.5 i --> Test verlief: [erfolgreich] :-)  : 16  14  16
Original: PowerBASIC 3.5 is --> Test verlief: [erfolgreich] :-)  : 17  15  17
Original: PowerBASIC 3.5 is  --> Test verlief: [erfolgreich] :-)  : 18  16  18
Original: PowerBASIC 3.5 is a --> Test verlief: [erfolgreich] :-)  : 19  17  19
Original:PowerBASIC 3.5 is a wonderful tool for writing fast, small and usefull
programs under DOS. --> Test verlief: [erfolgreich] :-)  : 90  79  90
Komprimiert:
░τ╡(§àf⌐◄`Γ¿ ÆS@►p}:ëE⌐▒╩♦PƒO&└°ö☻«╥$5Θ<☻î┴)ò☺ÿ6âL& Φ$☻¬╙ó▒╩d☻á╥τQ→lN☺U'▒(♣É^S.
ist = 79  Byte lang
Dekomprimiert:
PowerBASIC 3.5 is a wonderful tool for writing fast, small and usefull programs
under DOS.
ist = 90  Byte lang
PowerBASIC 3.5 ist ein wundervolles Werkzeug für schnelle, kleine bis große und
vor allen »nützliche« Programme unter DOS. 
Falsche Quelle: ß»« Perhaps the code will also be of use to others...

 

