[futurebasic] Base64 Encoding, Decoding & Bitshifting...

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : December 1997 : Group Archive : Group : All Groups

From: Michael Evans <mikonic@...>
Date: Mon, 22 Dec 1997 12:27:20 -0500
Ok you bitshifters out there.

I thought I would try to add a Base64 encoding routine to Greg Neagle's
great TCP demo and then try to figure out how to enable e-mailing binary
attachments from FB. I have a PG:PRO/FB project which is working correctly.
Or at least I can decode what I'm encoding.

I tried using bitshifting but quickly became hopelessly tangled so I
resorted to a brute force solution of using BIN$ and string manipulation.
Needless to say the encoding and decoding are very slow.

Base64 encoding requires that you grab data from the source uncoded data
stream in three byte chunks. Concatenating the three bytes gives you 24
bits. Divide the 24 bits into four seperate 6-bit chunks. The value of the
6-bit chunks can vary between 0 to 63. Based on the four 6-bit values, get
a single character for each value from a lookup table which gives you four
output bytes.
(My internal terminology: A triad is a group of three bytes a quad is a
group of four bytes
Thus:
<snip>
          outHandleSize&     = FN getEncodedHsize(srcHandleSize&)
          quadCount& = outHandleSize&/4
          wholeTriadCount&   = srcHandleSize&/3
          triadRemnants%     = srcHandleSize& MOD 3

          InputStart&        = [theInputHandle&]
          LONG IF triadRemnants%
            LONG IF triadRemnants% = 1
              InputEnd&      = InputStart& + srcHandleSize& - 3 - 1
            END IF
            LONG IF triadRemnants% = 2
              InputEnd&      = InputStart& + srcHandleSize& - 3 - 2
            END IF
          XELSE
            InputEnd&        = InputStart& + srcHandleSize& - 3
          END IF

          theEncodedHandle& = FN NEWHANDLE(outHandleSize&)

          LONG IF theEncodedHandle&
            myErr%               = FN HLOCK(theEncodedHandle&)
            outOffset& = [theEncodedHandle&]

            FOR count& = InputStart& TO InputEnd& STEP 3

              Byte1% = PEEK(count& + 0)
              Byte2% = PEEK(count& + 1)
              byte3% = PEEK(count& + 2)

              Bin1$ = RIGHT$(BIN$(Byte1%),8)
              Bin2$ = RIGHT$(BIN$(Byte2%),8)
              Bin3$ = RIGHT$(BIN$(Byte3%),8)

              leftBin1$         = LEFT$(Bin1$, 6)
              rightBin1$        = RIGHT$(Bin1$, 2)

              leftBin2$         = LEFT$(Bin2$, 4)
              rightBin2$        = RIGHT$(Bin2$, 4)

              leftBin3$         = LEFT$(Bin3$, 2)
              rightBin3$        = RIGHT$(Bin3$, 6)

              outBin1$         = "&X00" + leftBin1$
              outBin2$         = "&X00" + rightBin1$ + leftBin2$
              outBin3$         = "&X00" + rightBin2$ + leftBin3$
              outBin4$         = "&X00" + rightBin3$

              outByte1%       = VAL(outBin1$)
              outByte2%       = VAL(outBin2$)
              outByte3%       = VAL(outBin3$)
              outByte4%       = VAL(outBin4$)

              outByte1%       = PEEK([gBase64LookUpH&] + outByte1%)
              outByte2%       = PEEK([gBase64LookUpH&] + outByte2%)
              outByte3%       = PEEK([gBase64LookUpH&] + outByte3%)
              outByte4%       = PEEK([gBase64LookUpH&] + outByte4%)

              POKE outOffset& + 0, outByte1%
              POKE outOffset& + 1, outByte2%
              POKE outOffset& + 2, outByte3%
              POKE outOffset& + 3, outByte4%
              outOffset& = outOffset& + 4
<snip>
Decoding is the reverse of the encoding process: Grab data from the source
encoded data stream in four byte chunks. Reverse the lookup process and get
the 6-bit value associated with each of the four bytes. Concatenating the
four 6-bit values gives you 24 bits. Divide the 24 bits into three seperate
8-bit chunks.  These are your decoded values. Thus:

<snip>
            TheStart& = [theEncodedHandle&]
            theEnd& = TheStart& + encodedHsize& - 4
            lastByte% = PEEK(TheStart& + encodedHsize&-1)
            LONG IF lastByte% = ASC("=")
              padCount% = padCount% + 1
              secondLastByte% = PEEK(TheStart& + encodedHsize& - 2)
              LONG IF secondLastByte% = ASC("=")
                padCount% = padCount% + 1
              END IF
            END IF
            decodedHsize& = FN getDecodedHsize(encodedHsize&, padCount%)
            theDecodedHandle& = FN NEWHANDLE(decodedHsize&)
            decodeOffset& = [theDecodedHandle&]
            LONG IF theDecodedHandle&
              myErr% = FN HLOCK(theDecodedHandle&)

              FOR count& = TheStart& TO theEnd& STEP 4

                theChar$ = CHR$(PEEK(count& + 0))
                code1% = FN getBase64Reverse%(theChar$)
                testOffset& = testOffset& + 1

                theChar$ = CHR$(PEEK(count& + 1))
                code2% = FN getBase64Reverse%(theChar$)
                testOffset& = testOffset& + 1

                theChar$ = CHR$(PEEK(count& + 2))
                LONG IF theChar$ <> "="
                  code3% = FN getBase64Reverse%(theChar$)
                XELSE
                  code3% = 0
                END IF
                testOffset& = testOffset& + 1

                theChar$ = CHR$(PEEK(count& + 3))
                LONG IF theChar$ <> "="
                  code4% = FN getBase64Reverse%(theChar$)
                XELSE
                  code4% = 0
                END IF
                testOffset& = testOffset& + 1

                bigBin$ = RIGHT$(BIN$(code1%),6)
                bigBin$ = bigBin$ + RIGHT$(BIN$(code2%),6)
                bigBin$ = bigBin$ + RIGHT$(BIN$(code3%),6)
                bigBin$ = bigBin$ + RIGHT$(BIN$(code4%),6)

                bin1$ = "&X" + MID$(bigBin$, 1, 8)
                bin2$ = "&X" + MID$(bigBin$, 9, 8)
                bin3$ = "&X" + MID$(bigBin$, 17, 8)

                outByte1% = VAL(bin1$)
                outByte2% = VAL(bin2$)
                outByte3% = VAL(bin3$)


                LONG IF count& < theEnd&
                  POKE decodeOffset& + 0, outByte1%
                  POKE decodeOffset& + 1, outByte2%
                  POKE decodeOffset& + 2, outByte3%
                  decodeOffset& = decodeOffset& + 3
                XELSE
                  SELECT padCount%
                    CASE 0
                      POKE decodeOffset& + 0, outByte1%
                      POKE decodeOffset& + 1, outByte2%
                      POKE decodeOffset& + 2, outByte3%
                    CASE 1
                      POKE decodeOffset& + 0, outByte1%
                      POKE decodeOffset& + 1, outByte2%
                    CASE 2
                      POKE decodeOffset& + 0, outByte1%
                  END SELECT
                END IF


              NEXT count&
              myErr% = FN HUNLOCK(theDecodedHandle&)
            END IF
          XELSE
            'error
          END IF
<snip>

For simplicity's sake I have omitted discussing what is done when there are
odd bytes left over. If anyone cares to share some bitshifting expertise, I
would appreciate it.

If anyone wants to look at my complete Base64 project, I'll be happy to
e-mail a copy.



Cheers,


--------------------------------------------------------------
Michael Evans
Manager of Software Development
Photo Systems, Inc.
3301 Wood Valley Road, NW
Atlanta, GA 30327-1515
Office: (404) 846-9386
Fax: (404) 240-0878
Home: (404) 240-0440
E-mail: evans@...
--------------------------------------------------------------