Base64Coder.st
changeset 4900 0e400da67727
parent 4895 301653b248c3
equal deleted inserted replaced
4899:a1b3e12f6ac0 4900:0e400da67727
     1 "{ Encoding: utf8 }"
     1 "{ Encoding: utf8 }"
     2 
     2 
     3 "
     3 "
     4  COPYRIGHT (c) 2002 by eXept Software AG
     4  COPYRIGHT (c) 2002 by eXept Software AG
     5               All Rights Reserved
     5 	      All Rights Reserved
     6 
     6 
     7  This software is furnished under a license and may be used
     7  This software is furnished under a license and may be used
     8  only in accordance with the terms of that license and with the
     8  only in accordance with the terms of that license and with the
     9  inclusion of the above copyright notice.   This software may not
     9  inclusion of the above copyright notice.   This software may not
    10  be provided or otherwise made available to, or used by, any
    10  be provided or otherwise made available to, or used by, any
    25 !Base64Coder class methodsFor:'documentation'!
    25 !Base64Coder class methodsFor:'documentation'!
    26 
    26 
    27 copyright
    27 copyright
    28 "
    28 "
    29  COPYRIGHT (c) 2002 by eXept Software AG
    29  COPYRIGHT (c) 2002 by eXept Software AG
    30               All Rights Reserved
    30 	      All Rights Reserved
    31 
    31 
    32  This software is furnished under a license and may be used
    32  This software is furnished under a license and may be used
    33  only in accordance with the terms of that license and with the
    33  only in accordance with the terms of that license and with the
    34  inclusion of the above copyright notice.   This software may not
    34  inclusion of the above copyright notice.   This software may not
    35  be provided or otherwise made available to, or used by, any
    35  be provided or otherwise made available to, or used by, any
    41 documentation
    41 documentation
    42 "
    42 "
    43     Instances of this class perform Base64 en- and decoding as defined in RFC 2045
    43     Instances of this class perform Base64 en- and decoding as defined in RFC 2045
    44     3 bytes are mapped to 4 characters, representing 6 bits each.
    44     3 bytes are mapped to 4 characters, representing 6 bits each.
    45     The encoded string consists only of characters from the set:
    45     The encoded string consists only of characters from the set:
    46         'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
    46 	'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
    47 
    47 
    48     Notice: for URLs, a slightly different encoding is used,      
    48     Notice: for URLs, a slightly different encoding is used,
    49     where instead of plus and slash, minus and underline are generated (see Base64UrlCoder).
    49     where instead of plus and slash, minus and underline are generated (see Base64UrlCoder).
    50 
    50 
    51     The main entry point API is:
    51     The main entry point API is:
    52         Base64Coder encode:aStringOrBytes
    52 	Base64Coder encode:aStringOrBytes
    53     and
    53     and
    54         Base64Coder decode:aString
    54 	Base64Coder decode:aString
    55 
    55 
    56     Typically, binary data is encoded as base64, 
    56     Typically, binary data is encoded as base64,
    57     so the natural return value is a byte array.
    57     so the natural return value is a byte array.
    58 
    58 
    59     If the decoder should return a string, use
    59     If the decoder should return a string, use
    60         Base64Coder decodeAsString:aString.
    60 	Base64Coder decodeAsString:aString.
    61     otherwise, a bytearray is returned from the decode: method.
    61     otherwise, a bytearray is returned from the decode: method.
    62     
    62 
    63     [author:]
    63     [author:]
    64         Stefan Vogel
    64 	Stefan Vogel
    65 
    65 
    66     [see also:]
    66     [see also:]
    67         RFC https://tools.ietf.org/html/rfc4648
    67 	RFC https://tools.ietf.org/html/rfc4648
    68         
    68 
    69     [instance variables:]
    69     [instance variables:]
    70 
    70 
    71     [class variables:]
    71     [class variables:]
    72         Base64Mapping         String   Mapping from bytes (with 6 valid bits)
    72 	Base64Mapping         String   Mapping from bytes (with 6 valid bits)
    73                                        to Base64 characters
    73 				       to Base64 characters
    74         Base64ReverseMapping  Array    Mapping from Base64 characters to 6-bit-Bytes
    74 	Base64ReverseMapping  Array    Mapping from Base64 characters to 6-bit-Bytes
    75 "
    75 "
    76 !
    76 !
    77 
    77 
    78 examples
    78 examples
    79 "
    79 "
    80                                                                 [exBegin]
    80 								[exBegin]
    81    (Base64Coder encode:'queen%27s%20gambit') asString = 'cXVlZW4lMjdzJTIwZ2FtYml0'
    81    (Base64Coder encode:'queen%27s%20gambit') asString = 'cXVlZW4lMjdzJTIwZ2FtYml0'
    82                                                                 [exEnd]
    82 								[exEnd]
    83 
    83 
    84                                                                 [exBegin]
    84 								[exBegin]
    85    (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString = 'queen%27s%20gambit'
    85    (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString = 'queen%27s%20gambit'
    86                                                                 [exEnd]
    86 								[exEnd]
    87 
    87 
    88                                                                 [exBegin]
    88 								[exBegin]
    89    |data1 text data2|
    89    |data1 text data2|
    90 
    90 
    91    data1 := #[0 1 16r7F 16r80 16r81 16rFE 16rFF].
    91    data1 := #[0 1 16r7F 16r80 16r81 16rFE 16rFF].
    92    text := Base64Coder encode:data1.
    92    text := Base64Coder encode:data1.
    93    data2 := Base64Coder decode:text.
    93    data2 := Base64Coder decode:text.
    94    data2
    94    data2
    95                                                                 [exEnd]
    95 								[exEnd]
    96 
    96 
    97                                                                 [exBegin]
    97 								[exBegin]
    98    |coder|
    98    |coder|
    99 
    99 
   100    coder := Base64Coder on:'' writeStream.
   100    coder := Base64Coder on:'' writeStream.
   101    coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19].
   101    coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19].
   102    coder flush.
   102    coder flush.
   103    coder contents inspect.
   103    coder contents inspect.
   104    coder reset.
   104    coder reset.
   105    coder nextPut:254.
   105    coder nextPut:254.
   106    coder contents inspect.
   106    coder contents inspect.
   107                                                                 [exEnd]
   107 								[exEnd]
   108 
   108 
   109                                                                 [exBegin]
   109 								[exBegin]
   110    |coder decoder|
   110    |coder decoder|
   111 
   111 
   112    coder := Base64Coder on:'' writeStream.
   112    coder := Base64Coder on:'' writeStream.
   113    coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20].
   113    coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20].
   114    coder flush.
   114    coder flush.
   116    decoder := Base64Coder on:(coder contents readStream).
   116    decoder := Base64Coder on:(coder contents readStream).
   117    [decoder atEnd] whileFalse:[
   117    [decoder atEnd] whileFalse:[
   118       Transcript show:decoder next
   118       Transcript show:decoder next
   119    ].
   119    ].
   120    Transcript cr.
   120    Transcript cr.
   121                                                                 [exEnd]
   121 								[exEnd]
   122                                                                 [exBegin]
   122 								[exBegin]
   123    |coder|
   123    |coder|
   124 
   124 
   125    coder := Base64Coder on:'' writeStream.
   125    coder := Base64Coder on:'' writeStream.
   126    coder nextPutAll:(0 to:200) asByteArray.
   126    coder nextPutAll:(0 to:200) asByteArray.
   127    coder flush.
   127    coder flush.
   128 
   128 
   129    Transcript showCR:(coder contents).
   129    Transcript showCR:(coder contents).
   130                                                                 [exEnd]
   130 								[exEnd]
   131                                                                 [exBegin]
   131 								[exBegin]
   132    |bytes|
   132    |bytes|
   133 
   133 
   134    bytes := ByteArray new:100000.
   134    bytes := ByteArray new:100000.
   135    Time millisecondsToRun:[    
   135    Time millisecondsToRun:[
   136        100 timesRepeat:[
   136        100 timesRepeat:[
   137            Base64Coder encode:bytes.
   137 	   Base64Coder encode:bytes.
   138        ].        
   138        ].
   139    ].        
   139    ].
   140                                                                 [exEnd]
   140 								[exEnd]
   141                                                                 [exBegin]
   141 								[exBegin]
   142    |bytes encoded decoded|
   142    |bytes encoded decoded|
   143 
   143 
   144    bytes := #[0 0 0] copy.
   144    bytes := #[0 0 0] copy.
   145    0 to:255 do:[:b1 |
   145    0 to:255 do:[:b1 |
   146        Transcript showCR:b1.  
   146        Transcript showCR:b1.
   147        bytes at:1 put:b1.  
   147        bytes at:1 put:b1.
   148        0 to:255 do:[:b2 |
   148        0 to:255 do:[:b2 |
   149            bytes at:2 put:b2.  
   149 	   bytes at:2 put:b2.
   150            0 to:255 do:[:b3 |
   150 	   0 to:255 do:[:b3 |
   151                bytes at:3 put:b3.  
   151 	       bytes at:3 put:b3.
   152                encoded := Base64Coder encode:bytes.
   152 	       encoded := Base64Coder encode:bytes.
   153                decoded := Base64Coder decode:encoded.
   153 	       decoded := Base64Coder decode:encoded.
   154                self assert:(decoded = bytes).
   154 	       self assert:(decoded = bytes).
   155            ]
   155 	   ]
   156        ]
   156        ]
   157    ].
   157    ].
   158                                                                 [exEnd]
   158 								[exEnd]
   159 "
   159 "
   160 ! !
   160 ! !
   161 
   161 
   162 !Base64Coder class methodsFor:'initialization'!
   162 !Base64Coder class methodsFor:'initialization'!
   163 
   163 
   164 initializeMappings
   164 initializeMappings
   165     "initialize class variables"
   165     "initialize class variables"
   166 
   166 
   167     Base64Mapping isNil ifTrue:[
   167     Base64Mapping isNil ifTrue:[
   168         "65 characters representing the 6-bit values from 0-63 and one pad character"
   168 	"65 characters representing the 6-bit values from 0-63 and one pad character"
   169         Base64Mapping := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.
   169 	Base64Mapping := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.
   170         Base64ReverseMapping := self reverseMappingFor:Base64Mapping.
   170 	Base64ReverseMapping := self reverseMappingFor:Base64Mapping.
   171     ].
   171     ].
   172 
   172 
   173     "
   173     "
   174      Base64Mapping := nil.
   174      Base64Mapping := nil.
   175      self initializeMappings
   175      self initializeMappings
   197      a specially tuned version is provided here
   197      a specially tuned version is provided here
   198      for the common case of decoding a string.
   198      for the common case of decoding a string.
   199      This returns a byteArray."
   199      This returns a byteArray."
   200 
   200 
   201     aStringOrStream isString ifTrue:[
   201     aStringOrStream isString ifTrue:[
   202         ^ self fastDecodeString:aStringOrStream asString:false
   202 	^ self fastDecodeString:aStringOrStream asString:false
   203     ].    
   203     ].
   204     ^ super decode:aStringOrStream.
   204     ^ super decode:aStringOrStream.
   205 
   205 
   206     "Created: / 30-09-2018 / 14:14:51 / Claus Gittinger"
   206     "Created: / 30-09-2018 / 14:14:51 / Claus Gittinger"
   207     "Modified: / 21-03-2019 / 22:37:27 / Claus Gittinger"
   207     "Modified: / 21-03-2019 / 22:37:27 / Claus Gittinger"
   208 !
   208 !
   212      a specially tuned version is provided here
   212      a specially tuned version is provided here
   213      for the common case of decoding a string.
   213      for the common case of decoding a string.
   214      This returns a string."
   214      This returns a string."
   215 
   215 
   216     encodedString isString ifTrue:[
   216     encodedString isString ifTrue:[
   217         ^ self fastDecodeString:encodedString asString:true
   217 	^ self fastDecodeString:encodedString asString:true
   218     ].    
   218     ].
   219     ^ super decodeAsString:encodedString.
   219     ^ super decodeAsString:encodedString.
   220 
   220 
   221     "Created: / 21-03-2019 / 20:43:47 / Claus Gittinger"
   221     "Created: / 21-03-2019 / 20:43:47 / Claus Gittinger"
   222     "Modified: / 21-03-2019 / 22:10:24 / Claus Gittinger"
   222     "Modified: / 21-03-2019 / 22:10:24 / Claus Gittinger"
   223 !
   223 !
   224 
   224 
   225 encode:aStringOrStream
   225 encode:aStringOrStream
   226     "because base64 encoding is used heavily in some protocols,
   226     "because base64 encoding is used heavily in some protocols,
   227      a specially tuned version is provided here 
   227      a specially tuned version is provided here
   228      for the common case of encoding a string.
   228      for the common case of encoding a string.
   229      A string is generated with an inserted
   229      A string is generated with an inserted
   230      newline after every 76 characters (see RFC 2045)"
   230      newline after every 76 characters (see RFC 2045)"
   231 
   231 
   232     (aStringOrStream isString or:[aStringOrStream isByteArray]) ifTrue:[
   232     (aStringOrStream isString or:[aStringOrStream isByteArray]) ifTrue:[
   233         ^ self fastEncode:aStringOrStream asString:true lineLimit:(self lineLimit)
   233 	^ self fastEncode:aStringOrStream asString:true lineLimit:(self lineLimit)
   234     ].    
   234     ].
   235     ^ super encode:aStringOrStream.
   235     ^ super encode:aStringOrStream.
   236 
   236 
   237     "Created: / 21-03-2019 / 20:44:35 / Claus Gittinger"
   237     "Created: / 21-03-2019 / 20:44:35 / Claus Gittinger"
   238     "Modified: / 21-03-2019 / 22:33:37 / Claus Gittinger"
   238     "Modified: / 21-03-2019 / 22:33:37 / Claus Gittinger"
   239 !
   239 !
   249     "
   249     "
   250      (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0'
   250      (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0'
   251 
   251 
   252      (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') => #[113 117 101 101 110 37 50 55 115 37 50 48 103 97 109 98 105 116]
   252      (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') => #[113 117 101 101 110 37 50 55 115 37 50 48 103 97 109 98 105 116]
   253      (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit'
   253      (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit'
   254      (Base64Coder decodeAsString:'cXVlZW4lMjdzJTIwZ2FtYml0') => 'queen%27s%20gambit' 
   254      (Base64Coder decodeAsString:'cXVlZW4lMjdzJTIwZ2FtYml0') => 'queen%27s%20gambit'
   255 
   255 
   256      (Base64Coder fastDecodeString:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit'
   256      (Base64Coder fastDecodeString:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit'
   257     "
   257     "
   258 
   258 
   259     "Created: / 30-09-2018 / 14:36:58 / Claus Gittinger"
   259     "Created: / 30-09-2018 / 14:36:58 / Claus Gittinger"
   260     "Modified (comment): / 21-03-2019 / 22:12:07 / Claus Gittinger"
   260     "Modified (comment): / 21-03-2019 / 22:12:07 / Claus Gittinger"
   261 !
   261 !
   262 
   262 
   263 fastDecodeString:aString asString:asStringBoolean
   263 fastDecodeString:aString asString:asStringBoolean
   264     "because base64 decoding is used heavily in some protocols,
   264     "because base64 decoding is used heavily in some protocols,
   265      a specially tuned version is provided here 
   265      a specially tuned version is provided here
   266      for the common case of decoding a string.
   266      for the common case of decoding a string.
   267      If the argument is true, a string is returned;
   267      If the argument is true, a string is returned;
   268      otherwise, a bytearray"
   268      otherwise, a bytearray"
   269 
   269 
   270     |decoding revMapping|
   270     |decoding revMapping|
   271 
   271 
   272     revMapping := self reverseMapping.
   272     revMapping := self reverseMapping.
   273     revMapping isNil ifTrue:[
   273     revMapping isNil ifTrue:[
   274         self initializeMappings.
   274 	self initializeMappings.
   275         revMapping := self reverseMapping.
   275 	revMapping := self reverseMapping.
   276     ].    
   276     ].
   277 %{
   277 %{
   278     // overallocate by 3
   278     // overallocate by 3
   279 #   define N_QUICKBUFFER 512
   279 #   define N_QUICKBUFFER 512
   280     if (__isStringLike(aString)
   280     if (__isStringLike(aString)
   281      && __isByteArray(revMapping)) {
   281      && __isByteArray(revMapping)) {
   282         unsigned char *_revMapping = __stringVal(revMapping);
   282 	unsigned char *_revMapping = __stringVal(revMapping);
   283         int numInChars = __stringSize(aString);
   283 	int numInChars = __stringSize(aString);
   284         char *in = __stringVal(aString);
   284 	char *in = __stringVal(aString);
   285         unsigned char quickBuffer[N_QUICKBUFFER+3];
   285 	unsigned char quickBuffer[N_QUICKBUFFER+3];
   286         unsigned char *buffer = quickBuffer;
   286 	unsigned char *buffer = quickBuffer;
   287         int bufferSize = N_QUICKBUFFER;
   287 	int bufferSize = N_QUICKBUFFER;
   288         int outLen = 0;
   288 	int outLen = 0;
   289         int charBuffer = 0;
   289 	int charBuffer = 0;
   290         int nBitsOut = 0;
   290 	int nBitsOut = 0;
   291         int i;
   291 	int i;
   292 
   292 
   293         for (i=0; i<numInChars; i++) {
   293 	for (i=0; i<numInChars; i++) {
   294             char ch = in[i];
   294 	    char ch = in[i];
   295             int bits = -1;
   295 	    int bits = -1;
   296 
   296 
   297             if (ch <= 127) {
   297 	    if (ch <= 127) {
   298                 bits = _revMapping[(ch-1) & 0x7F];
   298 		bits = _revMapping[(ch-1) & 0x7F];
   299             }    
   299 	    }
   300 
   300 
   301             if ((unsigned)bits <= 0x3F) { 
   301 	    if ((unsigned)bits <= 0x3F) {
   302                 charBuffer = (charBuffer << 6) | bits;
   302 		charBuffer = (charBuffer << 6) | bits;
   303                 nBitsOut += 6;
   303 		nBitsOut += 6;
   304                 if (nBitsOut == 24) {
   304 		if (nBitsOut == 24) {
   305                     if ((outLen + 3) > bufferSize) {
   305 		    if ((outLen + 3) > bufferSize) {
   306                         if (buffer == quickBuffer) {
   306 			if (buffer == quickBuffer) {
   307                             // overallocate by 3
   307 			    // overallocate by 3
   308                             buffer = (unsigned char *)malloc(bufferSize*2+3);
   308 			    buffer = (unsigned char *)malloc(bufferSize*2+3);
   309                             memcpy(buffer, quickBuffer, bufferSize);
   309 			    memcpy(buffer, quickBuffer, bufferSize);
   310                         } else {
   310 			} else {
   311                             buffer = (unsigned char *)realloc(buffer, bufferSize*2+3);
   311 			    buffer = (unsigned char *)realloc(buffer, bufferSize*2+3);
   312                         }
   312 			}
   313                         bufferSize = bufferSize * 2;
   313 			bufferSize = bufferSize * 2;
   314                     }
   314 		    }
   315                     buffer[outLen] = (charBuffer >> 16) & 0xFF;
   315 		    buffer[outLen] = (charBuffer >> 16) & 0xFF;
   316                     buffer[outLen+1] = (charBuffer >> 8) & 0xFF;
   316 		    buffer[outLen+1] = (charBuffer >> 8) & 0xFF;
   317                     buffer[outLen+2] = (charBuffer) & 0xFF;
   317 		    buffer[outLen+2] = (charBuffer) & 0xFF;
   318                     outLen += 3;
   318 		    outLen += 3;
   319                     charBuffer = nBitsOut = 0;
   319 		    charBuffer = nBitsOut = 0;
   320                 }
   320 		}
   321             } else {
   321 	    } else {
   322                 if ((unsigned)bits == 0x40) {
   322 		if ((unsigned)bits == 0x40) {
   323                     // end mark
   323 		    // end mark
   324                     // because of overallocation, there is no need to check for buffer-full condition here
   324 		    // because of overallocation, there is no need to check for buffer-full condition here
   325                     if (nBitsOut == 12) {
   325 		    if (nBitsOut == 12) {
   326                         // data has been padded to 12, skip 4 bits
   326 			// data has been padded to 12, skip 4 bits
   327                         // one more byte coming
   327 			// one more byte coming
   328                         charBuffer >>= 4;
   328 			charBuffer >>= 4;
   329                         nBitsOut -= 4;
   329 			nBitsOut -= 4;
   330                         buffer[outLen] = (charBuffer) & 0xFF;
   330 			buffer[outLen] = (charBuffer) & 0xFF;
   331                         outLen += 1;
   331 			outLen += 1;
   332                     } else if (nBitsOut == 18) {
   332 		    } else if (nBitsOut == 18) {
   333                         // data has been padded to 18, skip 2 bits
   333 			// data has been padded to 18, skip 2 bits
   334                         charBuffer >>= 2;
   334 			charBuffer >>= 2;
   335                         nBitsOut -= 2;
   335 			nBitsOut -= 2;
   336                         buffer[outLen] = (charBuffer >> 8) & 0xFF;
   336 			buffer[outLen] = (charBuffer >> 8) & 0xFF;
   337                         buffer[outLen+1] = (charBuffer) & 0xFF;
   337 			buffer[outLen+1] = (charBuffer) & 0xFF;
   338                         outLen += 2;
   338 			outLen += 2;
   339                     }
   339 		    }
   340                 } else {
   340 		} else {
   341                     // ignore
   341 		    // ignore
   342                 }    
   342 		}
   343             }
   343 	    }
   344         }
   344 	}
   345 
   345 
   346         if (asStringBoolean == true) {
   346 	if (asStringBoolean == true) {
   347             decoding = __MKSTRING_L(buffer, outLen);
   347 	    decoding = __MKSTRING_L(buffer, outLen);
   348         } else {
   348 	} else {
   349             decoding = __MKBYTEARRAY(buffer, outLen);
   349 	    decoding = __MKBYTEARRAY(buffer, outLen);
   350         }
   350 	}
   351         if (buffer != quickBuffer) {
   351 	if (buffer != quickBuffer) {
   352             free(buffer);
   352 	    free(buffer);
   353         }
   353 	}
   354         RETURN(decoding);
   354 	RETURN(decoding);
   355     }    
   355     }
   356 %}.
   356 %}.
   357     decoding := super decode:aString.
   357     decoding := super decode:aString.
   358     asStringBoolean ifTrue:[
   358     asStringBoolean ifTrue:[
   359         ^ decoding asString
   359 	^ decoding asString
   360     ].    
   360     ].
   361     ^ decoding
   361     ^ decoding
   362     
   362 
   363     "
   363     "
   364      (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0'
   364      (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0'
   365 
   365 
   366      (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit'
   366      (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit'
   367      (Base64Coder fastDecodeString:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit'
   367      (Base64Coder fastDecodeString:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit'
   375 
   375 
   376      |data encoded|
   376      |data encoded|
   377      data := ByteArray new:100000.
   377      data := ByteArray new:100000.
   378      encoded := Base64Coder encode:data.
   378      encoded := Base64Coder encode:data.
   379      Time millisecondsToRun:[
   379      Time millisecondsToRun:[
   380         10 timesRepeat:[
   380 	10 timesRepeat:[
   381             Base64Coder decode:encoded.
   381 	    Base64Coder decode:encoded.
   382         ]
   382 	]
   383      ] 
   383      ]
   384      
   384 
   385      |data encoded|
   385      |data encoded|
   386      data := ByteArray new:100000.
   386      data := ByteArray new:100000.
   387      encoded := Base64Coder encode:data.
   387      encoded := Base64Coder encode:data.
   388      Time millisecondsToRun:[
   388      Time millisecondsToRun:[
   389         10 timesRepeat:[
   389 	10 timesRepeat:[
   390             Base64Coder fastDecodeString:encoded.
   390 	    Base64Coder fastDecodeString:encoded.
   391         ]
   391 	]
   392      ]
   392      ]
   393 
   393 
   394     "
   394     "
   395 
   395 
   396     "Created: / 30-09-2018 / 14:35:05 / Claus Gittinger"
   396     "Created: / 30-09-2018 / 14:35:05 / Claus Gittinger"
   397     "Modified: / 21-03-2019 / 22:34:49 / Claus Gittinger"
   397     "Modified: / 21-03-2019 / 22:34:49 / Claus Gittinger"
   398 !
   398 !
   399 
   399 
   400 fastEncode:aStringOrByteArray 
   400 fastEncode:aStringOrByteArray
   401     "because base64 encoding is used heavily in some protocols,
   401     "because base64 encoding is used heavily in some protocols,
   402      a specially tuned version is provided here
   402      a specially tuned version is provided here
   403      for the common case of encoding a string or bytearray.
   403      for the common case of encoding a string or bytearray.
   404      A string is generated with an inserted
   404      A string is generated with an inserted
   405      newline after every 76 characters (see RFC 2045)"
   405      newline after every 76 characters (see RFC 2045)"
   423     "Modified (comment): / 21-03-2019 / 22:14:17 / Claus Gittinger"
   423     "Modified (comment): / 21-03-2019 / 22:14:17 / Claus Gittinger"
   424 !
   424 !
   425 
   425 
   426 fastEncode:aStringOrByteArray asString:asStringBoolean lineLimit:lineLimitOrNil
   426 fastEncode:aStringOrByteArray asString:asStringBoolean lineLimit:lineLimitOrNil
   427     "because base64 encoding is used heavily in some protocols,
   427     "because base64 encoding is used heavily in some protocols,
   428      a specially tuned version is provided here 
   428      a specially tuned version is provided here
   429      for the common case of encoding a string.
   429      for the common case of encoding a string.
   430      If asStringBoolean is true, a string is generated; otherwise, a bytearray is returned.
   430      If asStringBoolean is true, a string is generated; otherwise, a bytearray is returned.
   431      If lineLimitOrNil is non-nil, a newline is inserted after every such number of characters"
   431      If lineLimitOrNil is non-nil, a newline is inserted after every such number of characters"
   432 
   432 
   433     |encoding mapping|
   433     |encoding mapping|
   434 
   434 
   435     mapping := self mapping.
   435     mapping := self mapping.
   436     mapping isNil ifTrue:[
   436     mapping isNil ifTrue:[
   437         self initializeMappings.
   437 	self initializeMappings.
   438         mapping := self mapping.
   438 	mapping := self mapping.
   439     ].    
   439     ].
   440 %{
   440 %{
   441     // overallocate by 5
   441     // overallocate by 5
   442 #   define N_QUICKBUFFER 512
   442 #   define N_QUICKBUFFER 512
   443     int argIsString = __isStringLike(aStringOrByteArray);
   443     int argIsString = __isStringLike(aStringOrByteArray);
   444     
   444 
   445     if ((argIsString || __isByteArray(aStringOrByteArray))
   445     if ((argIsString || __isByteArray(aStringOrByteArray))
   446      && __isStringLike(mapping)) {
   446      && __isStringLike(mapping)) {
   447         unsigned char *__mapping = __stringVal(mapping);;
   447 	unsigned char *__mapping = __stringVal(mapping);
   448         int numInChars;
   448 	int numInChars;
   449         unsigned char *in;
   449 	unsigned char *in;
   450         unsigned char quickBuffer[N_QUICKBUFFER+5];
   450 	unsigned char quickBuffer[N_QUICKBUFFER+5];
   451         unsigned char *buffer = quickBuffer;
   451 	unsigned char *buffer = quickBuffer;
   452         int bufferSize = N_QUICKBUFFER;
   452 	int bufferSize = N_QUICKBUFFER;
   453         int outLen = 0;
   453 	int outLen = 0;
   454         int nBitsOut = 0;
   454 	int nBitsOut = 0;
   455         int i;
   455 	int i;
   456         int numInCharsMinus3;
   456 	int numInCharsMinus3;
   457         unsigned int lineLimit = ~0;
   457 	unsigned int lineLimit = ~0;
   458         int lineLength = 0;
   458 	int lineLength = 0;
   459         int restLength = 0;
   459 	int restLength = 0;
   460 
   460 
   461         if (__isSmallInteger(lineLimitOrNil)) {
   461 	if (__isSmallInteger(lineLimitOrNil)) {
   462             lineLimit = __intVal(lineLimitOrNil);
   462 	    lineLimit = __intVal(lineLimitOrNil);
   463         } 
   463 	}
   464 
   464 
   465         if (argIsString) {
   465 	if (argIsString) {
   466             numInChars = __stringSize(aStringOrByteArray);
   466 	    numInChars = __stringSize(aStringOrByteArray);
   467             in = __stringVal(aStringOrByteArray);
   467 	    in = __stringVal(aStringOrByteArray);
   468         } else {
   468 	} else {
   469             numInChars = __byteArraySize(aStringOrByteArray);
   469 	    numInChars = __byteArraySize(aStringOrByteArray);
   470             in = __byteArrayVal(aStringOrByteArray);
   470 	    in = __byteArrayVal(aStringOrByteArray);
   471         }
   471 	}
   472         // fprintf(stderr, "%d\n", numInChars);
   472 	// fprintf(stderr, "%d\n", numInChars);
   473 
   473 
   474         lineLength = 0;
   474 	lineLength = 0;
   475         numInCharsMinus3 = numInChars-3;
   475 	numInCharsMinus3 = numInChars-3;
   476 
   476 
   477         for (i=0; i<=numInCharsMinus3; i+=3) {
   477 	for (i=0; i<=numInCharsMinus3; i+=3) {
   478             int charBuffer;
   478 	    int charBuffer;
   479             
   479 
   480             if (lineLength >= lineLimit) {
   480 	    if (lineLength >= lineLimit) {
   481                 buffer[outLen++] = '\n';
   481 		buffer[outLen++] = '\n';
   482                 lineLength = 0;
   482 		lineLength = 0;
   483             }
   483 	    }
   484 
   484 
   485             charBuffer = (in[i]) << 16;    
   485 	    charBuffer = (in[i]) << 16;
   486             charBuffer |= ((in[i+1]) << 8);    
   486 	    charBuffer |= ((in[i+1]) << 8);
   487             charBuffer |= (in[i+2]);
   487 	    charBuffer |= (in[i+2]);
   488             
   488 
   489             if ((outLen + 5) > bufferSize) {
   489 	    if ((outLen + 5) > bufferSize) {
   490                 if (buffer == quickBuffer) {
   490 		if (buffer == quickBuffer) {
   491                     // overallocate by 5
   491 		    // overallocate by 5
   492                     buffer = (unsigned char *)malloc(bufferSize*2+5);
   492 		    buffer = (unsigned char *)malloc(bufferSize*2+5);
   493                     memcpy(buffer, quickBuffer, bufferSize);
   493 		    memcpy(buffer, quickBuffer, bufferSize);
   494                 } else {
   494 		} else {
   495                     buffer = (unsigned char *)realloc(buffer, bufferSize*2+5);
   495 		    buffer = (unsigned char *)realloc(buffer, bufferSize*2+5);
   496                 }
   496 		}
   497                 bufferSize = bufferSize * 2;
   497 		bufferSize = bufferSize * 2;
   498             }
   498 	    }
   499 #ifdef __LSBFIRST__
   499 #ifdef __LSBFIRST__
   500             {
   500 	    {
   501                 unsigned int out = __mapping[(charBuffer >> 18) & 0x3F];
   501 		unsigned int out = __mapping[(charBuffer >> 18) & 0x3F];
   502                 out |= (__mapping[(charBuffer >> 12) & 0x3F]) << 8;
   502 		out |= (__mapping[(charBuffer >> 12) & 0x3F]) << 8;
   503                 out |= (__mapping[(charBuffer >> 6) & 0x3F]) << 16;
   503 		out |= (__mapping[(charBuffer >> 6) & 0x3F]) << 16;
   504                 out |= (__mapping[(charBuffer) & 0x3F]) << 24;
   504 		out |= (__mapping[(charBuffer) & 0x3F]) << 24;
   505                 ((unsigned int*)(&buffer[outLen]))[0] = out;
   505 		((unsigned int*)(&buffer[outLen]))[0] = out;
   506             }
   506 	    }
   507 #else
   507 #else
   508             buffer[outLen] = __mapping[(charBuffer >> 18) & 0x3F];
   508 	    buffer[outLen] = __mapping[(charBuffer >> 18) & 0x3F];
   509             buffer[outLen+1] = __mapping[(charBuffer >> 12) & 0x3F];
   509 	    buffer[outLen+1] = __mapping[(charBuffer >> 12) & 0x3F];
   510             buffer[outLen+2] = __mapping[(charBuffer >> 6) & 0x3F];
   510 	    buffer[outLen+2] = __mapping[(charBuffer >> 6) & 0x3F];
   511             buffer[outLen+3] = __mapping[(charBuffer) & 0x3F];
   511 	    buffer[outLen+3] = __mapping[(charBuffer) & 0x3F];
   512 #endif
   512 #endif
   513             outLen += 4;
   513 	    outLen += 4;
   514             lineLength += 4;
   514 	    lineLength += 4;
   515         }
   515 	}
   516 
   516 
   517         restLength = numInChars-i;
   517 	restLength = numInChars-i;
   518         // fprintf(stderr, "rest: %d\n", restLength);
   518 	// fprintf(stderr, "rest: %d\n", restLength);
   519         if (restLength) {
   519 	if (restLength) {
   520             if (lineLength >= lineLimit) {
   520 	    if (lineLength >= lineLimit) {
   521                 buffer[outLen++] = '\n';
   521 		buffer[outLen++] = '\n';
   522                 lineLength = 0;
   522 		lineLength = 0;
   523             }
   523 	    }
   524             if (restLength == 1) {
   524 	    if (restLength == 1) {
   525                 unsigned int charBuffer;
   525 		unsigned int charBuffer;
   526                 
   526 
   527                 // pad with '=='
   527 		// pad with '=='
   528                 charBuffer = (in[i]) << 4;    
   528 		charBuffer = (in[i]) << 4;
   529                 buffer[outLen] = __mapping[(charBuffer >> 6) & 0x3F];
   529 		buffer[outLen] = __mapping[(charBuffer >> 6) & 0x3F];
   530                 buffer[outLen+1] = __mapping[(charBuffer) & 0x3F];
   530 		buffer[outLen+1] = __mapping[(charBuffer) & 0x3F];
   531                 buffer[outLen+2] = __mapping[64];
   531 		buffer[outLen+2] = __mapping[64];
   532                 buffer[outLen+3] = __mapping[64];
   532 		buffer[outLen+3] = __mapping[64];
   533                 outLen += 4;
   533 		outLen += 4;
   534             } else {
   534 	    } else {
   535                 unsigned int charBuffer;
   535 		unsigned int charBuffer;
   536 
   536 
   537                 // restLength == 2
   537 		// restLength == 2
   538                 // pad with '='
   538 		// pad with '='
   539                 charBuffer = (in[i]) << 8;    
   539 		charBuffer = (in[i]) << 8;
   540                 charBuffer |= (in[i+1]); 
   540 		charBuffer |= (in[i+1]);
   541                 charBuffer <<= 2;
   541 		charBuffer <<= 2;
   542                 buffer[outLen] = __mapping[(charBuffer >> 12) & 0x3F];
   542 		buffer[outLen] = __mapping[(charBuffer >> 12) & 0x3F];
   543                 buffer[outLen+1] = __mapping[(charBuffer >> 6) & 0x3F];
   543 		buffer[outLen+1] = __mapping[(charBuffer >> 6) & 0x3F];
   544                 buffer[outLen+2] = __mapping[(charBuffer) & 0x3F];
   544 		buffer[outLen+2] = __mapping[(charBuffer) & 0x3F];
   545                 buffer[outLen+3] = __mapping[64];
   545 		buffer[outLen+3] = __mapping[64];
   546                 outLen += 4;
   546 		outLen += 4;
   547             }    
   547 	    }
   548         }
   548 	}
   549 
   549 
   550         if (asStringBoolean == true) {
   550 	if (asStringBoolean == true) {
   551             encoding = __MKSTRING_L(buffer, outLen);
   551 	    encoding = __MKSTRING_L(buffer, outLen);
   552         } else {
   552 	} else {
   553             encoding = __MKBYTEARRAY(buffer, outLen);
   553 	    encoding = __MKBYTEARRAY(buffer, outLen);
   554         }
   554 	}
   555         if (buffer != quickBuffer) {
   555 	if (buffer != quickBuffer) {
   556             free(buffer);
   556 	    free(buffer);
   557         }
   557 	}
   558         RETURN(encoding);
   558 	RETURN(encoding);
   559     }    
   559     }
   560 %}.
   560 %}.
   561     encoding := super encode:aStringOrByteArray with:lineLimitOrNil.
   561     encoding := super encode:aStringOrByteArray with:lineLimitOrNil.
   562     asStringBoolean ifTrue:[
   562     asStringBoolean ifTrue:[
   563         ^ encoding asString
   563 	^ encoding asString
   564     ].    
   564     ].
   565     ^ encoding
   565     ^ encoding
   566     
   566 
   567     "
   567     "
   568      (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0'
   568      (Base64Coder encode:'queen%27s%20gambit') => 'cXVlZW4lMjdzJTIwZ2FtYml0'
   569      (Base64Coder fastEncode:'queen%27s%20gambit' asString:true) => 'cXVlZW4lMjdzJTIwZ2FtYml0'
   569      (Base64Coder fastEncode:'queen%27s%20gambit' asString:true) => 'cXVlZW4lMjdzJTIwZ2FtYml0'
   570 
   570 
   571      (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit'
   571      (Base64Coder decode:'cXVlZW4lMjdzJTIwZ2FtYml0') asString => 'queen%27s%20gambit'
   581      (Base64Coder fastDecodeString:'YWE=' asString:true) => 'aa'
   581      (Base64Coder fastDecodeString:'YWE=' asString:true) => 'aa'
   582 
   582 
   583      |data|
   583      |data|
   584      data := ByteArray new:1000.
   584      data := ByteArray new:1000.
   585      Time millisecondsToRun:[
   585      Time millisecondsToRun:[
   586         10000 timesRepeat:[self halt.
   586 	10000 timesRepeat:[self halt.
   587             Base64Coder encode:data.
   587 	    Base64Coder encode:data.
   588         ]
   588 	]
   589      ]
   589      ]
   590      
   590 
   591      |data|
   591      |data|
   592      data := ByteArray new:1000.
   592      data := ByteArray new:1000.
   593      Base64Coder fastEncode:data asString:true lineLimit:20.
   593      Base64Coder fastEncode:data asString:true lineLimit:20.
   594 
   594 
   595      |data|
   595      |data|
   597      Base64Coder fastEncode:data asString:true lineLimit:nil.
   597      Base64Coder fastEncode:data asString:true lineLimit:nil.
   598 
   598 
   599      |data|
   599      |data|
   600      data := ByteArray new:1000.
   600      data := ByteArray new:1000.
   601      Time millisecondsToRun:[
   601      Time millisecondsToRun:[
   602         10000 timesRepeat:[
   602 	10000 timesRepeat:[
   603             Base64Coder fastEncode:data.
   603 	    Base64Coder fastEncode:data.
   604         ]
   604 	]
   605      ]
   605      ]
   606 
   606 
   607      self assert:((Base64Coder fastEncode:'abc' asString:true)
   607      self assert:((Base64Coder fastEncode:'abc' asString:true)
   608                   = 'abc' base64Encoded).
   608 		  = 'abc' base64Encoded).
   609      self assert:((Base64Coder fastEncode:'a' asString:true)
   609      self assert:((Base64Coder fastEncode:'a' asString:true)
   610                   = 'a' base64Encoded).
   610 		  = 'a' base64Encoded).
   611      self assert:((Base64Coder fastEncode:'ab' asString:true)
   611      self assert:((Base64Coder fastEncode:'ab' asString:true)
   612                   = 'ab' base64Encoded).
   612 		  = 'ab' base64Encoded).
   613      self assert:((Base64Coder fastEncode:'abcd' asString:true)
   613      self assert:((Base64Coder fastEncode:'abcd' asString:true)
   614                   = 'abcd' base64Encoded).
   614 		  = 'abcd' base64Encoded).
   615      self assert:((Base64Coder fastEncode:'abcde' asString:true)
   615      self assert:((Base64Coder fastEncode:'abcde' asString:true)
   616                   = 'abcde' base64Encoded).
   616 		  = 'abcde' base64Encoded).
   617      self assert:((Base64Coder fastEncode:'abcdef' asString:true)
   617      self assert:((Base64Coder fastEncode:'abcdef' asString:true)
   618                   = 'abcdef' base64Encoded).
   618 		  = 'abcdef' base64Encoded).
   619 
   619 
   620      self assert:((Base64Coder fastEncode:#'parseMethod:onError:rememberNodes:nodeGenerationCallback:' asString:true)
   620      self assert:((Base64Coder fastEncode:#'parseMethod:onError:rememberNodes:nodeGenerationCallback:' asString:true)
   621                   = #'parseMethod:onError:rememberNodes:nodeGenerationCallback:' base64Encoded).
   621 		  = #'parseMethod:onError:rememberNodes:nodeGenerationCallback:' base64Encoded).
   622 
   622 
   623      self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:' asString:true)
   623      self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:' asString:true)
   624                   = '_INVOKESTATIC_R:' base64Encoded).
   624 		  = '_INVOKESTATIC_R:' base64Encoded).
   625 
   625 
   626      self assert:((Base64Coder fastEncode:#'_INVOKESTATIC_R:' asString:true)
   626      self assert:((Base64Coder fastEncode:#'_INVOKESTATIC_R:' asString:true)
   627                   = #'_INVOKESTATIC_R:' base64Encoded) 
   627 		  = #'_INVOKESTATIC_R:' base64Encoded)
   628 
   628 
   629      self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:' asString:true)
   629      self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:' asString:true)
   630                   = (Base64Coder fastEncode:#'_INVOKESTATIC_R:' asString:true)).
   630 		  = (Base64Coder fastEncode:#'_INVOKESTATIC_R:' asString:true)).
   631 
   631 
   632      self assert:((#'_INVOKESTATIC_R:' base64Encoded)
   632      self assert:((#'_INVOKESTATIC_R:' base64Encoded)
   633                   = ('_INVOKESTATIC_R:' base64Encoded)).
   633 		  = ('_INVOKESTATIC_R:' base64Encoded)).
   634 
   634 
   635 
   635 
   636 
   636 
   637      self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:_:' asString:true)
   637      self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:_:' asString:true)
   638                   = '_INVOKESTATIC_R:_:' base64Encoded).
   638 		  = '_INVOKESTATIC_R:_:' base64Encoded).
   639 
   639 
   640      self assert:((Base64Coder fastEncode:#'_INVOKESTATIC_R:_:' asString:true)
   640      self assert:((Base64Coder fastEncode:#'_INVOKESTATIC_R:_:' asString:true)
   641                   = #'_INVOKESTATIC_R:_:' base64Encoded) 
   641 		  = #'_INVOKESTATIC_R:_:' base64Encoded)
   642 
   642 
   643      self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:_:' asString:true)
   643      self assert:((Base64Coder fastEncode:'_INVOKESTATIC_R:_:' asString:true)
   644                   = (Base64Coder fastEncode:#'_INVOKESTATIC_R:_:' asString:true)).
   644 		  = (Base64Coder fastEncode:#'_INVOKESTATIC_R:_:' asString:true)).
   645 
   645 
   646      self assert:((#'_INVOKESTATIC_R:_:' base64Encoded)
   646      self assert:((#'_INVOKESTATIC_R:_:' base64Encoded)
   647                   = ('_INVOKESTATIC_R:_:' base64Encoded)).
   647 		  = ('_INVOKESTATIC_R:_:' base64Encoded)).
   648                   
   648 
   649      Symbol allInstancesDo:[:each |
   649      Symbol allInstancesDo:[:each |
   650          self assert:((Base64Coder fastEncode:each asString:true)
   650 	 self assert:((Base64Coder fastEncode:each asString:true)
   651                      =  (Base64Coder encode:each with:nil)) 
   651 		     =  (Base64Coder encode:each with:nil))
   652      ]                
   652      ]
   653     "
   653     "
   654 
   654 
   655     "Created: / 21-03-2019 / 21:58:59 / Claus Gittinger"
   655     "Created: / 21-03-2019 / 21:58:59 / Claus Gittinger"
   656     "Modified (comment): / 21-03-2019 / 23:11:50 / Claus Gittinger"
   656     "Modified (comment): / 21-03-2019 / 23:11:50 / Claus Gittinger"
   657 ! !
   657 ! !
   660 
   660 
   661 nextPutByte:aByte
   661 nextPutByte:aByte
   662     "encode aByte on the output stream"
   662     "encode aByte on the output stream"
   663 
   663 
   664     |b1 "{ Class: SmallInteger }"
   664     |b1 "{ Class: SmallInteger }"
   665      b2 "{ Class: SmallInteger }" 
   665      b2 "{ Class: SmallInteger }"
   666      b3 "{ Class: SmallInteger }"
   666      b3 "{ Class: SmallInteger }"
   667      b4 "{ Class: SmallInteger }" 
   667      b4 "{ Class: SmallInteger }"
   668      bufferedBytes "{ Class: SmallInteger }" |
   668      bufferedBytes "{ Class: SmallInteger }" |
   669 
   669 
   670     buffer := (buffer bitShift:8) bitOr:aByte.
   670     buffer := (buffer bitShift:8) bitOr:aByte.
   671     bits := bits + 8.
   671     bits := bits + 8.
   672     bits == 24 ifTrue:[
   672     bits == 24 ifTrue:[
   673         "RFC 2045 says: max 76 characters in one line"
   673 	"RFC 2045 says: max 76 characters in one line"
   674         (lineLimit notNil and:[charCount >= lineLimit]) ifTrue:[
   674 	(lineLimit notNil and:[charCount >= lineLimit]) ifTrue:[
   675             stream cr.
   675 	    stream cr.
   676             charCount := 0.
   676 	    charCount := 0.
   677         ].
   677 	].
   678         bufferedBytes := buffer.
   678 	bufferedBytes := buffer.
   679         
   679 
   680         b4 := bufferedBytes bitAnd:16r3F.
   680 	b4 := bufferedBytes bitAnd:16r3F.
   681         b3 := (bufferedBytes bitShift:-6)  bitAnd:16r3F.
   681 	b3 := (bufferedBytes bitShift:-6)  bitAnd:16r3F.
   682         b2 := (bufferedBytes bitShift:-12) bitAnd:16r3F.
   682 	b2 := (bufferedBytes bitShift:-12) bitAnd:16r3F.
   683         b1 := (bufferedBytes bitShift:-18) bitAnd:16r3F.
   683 	b1 := (bufferedBytes bitShift:-18) bitAnd:16r3F.
   684         buffer := bits := 0.
   684 	buffer := bits := 0.
   685         
   685 
   686         stream nextPut:(mapping at:b1+1);
   686 	stream nextPut:(mapping at:b1+1);
   687                nextPut:(mapping at:b2+1);
   687 	       nextPut:(mapping at:b2+1);
   688                nextPut:(mapping at:b3+1);
   688 	       nextPut:(mapping at:b3+1);
   689                nextPut:(mapping at:b4+1).
   689 	       nextPut:(mapping at:b4+1).
   690 
   690 
   691         charCount := charCount + 4.
   691 	charCount := charCount + 4.
   692     ].
   692     ].
   693 
   693 
   694     "Modified: / 26-08-2017 / 12:35:17 / cg"
   694     "Modified: / 26-08-2017 / 12:35:17 / cg"
   695     "Modified: / 30-09-2018 / 15:15:14 / Claus Gittinger"
   695     "Modified: / 30-09-2018 / 15:15:14 / Claus Gittinger"
   696 ! !
   696 ! !
   697 
   697 
   698 !Base64Coder methodsFor:'misc'!
   698 !Base64Coder methodsFor:'misc'!
   699 
   699 
   700 flush
   700 flush
   701     "flush the remaining bits of buffer. 
   701     "flush the remaining bits of buffer.
   702      The number of bits in buffer is not a multiple of 6, so we pad
   702      The number of bits in buffer is not a multiple of 6, so we pad
   703      the buffer and signal that padding has been done via $= characters."
   703      the buffer and signal that padding has been done via $= characters."
   704 
   704 
   705     |tempBuffer "{Class: SmallInteger}"
   705     |tempBuffer "{Class: SmallInteger}"
   706      b1 b2 b3 b4|
   706      b1 b2 b3 b4|
   707         
   707 
   708     bits == 0 ifTrue:[
   708     bits == 0 ifTrue:[
   709         "buffer is empty, nothing to do"
   709 	"buffer is empty, nothing to do"
   710         ^ self.
   710 	^ self.
   711     ].
   711     ].
   712             
   712 
   713     tempBuffer := buffer.
   713     tempBuffer := buffer.
   714     bits == 8 ifTrue:[
   714     bits == 8 ifTrue:[
   715         tempBuffer := tempBuffer bitShift:4.
   715 	tempBuffer := tempBuffer bitShift:4.
   716         b4 := b3 := 64. "pad with '=='"
   716 	b4 := b3 := 64. "pad with '=='"
   717         b1 := (tempBuffer bitShift:-6) bitAnd:16r3F.
   717 	b1 := (tempBuffer bitShift:-6) bitAnd:16r3F.
   718         b2 := tempBuffer bitAnd:16r3F.
   718 	b2 := tempBuffer bitAnd:16r3F.
   719     ] ifFalse:[
   719     ] ifFalse:[
   720         bits = 16 ifTrue:[
   720 	bits = 16 ifTrue:[
   721             tempBuffer := tempBuffer bitShift:2.
   721 	    tempBuffer := tempBuffer bitShift:2.
   722             b4 := 64.        "pad with '='"
   722 	    b4 := 64.        "pad with '='"
   723             b3 := tempBuffer bitAnd:16r3F.
   723 	    b3 := tempBuffer bitAnd:16r3F.
   724             b2 := (tempBuffer bitShift:-6)  bitAnd:16r3F.
   724 	    b2 := (tempBuffer bitShift:-6)  bitAnd:16r3F.
   725             b1 := (tempBuffer bitShift:-12) bitAnd:16r3F.
   725 	    b1 := (tempBuffer bitShift:-12) bitAnd:16r3F.
   726         ]
   726 	]
   727     ].
   727     ].
   728     bits := buffer := 0.
   728     bits := buffer := 0.
   729 
   729 
   730     "RFC 2045 says: max 76 characters in one line"
   730     "RFC 2045 says: max 76 characters in one line"
   731     (lineLimit notNil and:[charCount >= lineLimit]) ifTrue:[
   731     (lineLimit notNil and:[charCount >= lineLimit]) ifTrue:[
   732         stream cr.
   732 	stream cr.
   733         charCount := 0.
   733 	charCount := 0.
   734     ].
   734     ].
   735 
   735 
   736     stream nextPut:(mapping at:b1+1);
   736     stream nextPut:(mapping at:b1+1);
   737            nextPut:(mapping at:b2+1);
   737 	   nextPut:(mapping at:b2+1);
   738            nextPut:(mapping at:b3+1);
   738 	   nextPut:(mapping at:b3+1);
   739            nextPut:(mapping at:b4+1).
   739 	   nextPut:(mapping at:b4+1).
   740     charCount := charCount + 4.
   740     charCount := charCount + 4.
   741 
   741 
   742     "Modified: / 20-03-2019 / 21:22:39 / Claus Gittinger"
   742     "Modified: / 20-03-2019 / 21:22:39 / Claus Gittinger"
   743 ! !
   743 ! !
   744 
   744 
   746 
   746 
   747 fillBuffer
   747 fillBuffer
   748     "fill buffer with next 4 characters each representing 6 bits.
   748     "fill buffer with next 4 characters each representing 6 bits.
   749      Used when decoding."
   749      Used when decoding."
   750 
   750 
   751     |b  
   751     |b
   752      tempBuffer "{Class: SmallInteger}"
   752      tempBuffer "{Class: SmallInteger}"
   753      _bits      "{Class: SmallInteger}" |
   753      _bits      "{Class: SmallInteger}" |
   754 
   754 
   755     tempBuffer := 0.
   755     tempBuffer := 0.
   756     _bits := 0.
   756     _bits := 0.
   757 
   757 
   758     [
   758     [
   759         "read next valid Base64 character, skip invalid characters"
   759 	"read next valid Base64 character, skip invalid characters"
   760         b := 255.
   760 	b := 255.
   761         [b == 255] whileTrue:[
   761 	[b == 255] whileTrue:[
   762             b := stream next.
   762 	    b := stream next.
   763             b isNil ifTrue:[ "end of stream"
   763 	    b isNil ifTrue:[ "end of stream"
   764                 b := 64.     "simulate end-mark"
   764 		b := 64.     "simulate end-mark"
   765             ] ifFalse:[
   765 	    ] ifFalse:[
   766                 b := reverseMapping at:b codePoint ifAbsent:255.
   766 		b := reverseMapping at:b codePoint ifAbsent:255.
   767             ]
   767 	    ]
   768         ].
   768 	].
   769 
   769 
   770         b == 64 ifTrue:[
   770 	b == 64 ifTrue:[
   771             "got $=, end of Base64 string has been reached"
   771 	    "got $=, end of Base64 string has been reached"
   772             atEnd := true.
   772 	    atEnd := true.
   773             _bits == 12 ifTrue:[
   773 	    _bits == 12 ifTrue:[
   774                 "data has been padded to 12, skip 4 bits"
   774 		"data has been padded to 12, skip 4 bits"
   775                 tempBuffer := tempBuffer bitShift:-4.
   775 		tempBuffer := tempBuffer bitShift:-4.
   776                 _bits := _bits - 4.
   776 		_bits := _bits - 4.
   777             ] ifFalse:[_bits == 18 ifTrue:[
   777 	    ] ifFalse:[_bits == 18 ifTrue:[
   778                 "data has been padded to 18, skip 2 bits"
   778 		"data has been padded to 18, skip 2 bits"
   779                 tempBuffer := tempBuffer bitShift:-2.
   779 		tempBuffer := tempBuffer bitShift:-2.
   780                 _bits := _bits - 2.
   780 		_bits := _bits - 2.
   781             ]].
   781 	    ]].
   782         ] ifFalse:[
   782 	] ifFalse:[
   783             "got valid Base64 character, append to buffer"
   783 	    "got valid Base64 character, append to buffer"
   784             tempBuffer := (tempBuffer bitShift:6) bitOr:b.
   784 	    tempBuffer := (tempBuffer bitShift:6) bitOr:b.
   785             _bits := _bits + 6.
   785 	    _bits := _bits + 6.
   786         ].
   786 	].
   787         (_bits == 24 or:[atEnd]) ifTrue:[
   787 	(_bits == 24 or:[atEnd]) ifTrue:[
   788             bits := _bits.
   788 	    bits := _bits.
   789             buffer := tempBuffer.
   789 	    buffer := tempBuffer.
   790             ^ self.
   790 	    ^ self.
   791         ].
   791 	].
   792     ] loop.
   792     ] loop.
   793 
   793 
   794     "Modified: / 30-09-2018 / 15:16:19 / Claus Gittinger"
   794     "Modified: / 30-09-2018 / 15:16:19 / Claus Gittinger"
   795 ! !
   795 ! !
   796 
   796 
   801 !
   801 !
   802 
   802 
   803 version_CVS
   803 version_CVS
   804     ^ '$Header$'
   804     ^ '$Header$'
   805 ! !
   805 ! !
   806