Base64Coder.st
changeset 1027 1c8e7e7785ab
child 1063 144a9e513f3e
equal deleted inserted replaced
1026:81cd85ca31d7 1027:1c8e7e7785ab
       
     1 "{ Package: 'stx:libbasic2' }"
       
     2 
       
     3 ObjectCoder subclass:#Base64Coder
       
     4 	instanceVariableNames:'buffer bits charCount atEnd'
       
     5 	classVariableNames:'Base64Mapping Base64ReverseMapping'
       
     6 	poolDictionaries:''
       
     7 	category:'System-Storage'
       
     8 !
       
     9 
       
    10 !Base64Coder class methodsFor:'documentation'!
       
    11 
       
    12 documentation
       
    13 "
       
    14     Instances of this class perform Base64 en- and decoding as defined in RFC 2045
       
    15     3 bytes are mapped to 4 characters, representing 6 bits each.
       
    16 
       
    17     [author:]
       
    18         Stefan Vogel
       
    19 
       
    20     [see also:]
       
    21 
       
    22     [instance variables:]
       
    23         buffer          SmallInteger   Up to 24 bits of data
       
    24         bits            SmallInteger   Number of valid bits in buffer
       
    25         charCount       SmallInteger   Number of characters since last cr
       
    26         atEnd           Boolean        true if end of Base64 string reached
       
    27 
       
    28     [class variables:]
       
    29         Base64Mapping         String   Mapping from bytes (with 6 valid bits)   
       
    30                                        to Base64 characters
       
    31         Base64ReverseMapping  Array    Mapping from Base64 characters to 6-bit-Bytes
       
    32 "
       
    33 !
       
    34 
       
    35 examples
       
    36 "
       
    37 
       
    38                                                                 [exBegin]
       
    39    |coder|
       
    40 
       
    41    coder := Base64Coder on:'' writeStream.
       
    42    coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19].
       
    43    coder flush.
       
    44    coder contents inspect.
       
    45    coder reset.
       
    46    coder nextPut:254.
       
    47    coder contents inspect.
       
    48                                                                 [exEnd]
       
    49 
       
    50                                                                 [exBegin]
       
    51    |coder coder1|
       
    52 
       
    53    coder := Base64Coder on:'' writeStream.
       
    54    coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20].
       
    55    coder flush.
       
    56 
       
    57    coder1 := Base64Coder on:coder contents readStream.
       
    58    [coder1 atEnd] whileFalse:[
       
    59       Transcript show:coder1 next
       
    60    ].
       
    61    Transcript cr.
       
    62                                                                 [exEnd]
       
    63 "
       
    64 ! !
       
    65 
       
    66 !Base64Coder class methodsFor:'initialization'!
       
    67 
       
    68 initialize
       
    69     "initialize class variables"
       
    70 
       
    71     "64 characters representing the 6-bit values from 0-63 and one pad character"
       
    72     Base64Mapping := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.
       
    73     Base64ReverseMapping := ByteArray new:256 withAll:255.
       
    74     Base64Mapping keysAndValuesDo:[:idx :char|
       
    75         Base64ReverseMapping at:char asciiValue put:idx-1.
       
    76     ].
       
    77 
       
    78     "
       
    79      self initialize
       
    80     "
       
    81 ! !
       
    82 
       
    83 !Base64Coder class methodsFor:'instance creation'!
       
    84 
       
    85 new
       
    86    ^ self basicNew initialize
       
    87 ! !
       
    88 
       
    89 !Base64Coder methodsFor:'encoding'!
       
    90 
       
    91 encodeObject:anObject with:aParameter
       
    92     "not defined. Use nextPut or nextPutAll:.
       
    93      Could encode the printString here"
       
    94 
       
    95     ^ self shouldNotImplement
       
    96 !
       
    97 
       
    98 next
       
    99     "answer the next decoded byte"
       
   100 
       
   101     |b shift|
       
   102 
       
   103     bits == 0 ifTrue:[
       
   104         "no more bits in buffer
       
   105          if end has been reached, raise signal"
       
   106         atEnd ifTrue:[
       
   107              ^ stream endOfStreamSignal raiseRequest.
       
   108         ].
       
   109 
       
   110         "read next 4 characters each representing 6 bits"
       
   111         buffer := 0.
       
   112         [
       
   113             "read next valid Base64 character, skip invalid characters"
       
   114             [
       
   115                 b := Base64ReverseMapping at:stream next asciiValue.
       
   116             ] doWhile:[b == 255].
       
   117 
       
   118             b == 64 ifTrue:[
       
   119                 "got #=, end of Base64 string has been reached"
       
   120                 atEnd := true.
       
   121                 bits == 12 ifTrue:[
       
   122                     "data has been padded to 12, skip 4 bits"
       
   123                     shift := -4.
       
   124                 ] ifFalse:[
       
   125                     "data has been padded to 18, skip 2 bits"
       
   126                     shift := -2.
       
   127                 ].
       
   128                 bits := bits + shift.
       
   129                 buffer := buffer bitShift:shift.
       
   130             ] ifFalse:[
       
   131                 "got valid Base64 character, append to buffer"
       
   132                 buffer := (buffer bitShift:6) bitOr:b.
       
   133                 bits := bits + 6.
       
   134             ].
       
   135         ] doWhile:[bits ~~ 24 and:[atEnd not]].
       
   136     ].
       
   137 
       
   138     b := (buffer bitShift:(8 - bits)) bitAnd:16rFF.
       
   139     bits := bits - 8.
       
   140 
       
   141     ^ b.
       
   142 !
       
   143 
       
   144 nextPut:aByte
       
   145     "encode aByte on the output stream"
       
   146 
       
   147     |b1 b2 b3 b4|
       
   148 
       
   149     buffer := (buffer bitShift:8) bitOr:aByte.
       
   150     bits := bits + 8.
       
   151     bits == 24 ifTrue:[
       
   152         b4 := buffer bitAnd:16r3F.
       
   153         b3 := (buffer bitShift:-6)  bitAnd:16r3F.
       
   154         b2 := (buffer bitShift:-12) bitAnd:16r3F.
       
   155         b1 := (buffer bitShift:-18) bitAnd:16r3F.
       
   156         buffer := bits := 0.
       
   157         stream nextPut:(Base64Mapping at:b1+1);
       
   158                nextPut:(Base64Mapping at:b2+1);
       
   159                nextPut:(Base64Mapping at:b3+1);
       
   160                nextPut:(Base64Mapping at:b4+1).
       
   161 
       
   162         "RFC 2045 says: max 76 characters in one line"
       
   163         charCount >= 68 ifTrue:[
       
   164             stream cr.
       
   165             charCount := 0.
       
   166         ] ifFalse:[
       
   167             charCount := charCount + 4.
       
   168         ]
       
   169     ].
       
   170 ! !
       
   171 
       
   172 !Base64Coder methodsFor:'initialization'!
       
   173 
       
   174 initialize
       
   175 
       
   176     buffer := bits := charCount := 0.
       
   177     atEnd := false.
       
   178 ! !
       
   179 
       
   180 !Base64Coder methodsFor:'misc'!
       
   181 
       
   182 flush
       
   183    "flush the remaining bits of buffer. 
       
   184     The number of bits in buffer is not a multiple of 6, so we pad
       
   185     the buffer and signal that padding has been done via $= characters."
       
   186 
       
   187    |b1 b2 b3 b4|
       
   188 
       
   189    bits == 0 ifTrue:[
       
   190         "buffer is empty, nothing to do"
       
   191         ^ self.
       
   192    ].
       
   193 
       
   194    bits == 8 ifTrue:[
       
   195         buffer := buffer bitShift:4.
       
   196         b4 := b3 := 64. "pad with '=='"
       
   197         b1 := (buffer bitShift:-6) bitAnd:16r3F.
       
   198         b2 := buffer bitAnd:16r3F.
       
   199    ] ifFalse:[bits = 16 ifTrue:[
       
   200         buffer := buffer bitShift:2.
       
   201         b4 := 64.        "pad with '='"
       
   202         b3 := buffer bitAnd:16r3F.
       
   203         b2 := (buffer bitShift:-6)  bitAnd:16r3F.
       
   204         b1 := (buffer bitShift:-12) bitAnd:16r3F.
       
   205    ]].
       
   206    bits := buffer := 0.
       
   207 
       
   208    stream nextPut:(Base64Mapping at:b1+1);
       
   209           nextPut:(Base64Mapping at:b2+1);
       
   210           nextPut:(Base64Mapping at:b3+1);
       
   211           nextPut:(Base64Mapping at:b4+1).
       
   212 !
       
   213 
       
   214 reset
       
   215     "reset to initial state"
       
   216 
       
   217     super reset.
       
   218     buffer := bits := charCount := 0.
       
   219     atEnd := false.
       
   220 ! !
       
   221 
       
   222 !Base64Coder methodsFor:'queries'!
       
   223 
       
   224 atEnd
       
   225     "answer true, if no more bytes can be read"
       
   226 
       
   227     ^ atEnd or:[bits == 0 and:[stream atEnd]]
       
   228 ! !
       
   229 
       
   230 !Base64Coder class methodsFor:'documentation'!
       
   231 
       
   232 version
       
   233     ^ '$Header: /cvs/stx/stx/libbasic2/Base64Coder.st,v 1.1 2002-03-18 10:55:48 stefan Exp $'
       
   234 ! !
       
   235 Base64Coder initialize!