initial checkin
authorStefan Vogel <sv@exept.de>
Mon, 18 Mar 2002 11:55:48 +0100
changeset 1027 1c8e7e7785ab
parent 1026 81cd85ca31d7
child 1028 5ca34e5c38b2
initial checkin
Base64Coder.st
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Base64Coder.st	Mon Mar 18 11:55:48 2002 +0100
@@ -0,0 +1,235 @@
+"{ Package: 'stx:libbasic2' }"
+
+ObjectCoder subclass:#Base64Coder
+	instanceVariableNames:'buffer bits charCount atEnd'
+	classVariableNames:'Base64Mapping Base64ReverseMapping'
+	poolDictionaries:''
+	category:'System-Storage'
+!
+
+!Base64Coder class methodsFor:'documentation'!
+
+documentation
+"
+    Instances of this class perform Base64 en- and decoding as defined in RFC 2045
+    3 bytes are mapped to 4 characters, representing 6 bits each.
+
+    [author:]
+        Stefan Vogel
+
+    [see also:]
+
+    [instance variables:]
+        buffer          SmallInteger   Up to 24 bits of data
+        bits            SmallInteger   Number of valid bits in buffer
+        charCount       SmallInteger   Number of characters since last cr
+        atEnd           Boolean        true if end of Base64 string reached
+
+    [class variables:]
+        Base64Mapping         String   Mapping from bytes (with 6 valid bits)   
+                                       to Base64 characters
+        Base64ReverseMapping  Array    Mapping from Base64 characters to 6-bit-Bytes
+"
+!
+
+examples
+"
+
+                                                                [exBegin]
+   |coder|
+
+   coder := Base64Coder on:'' writeStream.
+   coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19].
+   coder flush.
+   coder contents inspect.
+   coder reset.
+   coder nextPut:254.
+   coder contents inspect.
+                                                                [exEnd]
+
+                                                                [exBegin]
+   |coder coder1|
+
+   coder := Base64Coder on:'' writeStream.
+   coder nextPutAll:#[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20].
+   coder flush.
+
+   coder1 := Base64Coder on:coder contents readStream.
+   [coder1 atEnd] whileFalse:[
+      Transcript show:coder1 next
+   ].
+   Transcript cr.
+                                                                [exEnd]
+"
+! !
+
+!Base64Coder class methodsFor:'initialization'!
+
+initialize
+    "initialize class variables"
+
+    "64 characters representing the 6-bit values from 0-63 and one pad character"
+    Base64Mapping := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.
+    Base64ReverseMapping := ByteArray new:256 withAll:255.
+    Base64Mapping keysAndValuesDo:[:idx :char|
+        Base64ReverseMapping at:char asciiValue put:idx-1.
+    ].
+
+    "
+     self initialize
+    "
+! !
+
+!Base64Coder class methodsFor:'instance creation'!
+
+new
+   ^ self basicNew initialize
+! !
+
+!Base64Coder methodsFor:'encoding'!
+
+encodeObject:anObject with:aParameter
+    "not defined. Use nextPut or nextPutAll:.
+     Could encode the printString here"
+
+    ^ self shouldNotImplement
+!
+
+next
+    "answer the next decoded byte"
+
+    |b shift|
+
+    bits == 0 ifTrue:[
+        "no more bits in buffer
+         if end has been reached, raise signal"
+        atEnd ifTrue:[
+             ^ stream endOfStreamSignal raiseRequest.
+        ].
+
+        "read next 4 characters each representing 6 bits"
+        buffer := 0.
+        [
+            "read next valid Base64 character, skip invalid characters"
+            [
+                b := Base64ReverseMapping at:stream next asciiValue.
+            ] doWhile:[b == 255].
+
+            b == 64 ifTrue:[
+                "got #=, end of Base64 string has been reached"
+                atEnd := true.
+                bits == 12 ifTrue:[
+                    "data has been padded to 12, skip 4 bits"
+                    shift := -4.
+                ] ifFalse:[
+                    "data has been padded to 18, skip 2 bits"
+                    shift := -2.
+                ].
+                bits := bits + shift.
+                buffer := buffer bitShift:shift.
+            ] ifFalse:[
+                "got valid Base64 character, append to buffer"
+                buffer := (buffer bitShift:6) bitOr:b.
+                bits := bits + 6.
+            ].
+        ] doWhile:[bits ~~ 24 and:[atEnd not]].
+    ].
+
+    b := (buffer bitShift:(8 - bits)) bitAnd:16rFF.
+    bits := bits - 8.
+
+    ^ b.
+!
+
+nextPut:aByte
+    "encode aByte on the output stream"
+
+    |b1 b2 b3 b4|
+
+    buffer := (buffer bitShift:8) bitOr:aByte.
+    bits := bits + 8.
+    bits == 24 ifTrue:[
+        b4 := buffer bitAnd:16r3F.
+        b3 := (buffer bitShift:-6)  bitAnd:16r3F.
+        b2 := (buffer bitShift:-12) bitAnd:16r3F.
+        b1 := (buffer bitShift:-18) bitAnd:16r3F.
+        buffer := bits := 0.
+        stream nextPut:(Base64Mapping at:b1+1);
+               nextPut:(Base64Mapping at:b2+1);
+               nextPut:(Base64Mapping at:b3+1);
+               nextPut:(Base64Mapping at:b4+1).
+
+        "RFC 2045 says: max 76 characters in one line"
+        charCount >= 68 ifTrue:[
+            stream cr.
+            charCount := 0.
+        ] ifFalse:[
+            charCount := charCount + 4.
+        ]
+    ].
+! !
+
+!Base64Coder methodsFor:'initialization'!
+
+initialize
+
+    buffer := bits := charCount := 0.
+    atEnd := false.
+! !
+
+!Base64Coder methodsFor:'misc'!
+
+flush
+   "flush the remaining bits of buffer. 
+    The number of bits in buffer is not a multiple of 6, so we pad
+    the buffer and signal that padding has been done via $= characters."
+
+   |b1 b2 b3 b4|
+
+   bits == 0 ifTrue:[
+        "buffer is empty, nothing to do"
+        ^ self.
+   ].
+
+   bits == 8 ifTrue:[
+        buffer := buffer bitShift:4.
+        b4 := b3 := 64. "pad with '=='"
+        b1 := (buffer bitShift:-6) bitAnd:16r3F.
+        b2 := buffer bitAnd:16r3F.
+   ] ifFalse:[bits = 16 ifTrue:[
+        buffer := buffer bitShift:2.
+        b4 := 64.        "pad with '='"
+        b3 := buffer bitAnd:16r3F.
+        b2 := (buffer bitShift:-6)  bitAnd:16r3F.
+        b1 := (buffer bitShift:-12) bitAnd:16r3F.
+   ]].
+   bits := buffer := 0.
+
+   stream nextPut:(Base64Mapping at:b1+1);
+          nextPut:(Base64Mapping at:b2+1);
+          nextPut:(Base64Mapping at:b3+1);
+          nextPut:(Base64Mapping at:b4+1).
+!
+
+reset
+    "reset to initial state"
+
+    super reset.
+    buffer := bits := charCount := 0.
+    atEnd := false.
+! !
+
+!Base64Coder methodsFor:'queries'!
+
+atEnd
+    "answer true, if no more bytes can be read"
+
+    ^ atEnd or:[bits == 0 and:[stream atEnd]]
+! !
+
+!Base64Coder class methodsFor:'documentation'!
+
+version
+    ^ '$Header: /cvs/stx/stx/libbasic2/Base64Coder.st,v 1.1 2002-03-18 10:55:48 stefan Exp $'
+! !
+Base64Coder initialize!