| 
 PB 3.5 8-to-7 Bit Text Compression - Andy Dee -  03.04.2025
 
 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:
 
 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  7Original: 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...
 
 
 
 |