=============== Summary ===============
Change Set: base32 Date: 18 May 2023 Author: Christoph Thiede
Adds support for base32 encoding and decoding. Adds Base32MimeConverter class and subsumes it with existing Base64MimeConverter class under new abstract RFC4648MimeConverter class. Adds #base32Encoded and #base32Decoded on String and String+ByteArray, resp. Adds convenient MimeConverter class>>#mimeDecode:. Tests new converter in new Base32MimeConverterTest and subsumes it with existing Base64MimeConverterTest under new abstract RFC4648MimeConverterTest.
=============== Diff ===============
Base32MimeConverter + nil subclass: #Base32MimeConverter + instanceVariableNames: '' + classVariableNames: 'FromCharTable ToCharTable' + poolDictionaries: '' + category: 'Collections-Streams' + + Base32MimeConverter class + instanceVariableNames: '' + + "I encode and decode data in base32 format. I translate a whole stream at once, taking a Stream as input and giving one as output, and answer a whole stream for the sender to use. 0 A 8 I 16 Q 24 Y 1 B 9 J 17 R 25 Z 2 C 10 K 18 S 26 2 3 D 11 L 19 T 27 3 4 E 12 M 20 U 28 4 5 F 13 N 21 V 29 5 6 G 14 O 22 W 30 6 7 H 15 P 23 X 31 7 (pad) = Outbound: bytes are broken into 5 bit chunks, and the 0-31 value is converted to a character. 5 data bytes go into 8 characters. Inbound: Characters are translated in to 0-31 values and shifted into 8 bit bytes."
Base32MimeConverter class>>initialize {class initialization} · ct 5/18/2023 16:07 + initialize + + FromCharTable := Array new: 256. "nils" + ToCharTable := ($A to: $Z) , ($2 to: $7) , '='. + ToCharTable keysAndValuesDo: [:ind :char | + FromCharTable at: char asciiValue + 1 put: ind - 1].
Base32MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:47 + fromCharTable + + ^ FromCharTable
Base32MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:49 + mimeDecode + "Convert the stream in base 32 with only A-Z,2-7 to a full byte stream of characters. Answer a whole stream for the user to read." + + | nibA nibB nibC nibD nibE nibF nibG nibH | + [mimeStream atEnd] whileFalse: [ + (nibA := self nextValue) ifNil: [^ dataStream]. + (nibB := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibA bitShift: 3) + (nibB bitShift: -2)) asCharacter. + nibB := nibB bitAnd: 16r3. + (nibC := self nextValue) ifNil: [^ dataStream]. + (nibD := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibB bitShift: 6) + (nibC bitShift: 1) + (nibD bitShift: -4)) asCharacter. + nibD := nibD bitAnd: 16rF. + (nibE := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibD bitShift: 4) + (nibE bitShift: -1)) asCharacter. + nibE := nibE bitAnd: 16r1. + (nibF := self nextValue) ifNil: [^ dataStream]. + (nibG := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibE bitShift: 7) + (nibF bitShift: 2) + (nibG bitShift: -3)) asCharacter. + nibG := nibG bitAnd: 16r7. + (nibH := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibG bitShift: 5) + nibH) asCharacter. + ]. + ^ dataStream
Base32MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:49 + mimeDecodeToByteArray + "Convert the stream in base 32 with only A-Z,2-7 to a full ByteArray of 0-255 values. Answer a whole stream for the user to read." + + | nibA nibB nibC nibD nibE nibF nibG nibH | + [mimeStream atEnd] whileFalse: [ + (nibA := self nextValue) ifNil: [^ dataStream]. + (nibB := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibA bitShift: 3) + (nibB bitShift: -2)). + nibB := nibB bitAnd: 16r3. + (nibC := self nextValue) ifNil: [^ dataStream]. + (nibD := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibB bitShift: 6) + (nibC bitShift: 1) + (nibD bitShift: -4)). + nibD := nibD bitAnd: 16rF. + (nibE := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibD bitShift: 4) + (nibE bitShift: -1)). + nibE := nibE bitAnd: 16r1. + (nibF := self nextValue) ifNil: [^ dataStream]. + (nibG := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibE bitShift: 7) + (nibF bitShift: 2) + (nibG bitShift: -3)). + nibG := nibG bitAnd: 16r7. + (nibH := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibG bitShift: 5) + nibH). + ]. + ^ dataStream
Base32MimeConverter>>mimeEncode {conversion} · ct 5/18/2023 19:12 + mimeEncode + "Convert from data to 5 bit characters." + + | padding raw nib lineLength | + padding := nil. + lineLength := 0. + [dataStream atEnd] whileFalse: [ + (multiLine and:[lineLength >= 70]) ifTrue: [ mimeStream cr. lineLength := 0. ]. + data := raw := dataStream next asInteger. + nib := (data bitAnd: 16rF8) bitShift: -3. "1111 1000" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding := -6]. + data := ((data bitAnd: 7) bitShift: 8) + raw asInteger. "0000 0111" + nib := (data bitAnd: 16r7C0) bitShift: -6. "0000 0111 1100 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + nib := (data bitAnd: 16r3E) bitShift: -1. "0011 1110" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -4]]. + data := ((data bitAnd: 1) bitShift: 8) + (raw asInteger). "0000 0001" + nib := (data bitAnd: 16r1F0) bitShift: -4. "0000 0001 1111 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -3]]. + data := ((data bitAnd: 16rF) bitShift: 8) + (raw asInteger). "0000 1111" + nib := (data bitAnd: 16rF80) bitShift: -7. "0000 1111 1000 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + nib := (data bitAnd: 16r7C) bitShift: -2. "0111 1100" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -1]]. + data := ((data bitAnd: 3) bitShift: 8) + (raw asInteger). "0000 0011" + nib := (data bitAnd: 16r3E0) bitShift: -5. "0000 0011 1110 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + nib := (data bitAnd: 16r1F). "0001 1111" + mimeStream nextPut: (ToCharTable at: nib+1). + + lineLength := lineLength + 8.]. + + padding ifNotNil: [ + mimeStream skip: padding. + padding to: -1 do: [:i | mimeStream nextPut: $=]. + ^ mimeStream].
Base32MimeConverterTest + nil subclass: #Base32MimeConverterTest + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'CollectionsTests-Streams' + + Base32MimeConverterTest class + instanceVariableNames: '' + + ""
Base32MimeConverterTest>>testBase32Encoded {tests} · ct 5/18/2023 20:08 + testBase32Encoded + + | encoded | + encoded := (self classToBeTested mimeEncode: message) contents. + self assert: string base32Encoded equals: encoded.
Base32MimeConverterTest>>testMimeEncodeDecode {tests} · ct 5/18/2023 20:10 + testMimeEncodeDecode + + | encoded | + encoded := self classToBeTested mimeEncode: message. + self assert: 'JBUSAVDIMVZGKII=' equals: encoded contents. + self assert: message contents equals: (self classToBeTested mimeDecodeToChars: encoded) contents. + + "Encoding should start from the beginning of the stream." + message reset. + message skip: 2. + encoded := self classToBeTested mimeEncode: message. + self assert: 'JBUSAVDIMVZGKII=' equals: encoded contents. + + "Encoding should start from the current position of the stream." + message reset. + message skip: 2. + encoded := self classToBeTested mimeEncodeContinue: message. + self assert: 'EBKGQZLSMUQQ====' equals: encoded contents.
Base32MimeConverterTest>>testMimeEncodeDecodeMultiLine {tests} · ct 5/18/2023 20:07 + testMimeEncodeDecodeMultiLine + + | encoded | + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream) contents. + self assert: encoded = + 'MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB + MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB + MFQWCYLBMFQWCYLB'. + + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents. + self assert: encoded = + 'MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB'.
Base32MimeConverterTest>>testOnByteArray {tests} · ct 5/18/2023 20:07 + testOnByteArray + + self assert: string base32Encoded = string asByteArray base32Encoded.
Base64MimeConverter (changed) - MimeConverter subclass: #Base64MimeConverter - instanceVariableNames: 'data multiLine' + nil subclass: #Base64MimeConverter + instanceVariableNames: '' classVariableNames: 'FromCharTable ToCharTable' poolDictionaries: '' category: 'Collections-Streams'
Base64MimeConverter class instanceVariableNames: ''
"This class encodes and decodes data in Base64 format. This is MIME encoding. We translate a whole stream at once, taking a Stream as input and giving one as output. Returns a whole stream for the caller to use. 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Outbound: bytes are broken into 6 bit chunks, and the 0-63 value is converted to a character. 3 data bytes go into 4 characters. Inbound: Characters are translated in to 0-63 values and shifted into 8 bit bytes.
(See: N. Borenstein, Bellcore, N. Freed, Innosoft, Network Working Group, Request for Comments: RFC 1521, September 1993, MIME (Multipurpose Internet Mail Extensions) Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies. Sec 6.2)
By Ted Kaehler, based on Tim Olson's Base64Filter."
Base64MimeConverter class>>decodeInteger: {convenience} · tk 2/19/2000 15:53 (removed) - decodeInteger: mimeString - | bytes sum | - "Decode the MIME string into an integer of any length" - - bytes := (Base64MimeConverter mimeDecodeToBytes: - (ReadStream on: mimeString)) contents. - sum := 0. - bytes reverseDo: [:by | sum := sum * 256 + by]. - ^ sum
Base64MimeConverter class>>encodeInteger: {convenience} · nice 7/26/2014 23:00 (removed) - encodeInteger: int - | strm | - "Encode an integer of any length and return the MIME string" - - strm := WriteStream on: (ByteArray new: int digitLength). - 1 to: int digitLength do: [:ii | strm nextPut: (int digitAt: ii)]. - ^ ((self mimeEncode: strm readStream) contents) copyUpTo: $= "remove padding"
Base64MimeConverter class>>mimeDecodeToBytes: {convenience} · nice 7/26/2014 23:03 (removed) - mimeDecodeToBytes: aStream - "Return a ReadStream of the original ByteArray. aStream has only 65 innocuous character values. aStream is not binary. (See class comment). 4 bytes in aStream goes to 3 bytes in output." - - | me | - aStream position: 0. - me := self new mimeStream: aStream. - me dataStream: (WriteStream on: (ByteArray new: aStream size * 3 // 4)). - me mimeDecodeToByteArray. - ^ me dataStream readStream
Base64MimeConverter class>>mimeDecodeToChars: {convenience} · nice 7/26/2014 23:01 (removed) - mimeDecodeToChars: aStream - "Return a ReadWriteStream of the original String. aStream has only 65 innocuous character values. It is not binary. (See class comment). 4 bytes in aStream goes to 3 bytes in output." - - | me | - aStream position: 0. - me := self new mimeStream: aStream. - me dataStream: (WriteStream on: (String new: aStream size * 3 // 4)). - me mimeDecode. - ^ me dataStream readStream
Base64MimeConverter class>>mimeEncode: {convenience} · ar 3/9/2010 22:17 (removed) - mimeEncode: aStream - "Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - ^self mimeEncode: aStream multiLine: true atStart: true
Base64MimeConverter class>>mimeEncode:multiLine: {convenience} · ar 3/9/2010 22:17 (removed) - mimeEncode: aStream multiLine: aBool - "Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - - ^self mimeEncode: aStream multiLine: aBool atStart: true
Base64MimeConverter class>>mimeEncode:multiLine:atStart: {private - convenience} · nice 7/26/2014 22:59 (removed) - mimeEncode: aStream multiLine: aBool atStart: resetInput - "Return a ReadStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - - | me | - resetInput ifTrue:[aStream position: 0]. - me := self new dataStream: aStream. - me multiLine: aBool. - me mimeStream: (WriteStream on: (String new: aStream size + 20 * 4 // 3)). - me mimeEncode. - ^ me mimeStream readStream
Base64MimeConverter class>>mimeEncodeContinue: {private - convenience} · ar 3/9/2010 22:17 (removed) - mimeEncodeContinue: aStream - "Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - ^self mimeEncode: aStream multiLine: true atStart: false
Base64MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:47 + fromCharTable + + ^ FromCharTable
Base64MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:49 (changed) mimeDecode - "Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Return a whole stream for the user to read." + "Convert the stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Answer a whole stream for the user to read."
| nibA nibB nibC nibD | [mimeStream atEnd] whileFalse: [ (nibA := self nextValue) ifNil: [^ dataStream]. (nibB := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)) asCharacter. nibB := nibB bitAnd: 16rF. (nibC := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)) asCharacter. nibC := nibC bitAnd: 16r3. (nibD := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibC bitShift: 6) + nibD) asCharacter. ]. ^ dataStream
Base64MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:50 (changed) mimeDecodeToByteArray - "Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Return a whole stream for the user to read." + "Convert the stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Answer a whole stream for the user to read."
| nibA nibB nibC nibD | [mimeStream atEnd] whileFalse: [ (nibA := self nextValue) ifNil: [^ dataStream]. (nibB := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)). nibB := nibB bitAnd: 16rF. (nibC := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)). nibC := nibC bitAnd: 16r3. (nibD := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibC bitShift: 6) + nibD). ]. ^ dataStream
Base64MimeConverterTest (changed) - TestCase subclass: #Base64MimeConverterTest - instanceVariableNames: 'message' + nil subclass: #Base64MimeConverterTest + instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'CollectionsTests-Streams'
Base64MimeConverterTest class instanceVariableNames: ''
"This is the unit test for the class Base64MimeConverter. Unit tests are a good way to exercise the functionality of your system in a repeatable and automatic manner. They are therefore recommended if you plan to release anything. For more information, see: - http://www.c2.com/cgi/wiki?UnitTest - http://minnow.cc.gatech.edu/squeak/1547 - the sunit class category"
Base64MimeConverterTest>>testBase64Encoded {tests} · ct 5/18/2023 20:09 (changed) testBase64Encoded + | encoded | - encoded := (Base64MimeConverter mimeEncode: message) contents. - self assert: encoded = 'Hi There!' base64Encoded. - + encoded := (self classToBeTested mimeEncode: message) contents. + self assert: string base64Encoded equals: encoded.
Base64MimeConverterTest>>testMimeEncodeDecode {tests} · ct 5/18/2023 20:10 (changed) testMimeEncodeDecode
| encoded | - encoded := Base64MimeConverter mimeEncode: message. - self assert: (encoded contents = 'SGkgVGhlcmUh'). - self assert: ((Base64MimeConverter mimeDecodeToChars: encoded) contents = message contents). - + encoded := self classToBeTested mimeEncode: message. + self assert: 'SGkgVGhlcmUh' equals: encoded contents. + self assert: message contents equals: (self classToBeTested mimeDecodeToChars: encoded) contents. + "Encoding should start from the beginning of the stream." message reset. message skip: 2. - encoded := Base64MimeConverter mimeEncode: message. - self assert: (encoded contents = 'SGkgVGhlcmUh'). + encoded := self classToBeTested mimeEncode: message. + self assert: 'SGkgVGhlcmUh' equals: encoded contents. "Encoding should start from the current position of the stream." message reset. message skip: 2. - encoded := Base64MimeConverter mimeEncodeContinue: message. - self assert: (encoded contents = 'IFRoZXJlIQ=='). + encoded := self classToBeTested mimeEncodeContinue: message. + self assert: 'IFRoZXJlIQ==' equals: encoded contents.
Base64MimeConverterTest>>testMimeEncodeDecodeMultiLine {tests} · ct 5/18/2023 20:11 (changed) testMimeEncodeDecodeMultiLine
| encoded | - encoded := (Base64MimeConverter mimeEncode: (String new: 100 withAll: $a) readStream) contents. + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream) contents. self assert: encoded = 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='. - - encoded := (Base64MimeConverter mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents. + + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents. self assert: encoded = - 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='. + 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='.
Base64MimeConverterTest>>testOnByteArray {tests} · ct 5/18/2023 20:11 (changed) testOnByteArray - self assert: ('Hi There!' base64Encoded = 'Hi There!' asByteArray base64Encoded) + + self assert: string base64Encoded = string asByteArray base64Encoded.
ByteArray>>base32Encoded {converting} · ct 5/18/2023 17:46 + base32Encoded + "Encode the receiver as base32" + "'Hello World' asByteArray base32Encoded" + ^(Base32MimeConverter mimeEncode: self readStream) contents
MimeConverter class>>mimeDecode: {convenience} · ct 5/18/2023 20:21 + mimeDecode: aCollectionOrStream + + ^ String streamContents: [:out | + self mimeDecode: aCollectionOrStream to: out]
RFC4648MimeConverter + MimeConverter subclass: #RFC4648MimeConverter + instanceVariableNames: 'data multiLine' + classVariableNames: '' + poolDictionaries: '' + category: 'Collections-Streams' + + RFC4648MimeConverter class + instanceVariableNames: '' + + "I am the abstract superclass for power-of-two-base mime converters according to RFC4648. We translate a whole stream at once, taking a Stream as input and giving one as output, and answer a whole stream for the sender to use."
RFC4648MimeConverter class>>decodeInteger: {convenience} · ct 5/18/2023 19:50 + decodeInteger: mimeString + "Decode the MIME string into an integer of any length" + + | bytes sum | + bytes := (self mimeDecodeToBytes: + (ReadStream on: mimeString)) contents. + sum := 0. + bytes reverseDo: [:by | sum := sum * 256 + by]. + ^ sum
RFC4648MimeConverter class>>encodeInteger: {convenience} · ct 5/18/2023 19:51 + encodeInteger: anInteger + "Encode anInteger of any length and return the MIME string" + + | stream | + stream := WriteStream on: (ByteArray new: anInteger digitLength). + 1 to: anInteger digitLength do: [:ii | stream nextPut: (anInteger digitAt: ii)]. + ^ ((self mimeEncode: stream readStream) contents) copyUpTo: $= "remove padding"
RFC4648MimeConverter class>>mimeDecodeToBytes: {convenience} · ct 5/18/2023 19:53 + mimeDecodeToBytes: aStream + "Return a ReadStream of the original ByteArray. aStream has only 33/65/... innocuous character values. aStream is not binary. (See class comment). 8/4/... bytes in aStream goes to 5/3/... bytes in output." + + | me | + aStream position: 0. + me := self new mimeStream: aStream. + me dataStream: (WriteStream on: (ByteArray new: aStream size * 3 // 4)). + me mimeDecodeToByteArray. + ^ me dataStream readStream
RFC4648MimeConverter class>>mimeDecodeToChars: {convenience} · ct 5/18/2023 19:54 + mimeDecodeToChars: aStream + "Return a ReadWriteStream of the original String. aStream has only 33/65/... innocuous character values. It is not binary. (See class comment). 8/4/... bytes in aStream goes to 5/3/... bytes in output." + + | me | + aStream position: 0. + me := self new mimeStream: aStream. + me dataStream: (WriteStream on: (String new: aStream size * 3 // 4)). + me mimeDecode. + ^ me dataStream readStream
RFC4648MimeConverter class>>mimeEncode: {convenience} · ct 5/18/2023 19:54 + mimeEncode: aStream + "Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + self flag: #suspicious. "ct: All other classes answer a String, just this one answers a ReadStream. Unfortunately, expectations of senders are inconsistent as well ..." + ^self mimeEncode: aStream multiLine: true atStart: true
RFC4648MimeConverter class>>mimeEncode:multiLine: {convenience} · ct 5/18/2023 19:55 + mimeEncode: aStream multiLine: aBool + "Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + + ^self mimeEncode: aStream multiLine: aBool atStart: true
RFC4648MimeConverter class>>mimeEncode:multiLine:atStart: {private - convenience} · ct 5/18/2023 19:55 + mimeEncode: aStream multiLine: aBool atStart: resetInput + "Return a ReadStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + + | me | + resetInput ifTrue:[aStream position: 0]. + me := self new dataStream: aStream. + me multiLine: aBool. + me mimeStream: (WriteStream on: (String new: aStream size + 20 * 4 // 3)). + me mimeEncode. + ^ me mimeStream readStream
RFC4648MimeConverter class>>mimeEncodeContinue: {private - convenience} · ct 5/18/2023 19:55 + mimeEncodeContinue: aStream + "Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + ^self mimeEncode: aStream multiLine: true atStart: false
RFC4648MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:46 + fromCharTable + + ^ self subclassResponsibility
RFC4648MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:48 + mimeDecode + "Convert the stream in the respective base to a full byte stream of characters. Answer a whole stream for the user to read." + + ^ self subclassResponsibility
RFC4648MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:49 + mimeDecodeToByteArray + "Convert the stream in the respective base to a full ByteArray of 0-255 values. Answer a whole stream for the user to read." + + ^ self subclassResponsibility
RFC4648MimeConverter>>mimeEncode {conversion} · ct 5/18/2023 19:45 + mimeEncode + "Convert from data to n bit characters." + + ^ self subclassResponsibility
RFC4648MimeConverter>>multiLine {accessing} · ar 4/15/2008 17:58 + multiLine + "Determines whether we allow multi-line encodings (the default) or force everything into a single line (for use with URLs etc. where the continuation marker and the line break cause problems)" + ^multiLine
RFC4648MimeConverter>>multiLine: {accessing} · ar 4/15/2008 17:58 + multiLine: aBool + "Determines whether we allow multi-line encodings (the default) or force everything into a single line (for use with URLs etc. where the continuation marker and the line break cause problems)" + multiLine := aBool
RFC4648MimeConverter>>nextValue {conversion} · ct 5/18/2023 19:46 + nextValue + "The next n bits of data char from the mimeStream, or nil. Skip all other chars" + | raw num | + [raw := mimeStream next. + raw ifNil: [^ nil]. "end of stream" + raw == $= ifTrue: [^ nil]. + num := self fromCharTable at: raw asciiValue + 1. + num ifNotNil: [^ num]. + "else ignore space, return, tab, ..." + ] repeat
RFC4648MimeConverterTest + ClassTestCase subclass: #RFC4648MimeConverterTest + instanceVariableNames: 'string message' + classVariableNames: '' + poolDictionaries: '' + category: 'CollectionsTests-Streams' + + RFC4648MimeConverterTest class + instanceVariableNames: '' + + ""
RFC4648MimeConverterTest class>>isAbstract {testing} · ct 5/18/2023 20:08 + isAbstract + + ^ self name = #RFC4648MimeConverterTest
RFC4648MimeConverterTest>>setUp {running} · ct 5/18/2023 20:04 + setUp + string := 'Hi There!'. + message := string readStream.
String>>base32Decoded {converting} · ct 5/18/2023 19:38 + base32Decoded + "Decode the receiver from base 32" + "'JBSWY3DPEBLW64TMMQ======' base32Decoded" + ^ Base32MimeConverter mimeDecode: self as: self class
String>>base32Encoded {converting} · ct 5/18/2023 19:15 + base32Encoded + "Encode the receiver as base32" + "'Hello World' base32Encoded" + + ^(Base32MimeConverter + mimeEncode: (ReadStream on: self) + multiLine: false) contents
StringTest>>testBase32 {tests - converting} · ct 5/18/2023 20:15 + testBase32 + + self + assert: 'JBSWY3DPEBLW64TMMQ======' base32Decoded = 'Hello World'; + assert: 'Hello World' base32Encoded = 'JBSWY3DPEBLW64TMMQ======'; + assert: (String new: 100 withAll: $x) base32Encoded = 'PB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DY'.
--- Sent from Squeak Inbox Talk ["base32.4.cs"]
Hi Christoph --
+1
Hmm... why does the diff show "nil subclass: ..." in the definitions?
Best, Marcel Am 18.05.2023 20:37:52 schrieb christoph.thiede@student.hpi.uni-potsdam.de christoph.thiede@student.hpi.uni-potsdam.de: =============== Summary ===============
Change Set: base32 Date: 18 May 2023 Author: Christoph Thiede
Adds support for base32 encoding and decoding. Adds Base32MimeConverter class and subsumes it with existing Base64MimeConverter class under new abstract RFC4648MimeConverter class. Adds #base32Encoded and #base32Decoded on String and String+ByteArray, resp. Adds convenient MimeConverter class>>#mimeDecode:. Tests new converter in new Base32MimeConverterTest and subsumes it with existing Base64MimeConverterTest under new abstract RFC4648MimeConverterTest.
=============== Diff ===============
Base32MimeConverter + nil subclass: #Base32MimeConverter + instanceVariableNames: '' + classVariableNames: 'FromCharTable ToCharTable' + poolDictionaries: '' + category: 'Collections-Streams' + + Base32MimeConverter class + instanceVariableNames: '' + + "I encode and decode data in base32 format. I translate a whole stream at once, taking a Stream as input and giving one as output, and answer a whole stream for the sender to use. 0 A 8 I 16 Q 24 Y 1 B 9 J 17 R 25 Z 2 C 10 K 18 S 26 2 3 D 11 L 19 T 27 3 4 E 12 M 20 U 28 4 5 F 13 N 21 V 29 5 6 G 14 O 22 W 30 6 7 H 15 P 23 X 31 7 (pad) = Outbound: bytes are broken into 5 bit chunks, and the 0-31 value is converted to a character. 5 data bytes go into 8 characters. Inbound: Characters are translated in to 0-31 values and shifted into 8 bit bytes."
Base32MimeConverter class>>initialize {class initialization} · ct 5/18/2023 16:07 + initialize + + FromCharTable := Array new: 256. "nils" + ToCharTable := ($A to: $Z) , ($2 to: $7) , '='. + ToCharTable keysAndValuesDo: [:ind :char | + FromCharTable at: char asciiValue + 1 put: ind - 1].
Base32MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:47 + fromCharTable + + ^ FromCharTable
Base32MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:49 + mimeDecode + "Convert the stream in base 32 with only A-Z,2-7 to a full byte stream of characters. Answer a whole stream for the user to read." + + | nibA nibB nibC nibD nibE nibF nibG nibH | + [mimeStream atEnd] whileFalse: [ + (nibA := self nextValue) ifNil: [^ dataStream]. + (nibB := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibA bitShift: 3) + (nibB bitShift: -2)) asCharacter. + nibB := nibB bitAnd: 16r3. + (nibC := self nextValue) ifNil: [^ dataStream]. + (nibD := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibB bitShift: 6) + (nibC bitShift: 1) + (nibD bitShift: -4)) asCharacter. + nibD := nibD bitAnd: 16rF. + (nibE := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibD bitShift: 4) + (nibE bitShift: -1)) asCharacter. + nibE := nibE bitAnd: 16r1. + (nibF := self nextValue) ifNil: [^ dataStream]. + (nibG := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibE bitShift: 7) + (nibF bitShift: 2) + (nibG bitShift: -3)) asCharacter. + nibG := nibG bitAnd: 16r7. + (nibH := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibG bitShift: 5) + nibH) asCharacter. + ]. + ^ dataStream
Base32MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:49 + mimeDecodeToByteArray + "Convert the stream in base 32 with only A-Z,2-7 to a full ByteArray of 0-255 values. Answer a whole stream for the user to read." + + | nibA nibB nibC nibD nibE nibF nibG nibH | + [mimeStream atEnd] whileFalse: [ + (nibA := self nextValue) ifNil: [^ dataStream]. + (nibB := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibA bitShift: 3) + (nibB bitShift: -2)). + nibB := nibB bitAnd: 16r3. + (nibC := self nextValue) ifNil: [^ dataStream]. + (nibD := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibB bitShift: 6) + (nibC bitShift: 1) + (nibD bitShift: -4)). + nibD := nibD bitAnd: 16rF. + (nibE := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibD bitShift: 4) + (nibE bitShift: -1)). + nibE := nibE bitAnd: 16r1. + (nibF := self nextValue) ifNil: [^ dataStream]. + (nibG := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibE bitShift: 7) + (nibF bitShift: 2) + (nibG bitShift: -3)). + nibG := nibG bitAnd: 16r7. + (nibH := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibG bitShift: 5) + nibH). + ]. + ^ dataStream
Base32MimeConverter>>mimeEncode {conversion} · ct 5/18/2023 19:12 + mimeEncode + "Convert from data to 5 bit characters." + + | padding raw nib lineLength | + padding := nil. + lineLength := 0. + [dataStream atEnd] whileFalse: [ + (multiLine and:[lineLength >= 70]) ifTrue: [ mimeStream cr. lineLength := 0. ]. + data := raw := dataStream next asInteger. + nib := (data bitAnd: 16rF8) bitShift: -3. "1111 1000" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding := -6]. + data := ((data bitAnd: 7) bitShift: 8) + raw asInteger. "0000 0111" + nib := (data bitAnd: 16r7C0) bitShift: -6. "0000 0111 1100 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + nib := (data bitAnd: 16r3E) bitShift: -1. "0011 1110" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -4]]. + data := ((data bitAnd: 1) bitShift: 8) + (raw asInteger). "0000 0001" + nib := (data bitAnd: 16r1F0) bitShift: -4. "0000 0001 1111 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -3]]. + data := ((data bitAnd: 16rF) bitShift: 8) + (raw asInteger). "0000 1111" + nib := (data bitAnd: 16rF80) bitShift: -7. "0000 1111 1000 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + nib := (data bitAnd: 16r7C) bitShift: -2. "0111 1100" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -1]]. + data := ((data bitAnd: 3) bitShift: 8) + (raw asInteger). "0000 0011" + nib := (data bitAnd: 16r3E0) bitShift: -5. "0000 0011 1110 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + nib := (data bitAnd: 16r1F). "0001 1111" + mimeStream nextPut: (ToCharTable at: nib+1). + + lineLength := lineLength + 8.]. + + padding ifNotNil: [ + mimeStream skip: padding. + padding to: -1 do: [:i | mimeStream nextPut: $=]. + ^ mimeStream].
Base32MimeConverterTest + nil subclass: #Base32MimeConverterTest + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'CollectionsTests-Streams' + + Base32MimeConverterTest class + instanceVariableNames: '' + + ""
Base32MimeConverterTest>>testBase32Encoded {tests} · ct 5/18/2023 20:08 + testBase32Encoded + + | encoded | + encoded := (self classToBeTested mimeEncode: message) contents. + self assert: string base32Encoded equals: encoded.
Base32MimeConverterTest>>testMimeEncodeDecode {tests} · ct 5/18/2023 20:10 + testMimeEncodeDecode + + | encoded | + encoded := self classToBeTested mimeEncode: message. + self assert: 'JBUSAVDIMVZGKII=' equals: encoded contents. + self assert: message contents equals: (self classToBeTested mimeDecodeToChars: encoded) contents. + + "Encoding should start from the beginning of the stream." + message reset. + message skip: 2. + encoded := self classToBeTested mimeEncode: message. + self assert: 'JBUSAVDIMVZGKII=' equals: encoded contents. + + "Encoding should start from the current position of the stream." + message reset. + message skip: 2. + encoded := self classToBeTested mimeEncodeContinue: message. + self assert: 'EBKGQZLSMUQQ====' equals: encoded contents.
Base32MimeConverterTest>>testMimeEncodeDecodeMultiLine {tests} · ct 5/18/2023 20:07 + testMimeEncodeDecodeMultiLine + + | encoded | + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream) contents. + self assert: encoded = + 'MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB + MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB + MFQWCYLBMFQWCYLB'. + + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents. + self assert: encoded = + 'MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB'.
Base32MimeConverterTest>>testOnByteArray {tests} · ct 5/18/2023 20:07 + testOnByteArray + + self assert: string base32Encoded = string asByteArray base32Encoded.
Base64MimeConverter (changed) - MimeConverter subclass: #Base64MimeConverter - instanceVariableNames: 'data multiLine' + nil subclass: #Base64MimeConverter + instanceVariableNames: '' classVariableNames: 'FromCharTable ToCharTable' poolDictionaries: '' category: 'Collections-Streams'
Base64MimeConverter class instanceVariableNames: ''
"This class encodes and decodes data in Base64 format. This is MIME encoding. We translate a whole stream at once, taking a Stream as input and giving one as output. Returns a whole stream for the caller to use. 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Outbound: bytes are broken into 6 bit chunks, and the 0-63 value is converted to a character. 3 data bytes go into 4 characters. Inbound: Characters are translated in to 0-63 values and shifted into 8 bit bytes.
(See: N. Borenstein, Bellcore, N. Freed, Innosoft, Network Working Group, Request for Comments: RFC 1521, September 1993, MIME (Multipurpose Internet Mail Extensions) Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies. Sec 6.2)
By Ted Kaehler, based on Tim Olson's Base64Filter."
Base64MimeConverter class>>decodeInteger: {convenience} · tk 2/19/2000 15:53 (removed) - decodeInteger: mimeString - | bytes sum | - "Decode the MIME string into an integer of any length" - - bytes := (Base64MimeConverter mimeDecodeToBytes: - (ReadStream on: mimeString)) contents. - sum := 0. - bytes reverseDo: [:by | sum := sum * 256 + by]. - ^ sum
Base64MimeConverter class>>encodeInteger: {convenience} · nice 7/26/2014 23:00 (removed) - encodeInteger: int - | strm | - "Encode an integer of any length and return the MIME string" - - strm := WriteStream on: (ByteArray new: int digitLength). - 1 to: int digitLength do: [:ii | strm nextPut: (int digitAt: ii)]. - ^ ((self mimeEncode: strm readStream) contents) copyUpTo: $= "remove padding"
Base64MimeConverter class>>mimeDecodeToBytes: {convenience} · nice 7/26/2014 23:03 (removed) - mimeDecodeToBytes: aStream - "Return a ReadStream of the original ByteArray. aStream has only 65 innocuous character values. aStream is not binary. (See class comment). 4 bytes in aStream goes to 3 bytes in output." - - | me | - aStream position: 0. - me := self new mimeStream: aStream. - me dataStream: (WriteStream on: (ByteArray new: aStream size * 3 // 4)). - me mimeDecodeToByteArray. - ^ me dataStream readStream
Base64MimeConverter class>>mimeDecodeToChars: {convenience} · nice 7/26/2014 23:01 (removed) - mimeDecodeToChars: aStream - "Return a ReadWriteStream of the original String. aStream has only 65 innocuous character values. It is not binary. (See class comment). 4 bytes in aStream goes to 3 bytes in output." - - | me | - aStream position: 0. - me := self new mimeStream: aStream. - me dataStream: (WriteStream on: (String new: aStream size * 3 // 4)). - me mimeDecode. - ^ me dataStream readStream
Base64MimeConverter class>>mimeEncode: {convenience} · ar 3/9/2010 22:17 (removed) - mimeEncode: aStream - "Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - ^self mimeEncode: aStream multiLine: true atStart: true
Base64MimeConverter class>>mimeEncode:multiLine: {convenience} · ar 3/9/2010 22:17 (removed) - mimeEncode: aStream multiLine: aBool - "Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - - ^self mimeEncode: aStream multiLine: aBool atStart: true
Base64MimeConverter class>>mimeEncode:multiLine:atStart: {private - convenience} · nice 7/26/2014 22:59 (removed) - mimeEncode: aStream multiLine: aBool atStart: resetInput - "Return a ReadStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - - | me | - resetInput ifTrue:[aStream position: 0]. - me := self new dataStream: aStream. - me multiLine: aBool. - me mimeStream: (WriteStream on: (String new: aStream size + 20 * 4 // 3)). - me mimeEncode. - ^ me mimeStream readStream
Base64MimeConverter class>>mimeEncodeContinue: {private - convenience} · ar 3/9/2010 22:17 (removed) - mimeEncodeContinue: aStream - "Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - ^self mimeEncode: aStream multiLine: true atStart: false
Base64MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:47 + fromCharTable + + ^ FromCharTable
Base64MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:49 (changed) mimeDecode - "Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Return a whole stream for the user to read." + "Convert the stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Answer a whole stream for the user to read."
| nibA nibB nibC nibD | [mimeStream atEnd] whileFalse: [ (nibA := self nextValue) ifNil: [^ dataStream]. (nibB := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)) asCharacter. nibB := nibB bitAnd: 16rF. (nibC := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)) asCharacter. nibC := nibC bitAnd: 16r3. (nibD := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibC bitShift: 6) + nibD) asCharacter. ]. ^ dataStream
Base64MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:50 (changed) mimeDecodeToByteArray - "Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Return a whole stream for the user to read." + "Convert the stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Answer a whole stream for the user to read."
| nibA nibB nibC nibD | [mimeStream atEnd] whileFalse: [ (nibA := self nextValue) ifNil: [^ dataStream]. (nibB := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)). nibB := nibB bitAnd: 16rF. (nibC := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)). nibC := nibC bitAnd: 16r3. (nibD := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibC bitShift: 6) + nibD). ]. ^ dataStream
Base64MimeConverterTest (changed) - TestCase subclass: #Base64MimeConverterTest - instanceVariableNames: 'message' + nil subclass: #Base64MimeConverterTest + instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'CollectionsTests-Streams'
Base64MimeConverterTest class instanceVariableNames: ''
"This is the unit test for the class Base64MimeConverter. Unit tests are a good way to exercise the functionality of your system in a repeatable and automatic manner. They are therefore recommended if you plan to release anything. For more information, see: - http://www.c2.com/cgi/wiki?UnitTest - http://minnow.cc.gatech.edu/squeak/1547 - the sunit class category"
Base64MimeConverterTest>>testBase64Encoded {tests} · ct 5/18/2023 20:09 (changed) testBase64Encoded + | encoded | - encoded := (Base64MimeConverter mimeEncode: message) contents. - self assert: encoded = 'Hi There!' base64Encoded. - + encoded := (self classToBeTested mimeEncode: message) contents. + self assert: string base64Encoded equals: encoded.
Base64MimeConverterTest>>testMimeEncodeDecode {tests} · ct 5/18/2023 20:10 (changed) testMimeEncodeDecode
| encoded | - encoded := Base64MimeConverter mimeEncode: message. - self assert: (encoded contents = 'SGkgVGhlcmUh'). - self assert: ((Base64MimeConverter mimeDecodeToChars: encoded) contents = message contents). - + encoded := self classToBeTested mimeEncode: message. + self assert: 'SGkgVGhlcmUh' equals: encoded contents. + self assert: message contents equals: (self classToBeTested mimeDecodeToChars: encoded) contents. + "Encoding should start from the beginning of the stream." message reset. message skip: 2. - encoded := Base64MimeConverter mimeEncode: message. - self assert: (encoded contents = 'SGkgVGhlcmUh'). + encoded := self classToBeTested mimeEncode: message. + self assert: 'SGkgVGhlcmUh' equals: encoded contents. "Encoding should start from the current position of the stream." message reset. message skip: 2. - encoded := Base64MimeConverter mimeEncodeContinue: message. - self assert: (encoded contents = 'IFRoZXJlIQ=='). + encoded := self classToBeTested mimeEncodeContinue: message. + self assert: 'IFRoZXJlIQ==' equals: encoded contents.
Base64MimeConverterTest>>testMimeEncodeDecodeMultiLine {tests} · ct 5/18/2023 20:11 (changed) testMimeEncodeDecodeMultiLine
| encoded | - encoded := (Base64MimeConverter mimeEncode: (String new: 100 withAll: $a) readStream) contents. + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream) contents. self assert: encoded = 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='. - - encoded := (Base64MimeConverter mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents. + + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents. self assert: encoded = - 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='. + 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='.
Base64MimeConverterTest>>testOnByteArray {tests} · ct 5/18/2023 20:11 (changed) testOnByteArray - self assert: ('Hi There!' base64Encoded = 'Hi There!' asByteArray base64Encoded) + + self assert: string base64Encoded = string asByteArray base64Encoded.
ByteArray>>base32Encoded {converting} · ct 5/18/2023 17:46 + base32Encoded + "Encode the receiver as base32" + "'Hello World' asByteArray base32Encoded" + ^(Base32MimeConverter mimeEncode: self readStream) contents
MimeConverter class>>mimeDecode: {convenience} · ct 5/18/2023 20:21 + mimeDecode: aCollectionOrStream + + ^ String streamContents: [:out | + self mimeDecode: aCollectionOrStream to: out]
RFC4648MimeConverter + MimeConverter subclass: #RFC4648MimeConverter + instanceVariableNames: 'data multiLine' + classVariableNames: '' + poolDictionaries: '' + category: 'Collections-Streams' + + RFC4648MimeConverter class + instanceVariableNames: '' + + "I am the abstract superclass for power-of-two-base mime converters according to RFC4648. We translate a whole stream at once, taking a Stream as input and giving one as output, and answer a whole stream for the sender to use."
RFC4648MimeConverter class>>decodeInteger: {convenience} · ct 5/18/2023 19:50 + decodeInteger: mimeString + "Decode the MIME string into an integer of any length" + + | bytes sum | + bytes := (self mimeDecodeToBytes: + (ReadStream on: mimeString)) contents. + sum := 0. + bytes reverseDo: [:by | sum := sum * 256 + by]. + ^ sum
RFC4648MimeConverter class>>encodeInteger: {convenience} · ct 5/18/2023 19:51 + encodeInteger: anInteger + "Encode anInteger of any length and return the MIME string" + + | stream | + stream := WriteStream on: (ByteArray new: anInteger digitLength). + 1 to: anInteger digitLength do: [:ii | stream nextPut: (anInteger digitAt: ii)]. + ^ ((self mimeEncode: stream readStream) contents) copyUpTo: $= "remove padding"
RFC4648MimeConverter class>>mimeDecodeToBytes: {convenience} · ct 5/18/2023 19:53 + mimeDecodeToBytes: aStream + "Return a ReadStream of the original ByteArray. aStream has only 33/65/... innocuous character values. aStream is not binary. (See class comment). 8/4/... bytes in aStream goes to 5/3/... bytes in output." + + | me | + aStream position: 0. + me := self new mimeStream: aStream. + me dataStream: (WriteStream on: (ByteArray new: aStream size * 3 // 4)). + me mimeDecodeToByteArray. + ^ me dataStream readStream
RFC4648MimeConverter class>>mimeDecodeToChars: {convenience} · ct 5/18/2023 19:54 + mimeDecodeToChars: aStream + "Return a ReadWriteStream of the original String. aStream has only 33/65/... innocuous character values. It is not binary. (See class comment). 8/4/... bytes in aStream goes to 5/3/... bytes in output." + + | me | + aStream position: 0. + me := self new mimeStream: aStream. + me dataStream: (WriteStream on: (String new: aStream size * 3 // 4)). + me mimeDecode. + ^ me dataStream readStream
RFC4648MimeConverter class>>mimeEncode: {convenience} · ct 5/18/2023 19:54 + mimeEncode: aStream + "Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + self flag: #suspicious. "ct: All other classes answer a String, just this one answers a ReadStream. Unfortunately, expectations of senders are inconsistent as well ..." + ^self mimeEncode: aStream multiLine: true atStart: true
RFC4648MimeConverter class>>mimeEncode:multiLine: {convenience} · ct 5/18/2023 19:55 + mimeEncode: aStream multiLine: aBool + "Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + + ^self mimeEncode: aStream multiLine: aBool atStart: true
RFC4648MimeConverter class>>mimeEncode:multiLine:atStart: {private - convenience} · ct 5/18/2023 19:55 + mimeEncode: aStream multiLine: aBool atStart: resetInput + "Return a ReadStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + + | me | + resetInput ifTrue:[aStream position: 0]. + me := self new dataStream: aStream. + me multiLine: aBool. + me mimeStream: (WriteStream on: (String new: aStream size + 20 * 4 // 3)). + me mimeEncode. + ^ me mimeStream readStream
RFC4648MimeConverter class>>mimeEncodeContinue: {private - convenience} · ct 5/18/2023 19:55 + mimeEncodeContinue: aStream + "Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + ^self mimeEncode: aStream multiLine: true atStart: false
RFC4648MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:46 + fromCharTable + + ^ self subclassResponsibility
RFC4648MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:48 + mimeDecode + "Convert the stream in the respective base to a full byte stream of characters. Answer a whole stream for the user to read." + + ^ self subclassResponsibility
RFC4648MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:49 + mimeDecodeToByteArray + "Convert the stream in the respective base to a full ByteArray of 0-255 values. Answer a whole stream for the user to read." + + ^ self subclassResponsibility
RFC4648MimeConverter>>mimeEncode {conversion} · ct 5/18/2023 19:45 + mimeEncode + "Convert from data to n bit characters." + + ^ self subclassResponsibility
RFC4648MimeConverter>>multiLine {accessing} · ar 4/15/2008 17:58 + multiLine + "Determines whether we allow multi-line encodings (the default) or force everything into a single line (for use with URLs etc. where the continuation marker and the line break cause problems)" + ^multiLine
RFC4648MimeConverter>>multiLine: {accessing} · ar 4/15/2008 17:58 + multiLine: aBool + "Determines whether we allow multi-line encodings (the default) or force everything into a single line (for use with URLs etc. where the continuation marker and the line break cause problems)" + multiLine := aBool
RFC4648MimeConverter>>nextValue {conversion} · ct 5/18/2023 19:46 + nextValue + "The next n bits of data char from the mimeStream, or nil. Skip all other chars" + | raw num | + [raw := mimeStream next. + raw ifNil: [^ nil]. "end of stream" + raw == $= ifTrue: [^ nil]. + num := self fromCharTable at: raw asciiValue + 1. + num ifNotNil: [^ num]. + "else ignore space, return, tab, ..." + ] repeat
RFC4648MimeConverterTest + ClassTestCase subclass: #RFC4648MimeConverterTest + instanceVariableNames: 'string message' + classVariableNames: '' + poolDictionaries: '' + category: 'CollectionsTests-Streams' + + RFC4648MimeConverterTest class + instanceVariableNames: '' + + ""
RFC4648MimeConverterTest class>>isAbstract {testing} · ct 5/18/2023 20:08 + isAbstract + + ^ self name = #RFC4648MimeConverterTest
RFC4648MimeConverterTest>>setUp {running} · ct 5/18/2023 20:04 + setUp + string := 'Hi There!'. + message := string readStream.
String>>base32Decoded {converting} · ct 5/18/2023 19:38 + base32Decoded + "Decode the receiver from base 32" + "'JBSWY3DPEBLW64TMMQ======' base32Decoded" + ^ Base32MimeConverter mimeDecode: self as: self class
String>>base32Encoded {converting} · ct 5/18/2023 19:15 + base32Encoded + "Encode the receiver as base32" + "'Hello World' base32Encoded" + + ^(Base32MimeConverter + mimeEncode: (ReadStream on: self) + multiLine: false) contents
StringTest>>testBase32 {tests - converting} · ct 5/18/2023 20:15 + testBase32 + + self + assert: 'JBSWY3DPEBLW64TMMQ======' base32Decoded = 'Hello World'; + assert: 'Hello World' base32Encoded = 'JBSWY3DPEBLW64TMMQ======'; + assert: (String new: 100 withAll: $x) base32Encoded = 'PB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DY'.
--- Sent from Squeak Inbox Talk [https://github.com/hpi-swa-lab/squeak-inbox-talk] ["base32.4.cs"]
Hi Marcel,
Hmm... why does the diff show "nil subclass: ..." in the definitions?
Sorry for that. It's my own implementation of generating nice diff texts for changesets. It's yet prototypical, and apparently, it cannot handle superclasses that are not loaded in the image. I will also have to fix some other quirks such as support for removals and postscript before I contribute that to the Trunk. :-)
Best, Christoph
________________________________ Von: Marcel Taeumel via Squeak-dev squeak-dev@lists.squeakfoundation.org Gesendet: Montag, 22. Mai 2023 15:39:33 An: Rein, Patrick via Squeak-dev Cc: Taeumel, Marcel Betreff: [squeak-dev] Re: Review Request: base32.4.cs
Hi Christoph --
+1
Hmm... why does the diff show "nil subclass: ..." in the definitions?
Best, Marcel
Am 18.05.2023 20:37:52 schrieb christoph.thiede@student.hpi.uni-potsdam.de christoph.thiede@student.hpi.uni-potsdam.de:
=============== Summary ===============
Change Set: base32 Date: 18 May 2023 Author: Christoph Thiede
Adds support for base32 encoding and decoding. Adds Base32MimeConverter class and subsumes it with existing Base64MimeConverter class under new abstract RFC4648MimeConverter class. Adds #base32Encoded and #base32Decoded on String and String+ByteArray, resp. Adds convenient MimeConverter class>>#mimeDecode:. Tests new converter in new Base32MimeConverterTest and subsumes it with existing Base64MimeConverterTest under new abstract RFC4648MimeConverterTest.
=============== Diff ===============
Base32MimeConverter + nil subclass: #Base32MimeConverter + instanceVariableNames: '' + classVariableNames: 'FromCharTable ToCharTable' + poolDictionaries: '' + category: 'Collections-Streams' + + Base32MimeConverter class + instanceVariableNames: '' + + "I encode and decode data in base32 format. I translate a whole stream at once, taking a Stream as input and giving one as output, and answer a whole stream for the sender to use. 0 A 8 I 16 Q 24 Y 1 B 9 J 17 R 25 Z 2 C 10 K 18 S 26 2 3 D 11 L 19 T 27 3 4 E 12 M 20 U 28 4 5 F 13 N 21 V 29 5 6 G 14 O 22 W 30 6 7 H 15 P 23 X 31 7 (pad) = Outbound: bytes are broken into 5 bit chunks, and the 0-31 value is converted to a character. 5 data bytes go into 8 characters. Inbound: Characters are translated in to 0-31 values and shifted into 8 bit bytes."
Base32MimeConverter class>>initialize {class initialization} · ct 5/18/2023 16:07 + initialize + + FromCharTable := Array new: 256. "nils" + ToCharTable := ($A to: $Z) , ($2 to: $7) , '='. + ToCharTable keysAndValuesDo: [:ind :char | + FromCharTable at: char asciiValue + 1 put: ind - 1].
Base32MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:47 + fromCharTable + + ^ FromCharTable
Base32MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:49 + mimeDecode + "Convert the stream in base 32 with only A-Z,2-7 to a full byte stream of characters. Answer a whole stream for the user to read." + + | nibA nibB nibC nibD nibE nibF nibG nibH | + [mimeStream atEnd] whileFalse: [ + (nibA := self nextValue) ifNil: [^ dataStream]. + (nibB := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibA bitShift: 3) + (nibB bitShift: -2)) asCharacter. + nibB := nibB bitAnd: 16r3. + (nibC := self nextValue) ifNil: [^ dataStream]. + (nibD := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibB bitShift: 6) + (nibC bitShift: 1) + (nibD bitShift: -4)) asCharacter. + nibD := nibD bitAnd: 16rF. + (nibE := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibD bitShift: 4) + (nibE bitShift: -1)) asCharacter. + nibE := nibE bitAnd: 16r1. + (nibF := self nextValue) ifNil: [^ dataStream]. + (nibG := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibE bitShift: 7) + (nibF bitShift: 2) + (nibG bitShift: -3)) asCharacter. + nibG := nibG bitAnd: 16r7. + (nibH := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibG bitShift: 5) + nibH) asCharacter. + ]. + ^ dataStream
Base32MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:49 + mimeDecodeToByteArray + "Convert the stream in base 32 with only A-Z,2-7 to a full ByteArray of 0-255 values. Answer a whole stream for the user to read." + + | nibA nibB nibC nibD nibE nibF nibG nibH | + [mimeStream atEnd] whileFalse: [ + (nibA := self nextValue) ifNil: [^ dataStream]. + (nibB := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibA bitShift: 3) + (nibB bitShift: -2)). + nibB := nibB bitAnd: 16r3. + (nibC := self nextValue) ifNil: [^ dataStream]. + (nibD := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibB bitShift: 6) + (nibC bitShift: 1) + (nibD bitShift: -4)). + nibD := nibD bitAnd: 16rF. + (nibE := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibD bitShift: 4) + (nibE bitShift: -1)). + nibE := nibE bitAnd: 16r1. + (nibF := self nextValue) ifNil: [^ dataStream]. + (nibG := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibE bitShift: 7) + (nibF bitShift: 2) + (nibG bitShift: -3)). + nibG := nibG bitAnd: 16r7. + (nibH := self nextValue) ifNil: [^ dataStream]. + dataStream nextPut: ((nibG bitShift: 5) + nibH). + ]. + ^ dataStream
Base32MimeConverter>>mimeEncode {conversion} · ct 5/18/2023 19:12 + mimeEncode + "Convert from data to 5 bit characters." + + | padding raw nib lineLength | + padding := nil. + lineLength := 0. + [dataStream atEnd] whileFalse: [ + (multiLine and:[lineLength >= 70]) ifTrue: [ mimeStream cr. lineLength := 0. ]. + data := raw := dataStream next asInteger. + nib := (data bitAnd: 16rF8) bitShift: -3. "1111 1000" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding := -6]. + data := ((data bitAnd: 7) bitShift: 8) + raw asInteger. "0000 0111" + nib := (data bitAnd: 16r7C0) bitShift: -6. "0000 0111 1100 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + nib := (data bitAnd: 16r3E) bitShift: -1. "0011 1110" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -4]]. + data := ((data bitAnd: 1) bitShift: 8) + (raw asInteger). "0000 0001" + nib := (data bitAnd: 16r1F0) bitShift: -4. "0000 0001 1111 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -3]]. + data := ((data bitAnd: 16rF) bitShift: 8) + (raw asInteger). "0000 1111" + nib := (data bitAnd: 16rF80) bitShift: -7. "0000 1111 1000 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + nib := (data bitAnd: 16r7C) bitShift: -2. "0111 1100" + mimeStream nextPut: (ToCharTable at: nib+1). + (raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -1]]. + data := ((data bitAnd: 3) bitShift: 8) + (raw asInteger). "0000 0011" + nib := (data bitAnd: 16r3E0) bitShift: -5. "0000 0011 1110 0000" + mimeStream nextPut: (ToCharTable at: nib+1). + nib := (data bitAnd: 16r1F). "0001 1111" + mimeStream nextPut: (ToCharTable at: nib+1). + + lineLength := lineLength + 8.]. + + padding ifNotNil: [ + mimeStream skip: padding. + padding to: -1 do: [:i | mimeStream nextPut: $=]. + ^ mimeStream].
Base32MimeConverterTest + nil subclass: #Base32MimeConverterTest + instanceVariableNames: '' + classVariableNames: '' + poolDictionaries: '' + category: 'CollectionsTests-Streams' + + Base32MimeConverterTest class + instanceVariableNames: '' + + ""
Base32MimeConverterTest>>testBase32Encoded {tests} · ct 5/18/2023 20:08 + testBase32Encoded + + | encoded | + encoded := (self classToBeTested mimeEncode: message) contents. + self assert: string base32Encoded equals: encoded.
Base32MimeConverterTest>>testMimeEncodeDecode {tests} · ct 5/18/2023 20:10 + testMimeEncodeDecode + + | encoded | + encoded := self classToBeTested mimeEncode: message. + self assert: 'JBUSAVDIMVZGKII=' equals: encoded contents. + self assert: message contents equals: (self classToBeTested mimeDecodeToChars: encoded) contents. + + "Encoding should start from the beginning of the stream." + message reset. + message skip: 2. + encoded := self classToBeTested mimeEncode: message. + self assert: 'JBUSAVDIMVZGKII=' equals: encoded contents. + + "Encoding should start from the current position of the stream." + message reset. + message skip: 2. + encoded := self classToBeTested mimeEncodeContinue: message. + self assert: 'EBKGQZLSMUQQ====' equals: encoded contents.
Base32MimeConverterTest>>testMimeEncodeDecodeMultiLine {tests} · ct 5/18/2023 20:07 + testMimeEncodeDecodeMultiLine + + | encoded | + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream) contents. + self assert: encoded = + 'MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB + MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB + MFQWCYLBMFQWCYLB'. + + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents. + self assert: encoded = + 'MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB'.
Base32MimeConverterTest>>testOnByteArray {tests} · ct 5/18/2023 20:07 + testOnByteArray + + self assert: string base32Encoded = string asByteArray base32Encoded.
Base64MimeConverter (changed) - MimeConverter subclass: #Base64MimeConverter - instanceVariableNames: 'data multiLine' + nil subclass: #Base64MimeConverter + instanceVariableNames: '' classVariableNames: 'FromCharTable ToCharTable' poolDictionaries: '' category: 'Collections-Streams'
Base64MimeConverter class instanceVariableNames: ''
"This class encodes and decodes data in Base64 format. This is MIME encoding. We translate a whole stream at once, taking a Stream as input and giving one as output. Returns a whole stream for the caller to use. 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Outbound: bytes are broken into 6 bit chunks, and the 0-63 value is converted to a character. 3 data bytes go into 4 characters. Inbound: Characters are translated in to 0-63 values and shifted into 8 bit bytes.
(See: N. Borenstein, Bellcore, N. Freed, Innosoft, Network Working Group, Request for Comments: RFC 1521, September 1993, MIME (Multipurpose Internet Mail Extensions) Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies. Sec 6.2)
By Ted Kaehler, based on Tim Olson's Base64Filter."
Base64MimeConverter class>>decodeInteger: {convenience} · tk 2/19/2000 15:53 (removed) - decodeInteger: mimeString - | bytes sum | - "Decode the MIME string into an integer of any length" - - bytes := (Base64MimeConverter mimeDecodeToBytes: - (ReadStream on: mimeString)) contents. - sum := 0. - bytes reverseDo: [:by | sum := sum * 256 + by]. - ^ sum
Base64MimeConverter class>>encodeInteger: {convenience} · nice 7/26/2014 23:00 (removed) - encodeInteger: int - | strm | - "Encode an integer of any length and return the MIME string" - - strm := WriteStream on: (ByteArray new: int digitLength). - 1 to: int digitLength do: [:ii | strm nextPut: (int digitAt: ii)]. - ^ ((self mimeEncode: strm readStream) contents) copyUpTo: $= "remove padding"
Base64MimeConverter class>>mimeDecodeToBytes: {convenience} · nice 7/26/2014 23:03 (removed) - mimeDecodeToBytes: aStream - "Return a ReadStream of the original ByteArray. aStream has only 65 innocuous character values. aStream is not binary. (See class comment). 4 bytes in aStream goes to 3 bytes in output." - - | me | - aStream position: 0. - me := self new mimeStream: aStream. - me dataStream: (WriteStream on: (ByteArray new: aStream size * 3 // 4)). - me mimeDecodeToByteArray. - ^ me dataStream readStream
Base64MimeConverter class>>mimeDecodeToChars: {convenience} · nice 7/26/2014 23:01 (removed) - mimeDecodeToChars: aStream - "Return a ReadWriteStream of the original String. aStream has only 65 innocuous character values. It is not binary. (See class comment). 4 bytes in aStream goes to 3 bytes in output." - - | me | - aStream position: 0. - me := self new mimeStream: aStream. - me dataStream: (WriteStream on: (String new: aStream size * 3 // 4)). - me mimeDecode. - ^ me dataStream readStream
Base64MimeConverter class>>mimeEncode: {convenience} · ar 3/9/2010 22:17 (removed) - mimeEncode: aStream - "Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - ^self mimeEncode: aStream multiLine: true atStart: true
Base64MimeConverter class>>mimeEncode:multiLine: {convenience} · ar 3/9/2010 22:17 (removed) - mimeEncode: aStream multiLine: aBool - "Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - - ^self mimeEncode: aStream multiLine: aBool atStart: true
Base64MimeConverter class>>mimeEncode:multiLine:atStart: {private - convenience} · nice 7/26/2014 22:59 (removed) - mimeEncode: aStream multiLine: aBool atStart: resetInput - "Return a ReadStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - - | me | - resetInput ifTrue:[aStream position: 0]. - me := self new dataStream: aStream. - me multiLine: aBool. - me mimeStream: (WriteStream on: (String new: aStream size + 20 * 4 // 3)). - me mimeEncode. - ^ me mimeStream readStream
Base64MimeConverter class>>mimeEncodeContinue: {private - convenience} · ar 3/9/2010 22:17 (removed) - mimeEncodeContinue: aStream - "Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output." - ^self mimeEncode: aStream multiLine: true atStart: false
Base64MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:47 + fromCharTable + + ^ FromCharTable
Base64MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:49 (changed) mimeDecode - "Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Return a whole stream for the user to read." + "Convert the stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Answer a whole stream for the user to read."
| nibA nibB nibC nibD | [mimeStream atEnd] whileFalse: [ (nibA := self nextValue) ifNil: [^ dataStream]. (nibB := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)) asCharacter. nibB := nibB bitAnd: 16rF. (nibC := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)) asCharacter. nibC := nibC bitAnd: 16r3. (nibD := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibC bitShift: 6) + nibD) asCharacter. ]. ^ dataStream
Base64MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:50 (changed) mimeDecodeToByteArray - "Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Return a whole stream for the user to read." + "Convert the stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Answer a whole stream for the user to read."
| nibA nibB nibC nibD | [mimeStream atEnd] whileFalse: [ (nibA := self nextValue) ifNil: [^ dataStream]. (nibB := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)). nibB := nibB bitAnd: 16rF. (nibC := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)). nibC := nibC bitAnd: 16r3. (nibD := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibC bitShift: 6) + nibD). ]. ^ dataStream
Base64MimeConverterTest (changed) - TestCase subclass: #Base64MimeConverterTest - instanceVariableNames: 'message' + nil subclass: #Base64MimeConverterTest + instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'CollectionsTests-Streams'
Base64MimeConverterTest class instanceVariableNames: ''
"This is the unit test for the class Base64MimeConverter. Unit tests are a good way to exercise the functionality of your system in a repeatable and automatic manner. They are therefore recommended if you plan to release anything. For more information, see: - http://www.c2.com/cgi/wiki?UnitTest - http://minnow.cc.gatech.edu/squeak/1547 - the sunit class category"
Base64MimeConverterTest>>testBase64Encoded {tests} · ct 5/18/2023 20:09 (changed) testBase64Encoded + | encoded | - encoded := (Base64MimeConverter mimeEncode: message) contents. - self assert: encoded = 'Hi There!' base64Encoded. - + encoded := (self classToBeTested mimeEncode: message) contents. + self assert: string base64Encoded equals: encoded.
Base64MimeConverterTest>>testMimeEncodeDecode {tests} · ct 5/18/2023 20:10 (changed) testMimeEncodeDecode
| encoded | - encoded := Base64MimeConverter mimeEncode: message. - self assert: (encoded contents = 'SGkgVGhlcmUh'). - self assert: ((Base64MimeConverter mimeDecodeToChars: encoded) contents = message contents). - + encoded := self classToBeTested mimeEncode: message. + self assert: 'SGkgVGhlcmUh' equals: encoded contents. + self assert: message contents equals: (self classToBeTested mimeDecodeToChars: encoded) contents. + "Encoding should start from the beginning of the stream." message reset. message skip: 2. - encoded := Base64MimeConverter mimeEncode: message. - self assert: (encoded contents = 'SGkgVGhlcmUh'). + encoded := self classToBeTested mimeEncode: message. + self assert: 'SGkgVGhlcmUh' equals: encoded contents.
"Encoding should start from the current position of the stream." message reset. message skip: 2. - encoded := Base64MimeConverter mimeEncodeContinue: message. - self assert: (encoded contents = 'IFRoZXJlIQ=='). + encoded := self classToBeTested mimeEncodeContinue: message. + self assert: 'IFRoZXJlIQ==' equals: encoded contents.
Base64MimeConverterTest>>testMimeEncodeDecodeMultiLine {tests} · ct 5/18/2023 20:11 (changed) testMimeEncodeDecodeMultiLine
| encoded | - encoded := (Base64MimeConverter mimeEncode: (String new: 100 withAll: $a) readStream) contents. + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream) contents. self assert: encoded = 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='. - - encoded := (Base64MimeConverter mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents. + + encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents. self assert: encoded = - 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='. + 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='.
Base64MimeConverterTest>>testOnByteArray {tests} · ct 5/18/2023 20:11 (changed) testOnByteArray - self assert: ('Hi There!' base64Encoded = 'Hi There!' asByteArray base64Encoded) + + self assert: string base64Encoded = string asByteArray base64Encoded.
ByteArray>>base32Encoded {converting} · ct 5/18/2023 17:46 + base32Encoded + "Encode the receiver as base32" + "'Hello World' asByteArray base32Encoded" + ^(Base32MimeConverter mimeEncode: self readStream) contents
MimeConverter class>>mimeDecode: {convenience} · ct 5/18/2023 20:21 + mimeDecode: aCollectionOrStream + + ^ String streamContents: [:out | + self mimeDecode: aCollectionOrStream to: out]
RFC4648MimeConverter + MimeConverter subclass: #RFC4648MimeConverter + instanceVariableNames: 'data multiLine' + classVariableNames: '' + poolDictionaries: '' + category: 'Collections-Streams' + + RFC4648MimeConverter class + instanceVariableNames: '' + + "I am the abstract superclass for power-of-two-base mime converters according to RFC4648. We translate a whole stream at once, taking a Stream as input and giving one as output, and answer a whole stream for the sender to use."
RFC4648MimeConverter class>>decodeInteger: {convenience} · ct 5/18/2023 19:50 + decodeInteger: mimeString + "Decode the MIME string into an integer of any length" + + | bytes sum | + bytes := (self mimeDecodeToBytes: + (ReadStream on: mimeString)) contents. + sum := 0. + bytes reverseDo: [:by | sum := sum * 256 + by]. + ^ sum
RFC4648MimeConverter class>>encodeInteger: {convenience} · ct 5/18/2023 19:51 + encodeInteger: anInteger + "Encode anInteger of any length and return the MIME string" + + | stream | + stream := WriteStream on: (ByteArray new: anInteger digitLength). + 1 to: anInteger digitLength do: [:ii | stream nextPut: (anInteger digitAt: ii)]. + ^ ((self mimeEncode: stream readStream) contents) copyUpTo: $= "remove padding"
RFC4648MimeConverter class>>mimeDecodeToBytes: {convenience} · ct 5/18/2023 19:53 + mimeDecodeToBytes: aStream + "Return a ReadStream of the original ByteArray. aStream has only 33/65/... innocuous character values. aStream is not binary. (See class comment). 8/4/... bytes in aStream goes to 5/3/... bytes in output." + + | me | + aStream position: 0. + me := self new mimeStream: aStream. + me dataStream: (WriteStream on: (ByteArray new: aStream size * 3 // 4)). + me mimeDecodeToByteArray. + ^ me dataStream readStream
RFC4648MimeConverter class>>mimeDecodeToChars: {convenience} · ct 5/18/2023 19:54 + mimeDecodeToChars: aStream + "Return a ReadWriteStream of the original String. aStream has only 33/65/... innocuous character values. It is not binary. (See class comment). 8/4/... bytes in aStream goes to 5/3/... bytes in output." + + | me | + aStream position: 0. + me := self new mimeStream: aStream. + me dataStream: (WriteStream on: (String new: aStream size * 3 // 4)). + me mimeDecode. + ^ me dataStream readStream
RFC4648MimeConverter class>>mimeEncode: {convenience} · ct 5/18/2023 19:54 + mimeEncode: aStream + "Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + self flag: #suspicious. "ct: All other classes answer a String, just this one answers a ReadStream. Unfortunately, expectations of senders are inconsistent as well ..." + ^self mimeEncode: aStream multiLine: true atStart: true
RFC4648MimeConverter class>>mimeEncode:multiLine: {convenience} · ct 5/18/2023 19:55 + mimeEncode: aStream multiLine: aBool + "Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + + ^self mimeEncode: aStream multiLine: aBool atStart: true
RFC4648MimeConverter class>>mimeEncode:multiLine:atStart: {private - convenience} · ct 5/18/2023 19:55 + mimeEncode: aStream multiLine: aBool atStart: resetInput + "Return a ReadStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + + | me | + resetInput ifTrue:[aStream position: 0]. + me := self new dataStream: aStream. + me multiLine: aBool. + me mimeStream: (WriteStream on: (String new: aStream size + 20 * 4 // 3)). + me mimeEncode. + ^ me mimeStream readStream
RFC4648MimeConverter class>>mimeEncodeContinue: {private - convenience} · ct 5/18/2023 19:55 + mimeEncodeContinue: aStream + "Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output." + ^self mimeEncode: aStream multiLine: true atStart: false
RFC4648MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:46 + fromCharTable + + ^ self subclassResponsibility
RFC4648MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:48 + mimeDecode + "Convert the stream in the respective base to a full byte stream of characters. Answer a whole stream for the user to read." + + ^ self subclassResponsibility
RFC4648MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:49 + mimeDecodeToByteArray + "Convert the stream in the respective base to a full ByteArray of 0-255 values. Answer a whole stream for the user to read." + + ^ self subclassResponsibility
RFC4648MimeConverter>>mimeEncode {conversion} · ct 5/18/2023 19:45 + mimeEncode + "Convert from data to n bit characters." + + ^ self subclassResponsibility
RFC4648MimeConverter>>multiLine {accessing} · ar 4/15/2008 17:58 + multiLine + "Determines whether we allow multi-line encodings (the default) or force everything into a single line (for use with URLs etc. where the continuation marker and the line break cause problems)" + ^multiLine
RFC4648MimeConverter>>multiLine: {accessing} · ar 4/15/2008 17:58 + multiLine: aBool + "Determines whether we allow multi-line encodings (the default) or force everything into a single line (for use with URLs etc. where the continuation marker and the line break cause problems)" + multiLine := aBool
RFC4648MimeConverter>>nextValue {conversion} · ct 5/18/2023 19:46 + nextValue + "The next n bits of data char from the mimeStream, or nil. Skip all other chars" + | raw num | + [raw := mimeStream next. + raw ifNil: [^ nil]. "end of stream" + raw == $= ifTrue: [^ nil]. + num := self fromCharTable at: raw asciiValue + 1. + num ifNotNil: [^ num]. + "else ignore space, return, tab, ..." + ] repeat
RFC4648MimeConverterTest + ClassTestCase subclass: #RFC4648MimeConverterTest + instanceVariableNames: 'string message' + classVariableNames: '' + poolDictionaries: '' + category: 'CollectionsTests-Streams' + + RFC4648MimeConverterTest class + instanceVariableNames: '' + + ""
RFC4648MimeConverterTest class>>isAbstract {testing} · ct 5/18/2023 20:08 + isAbstract + + ^ self name = #RFC4648MimeConverterTest
RFC4648MimeConverterTest>>setUp {running} · ct 5/18/2023 20:04 + setUp + string := 'Hi There!'. + message := string readStream.
String>>base32Decoded {converting} · ct 5/18/2023 19:38 + base32Decoded + "Decode the receiver from base 32" + "'JBSWY3DPEBLW64TMMQ======' base32Decoded" + ^ Base32MimeConverter mimeDecode: self as: self class
String>>base32Encoded {converting} · ct 5/18/2023 19:15 + base32Encoded + "Encode the receiver as base32" + "'Hello World' base32Encoded" + + ^(Base32MimeConverter + mimeEncode: (ReadStream on: self) + multiLine: false) contents
StringTest>>testBase32 {tests - converting} · ct 5/18/2023 20:15 + testBase32 + + self + assert: 'JBSWY3DPEBLW64TMMQ======' base32Decoded = 'Hello World'; + assert: 'Hello World' base32Encoded = 'JBSWY3DPEBLW64TMMQ======'; + assert: (String new: 100 withAll: $x) base32Encoded = 'PB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DY'.
--- Sent from Squeak Inbox Talkhttps://github.com/hpi-swa-lab/squeak-inbox-talk ["base32.4.cs"]
Merged via Collections-ct.1041/CollectionsTests-ct.396.
Best, Christoph
--- Sent from Squeak Inbox Talk
On 2023-05-22T14:33:00+00:00, christoph.thiede@student.hpi.uni-potsdam.de wrote:
Hi Marcel,
Hmm... why does the diff show "nil subclass: ..." in the definitions?
Sorry for that. It's my own implementation of generating nice diff texts for changesets. It's yet prototypical, and apparently, it cannot handle superclasses that are not loaded in the image. I will also have to fix some other quirks such as support for removals and postscript before I contribute that to the Trunk. :-)
Best, Christoph
Von: Marcel Taeumel via Squeak-dev <squeak-dev(a)lists.squeakfoundation.org> Gesendet: Montag, 22. Mai 2023 15:39:33 An: Rein, Patrick via Squeak-dev Cc: Taeumel, Marcel Betreff: [squeak-dev] Re: Review Request: base32.4.cs
Hi Christoph --
+1
Hmm... why does the diff show "nil subclass: ..." in the definitions?
Best, Marcel
Am 18.05.2023 20:37:52 schrieb christoph.thiede(a)student.hpi.uni-potsdam.de <christoph.thiede(a)student.hpi.uni-potsdam.de>:
=============== Summary ===============
Change Set: base32 Date: 18 May 2023 Author: Christoph Thiede
Adds support for base32 encoding and decoding. Adds Base32MimeConverter class and subsumes it with existing Base64MimeConverter class under new abstract RFC4648MimeConverter class. Adds #base32Encoded and #base32Decoded on String and String+ByteArray, resp. Adds convenient MimeConverter class>>#mimeDecode:. Tests new converter in new Base32MimeConverterTest and subsumes it with existing Base64MimeConverterTest under new abstract RFC4648MimeConverterTest.
=============== Diff ===============
Base32MimeConverter
- nil subclass: #Base32MimeConverter
instanceVariableNames: ''
classVariableNames: 'FromCharTable ToCharTable'
poolDictionaries: ''
category: 'Collections-Streams'
- Base32MimeConverter class
instanceVariableNames: ''
- "I encode and decode data in base32 format. I translate a whole stream at once, taking a Stream as input and giving one as output, and answer a whole stream for the sender to use. 0 A 8 I 16 Q 24 Y 1 B 9 J 17 R 25 Z 2 C 10 K 18 S 26 2 3 D 11 L 19 T 27 3 4 E 12 M 20 U 28 4 5 F 13 N 21 V 29 5 6 G 14 O 22 W 30 6 7 H 15 P 23 X 31 7 (pad) =
Outbound: bytes are broken into 5 bit chunks, and the 0-31 value is converted to a character. 5 data bytes go into 8 characters. Inbound: Characters are translated in to 0-31 values and shifted into 8 bit bytes."
Base32MimeConverter class>>initialize {class initialization} · ct 5/18/2023 16:07
- initialize
FromCharTable := Array new: 256. "nils"
ToCharTable := ($A to: $Z) , ($2 to: $7) , '='.
ToCharTable keysAndValuesDo: [:ind :char |
FromCharTable at: char asciiValue + 1 put: ind - 1].
Base32MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:47
- fromCharTable
^ FromCharTable
Base32MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:49
- mimeDecode
"Convert the stream in base 32 with only A-Z,2-7 to a full byte stream of characters. Answer a whole stream for the user to read."
| nibA nibB nibC nibD nibE nibF nibG nibH |
[mimeStream atEnd] whileFalse: [
(nibA := self nextValue) ifNil: [^ dataStream].
(nibB := self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibA bitShift: 3) + (nibB bitShift: -2)) asCharacter.
nibB := nibB bitAnd: 16r3.
(nibC := self nextValue) ifNil: [^ dataStream].
(nibD := self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibB bitShift: 6) + (nibC bitShift: 1) + (nibD bitShift: -4)) asCharacter.
nibD := nibD bitAnd: 16rF.
(nibE := self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibD bitShift: 4) + (nibE bitShift: -1)) asCharacter.
nibE := nibE bitAnd: 16r1.
(nibF := self nextValue) ifNil: [^ dataStream].
(nibG := self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibE bitShift: 7) + (nibF bitShift: 2) + (nibG bitShift: -3)) asCharacter.
nibG := nibG bitAnd: 16r7.
(nibH := self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibG bitShift: 5) + nibH) asCharacter.
].
^ dataStream
Base32MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:49
- mimeDecodeToByteArray
"Convert the stream in base 32 with only A-Z,2-7 to a full ByteArray of 0-255 values. Answer a whole stream for the user to read."
| nibA nibB nibC nibD nibE nibF nibG nibH |
[mimeStream atEnd] whileFalse: [
(nibA := self nextValue) ifNil: [^ dataStream].
(nibB := self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibA bitShift: 3) + (nibB bitShift: -2)).
nibB := nibB bitAnd: 16r3.
(nibC := self nextValue) ifNil: [^ dataStream].
(nibD := self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibB bitShift: 6) + (nibC bitShift: 1) + (nibD bitShift: -4)).
nibD := nibD bitAnd: 16rF.
(nibE := self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibD bitShift: 4) + (nibE bitShift: -1)).
nibE := nibE bitAnd: 16r1.
(nibF := self nextValue) ifNil: [^ dataStream].
(nibG := self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibE bitShift: 7) + (nibF bitShift: 2) + (nibG bitShift: -3)).
nibG := nibG bitAnd: 16r7.
(nibH := self nextValue) ifNil: [^ dataStream].
dataStream nextPut: ((nibG bitShift: 5) + nibH).
].
^ dataStream
Base32MimeConverter>>mimeEncode {conversion} · ct 5/18/2023 19:12
- mimeEncode
"Convert from data to 5 bit characters."
| padding raw nib lineLength |
padding := nil.
lineLength := 0.
[dataStream atEnd] whileFalse: [
(multiLine and:[lineLength >= 70]) ifTrue: [ mimeStream cr. lineLength := 0. ].
data := raw := dataStream next asInteger.
nib := (data bitAnd: 16rF8) bitShift: -3. "1111 1000"
mimeStream nextPut: (ToCharTable at: nib+1).
(raw := dataStream next) ifNil: [raw := 0. padding := -6].
data := ((data bitAnd: 7) bitShift: 8) + raw asInteger. "0000 0111"
nib := (data bitAnd: 16r7C0) bitShift: -6. "0000 0111 1100 0000"
mimeStream nextPut: (ToCharTable at: nib+1).
nib := (data bitAnd: 16r3E) bitShift: -1. "0011 1110"
mimeStream nextPut: (ToCharTable at: nib+1).
(raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -4]].
data := ((data bitAnd: 1) bitShift: 8) + (raw asInteger). "0000 0001"
nib := (data bitAnd: 16r1F0) bitShift: -4. "0000 0001 1111 0000"
mimeStream nextPut: (ToCharTable at: nib+1).
(raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -3]].
data := ((data bitAnd: 16rF) bitShift: 8) + (raw asInteger). "0000 1111"
nib := (data bitAnd: 16rF80) bitShift: -7. "0000 1111 1000 0000"
mimeStream nextPut: (ToCharTable at: nib+1).
nib := (data bitAnd: 16r7C) bitShift: -2. "0111 1100"
mimeStream nextPut: (ToCharTable at: nib+1).
(raw := dataStream next) ifNil: [raw := 0. padding ifNil: [padding := -1]].
data := ((data bitAnd: 3) bitShift: 8) + (raw asInteger). "0000 0011"
nib := (data bitAnd: 16r3E0) bitShift: -5. "0000 0011 1110 0000"
mimeStream nextPut: (ToCharTable at: nib+1).
nib := (data bitAnd: 16r1F). "0001 1111"
mimeStream nextPut: (ToCharTable at: nib+1).
lineLength := lineLength + 8.].
padding ifNotNil: [
mimeStream skip: padding.
padding to: -1 do: [:i | mimeStream nextPut: $=].
^ mimeStream].
Base32MimeConverterTest
- nil subclass: #Base32MimeConverterTest
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CollectionsTests-Streams'
- Base32MimeConverterTest class
instanceVariableNames: ''
- ""
Base32MimeConverterTest>>testBase32Encoded {tests} · ct 5/18/2023 20:08
- testBase32Encoded
| encoded |
encoded := (self classToBeTested mimeEncode: message) contents.
self assert: string base32Encoded equals: encoded.
Base32MimeConverterTest>>testMimeEncodeDecode {tests} · ct 5/18/2023 20:10
- testMimeEncodeDecode
| encoded |
encoded := self classToBeTested mimeEncode: message.
self assert: 'JBUSAVDIMVZGKII=' equals: encoded contents.
self assert: message contents equals: (self classToBeTested mimeDecodeToChars: encoded) contents.
"Encoding should start from the beginning of the stream."
message reset.
message skip: 2.
encoded := self classToBeTested mimeEncode: message.
self assert: 'JBUSAVDIMVZGKII=' equals: encoded contents.
"Encoding should start from the current position of the stream."
message reset.
message skip: 2.
encoded := self classToBeTested mimeEncodeContinue: message.
self assert: 'EBKGQZLSMUQQ====' equals: encoded contents.
Base32MimeConverterTest>>testMimeEncodeDecodeMultiLine {tests} · ct 5/18/2023 20:07
- testMimeEncodeDecodeMultiLine
| encoded |
encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream) contents.
self assert: encoded =
- 'MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB
- MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB
- MFQWCYLBMFQWCYLB'.
encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents.
self assert: encoded =
- 'MFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLBMFQWCYLB'.
Base32MimeConverterTest>>testOnByteArray {tests} · ct 5/18/2023 20:07
- testOnByteArray
self assert: string base32Encoded = string asByteArray base32Encoded.
Base64MimeConverter (changed)
- MimeConverter subclass: #Base64MimeConverter
instanceVariableNames: 'data multiLine'
- nil subclass: #Base64MimeConverter
classVariableNames: 'FromCharTable ToCharTable' poolDictionaries: '' category: 'Collections-Streams'instanceVariableNames: ''
Base64MimeConverter class instanceVariableNames: ''
"This class encodes and decodes data in Base64 format. This is MIME encoding. We translate a whole stream at once, taking a Stream as input and giving one as output. Returns a whole stream for the caller to use. 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Outbound: bytes are broken into 6 bit chunks, and the 0-63 value is converted to a character. 3 data bytes go into 4 characters. Inbound: Characters are translated in to 0-63 values and shifted into 8 bit bytes.
(See: N. Borenstein, Bellcore, N. Freed, Innosoft, Network Working Group, Request for Comments: RFC 1521, September 1993, MIME (Multipurpose Internet Mail Extensions) Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies. Sec 6.2)
By Ted Kaehler, based on Tim Olson's Base64Filter."
Base64MimeConverter class>>decodeInteger: {convenience} · tk 2/19/2000 15:53 (removed)
- decodeInteger: mimeString
| bytes sum |
"Decode the MIME string into an integer of any length"
bytes := (Base64MimeConverter mimeDecodeToBytes:
(ReadStream on: mimeString)) contents.
sum := 0.
bytes reverseDo: [:by | sum := sum * 256 + by].
^ sum
Base64MimeConverter class>>encodeInteger: {convenience} · nice 7/26/2014 23:00 (removed)
- encodeInteger: int
| strm |
"Encode an integer of any length and return the MIME string"
strm := WriteStream on: (ByteArray new: int digitLength).
1 to: int digitLength do: [:ii | strm nextPut: (int digitAt: ii)].
^ ((self mimeEncode: strm readStream) contents) copyUpTo: $= "remove padding"
Base64MimeConverter class>>mimeDecodeToBytes: {convenience} · nice 7/26/2014 23:03 (removed)
- mimeDecodeToBytes: aStream
"Return a ReadStream of the original ByteArray. aStream has only 65 innocuous character values. aStream is not binary. (See class comment). 4 bytes in aStream goes to 3 bytes in output."
| me |
aStream position: 0.
me := self new mimeStream: aStream.
me dataStream: (WriteStream on: (ByteArray new: aStream size * 3 // 4)).
me mimeDecodeToByteArray.
^ me dataStream readStream
Base64MimeConverter class>>mimeDecodeToChars: {convenience} · nice 7/26/2014 23:01 (removed)
- mimeDecodeToChars: aStream
"Return a ReadWriteStream of the original String. aStream has only 65 innocuous character values. It is not binary. (See class comment). 4 bytes in aStream goes to 3 bytes in output."
| me |
aStream position: 0.
me := self new mimeStream: aStream.
me dataStream: (WriteStream on: (String new: aStream size * 3 // 4)).
me mimeDecode.
^ me dataStream readStream
Base64MimeConverter class>>mimeEncode: {convenience} · ar 3/9/2010 22:17 (removed)
- mimeEncode: aStream
"Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output."
^self mimeEncode: aStream multiLine: true atStart: true
Base64MimeConverter class>>mimeEncode:multiLine: {convenience} · ar 3/9/2010 22:17 (removed)
- mimeEncode: aStream multiLine: aBool
"Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output."
^self mimeEncode: aStream multiLine: aBool atStart: true
Base64MimeConverter class>>mimeEncode:multiLine:atStart: {private - convenience} · nice 7/26/2014 22:59 (removed)
- mimeEncode: aStream multiLine: aBool atStart: resetInput
"Return a ReadStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output."
| me |
resetInput ifTrue:[aStream position: 0].
me := self new dataStream: aStream.
me multiLine: aBool.
me mimeStream: (WriteStream on: (String new: aStream size + 20 * 4 // 3)).
me mimeEncode.
^ me mimeStream readStream
Base64MimeConverter class>>mimeEncodeContinue: {private - convenience} · ar 3/9/2010 22:17 (removed)
- mimeEncodeContinue: aStream
"Return a ReadWriteStream of characters. The data of aStream is encoded as 65 innocuous characters. (See class comment). 3 bytes in aStream goes to 4 bytes in output."
^self mimeEncode: aStream multiLine: true atStart: false
Base64MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:47
- fromCharTable
^ FromCharTable
Base64MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:49 (changed) mimeDecode
"Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Return a whole stream for the user to read."
"Convert the stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Answer a whole stream for the user to read."
| nibA nibB nibC nibD | [mimeStream atEnd] whileFalse: [ (nibA := self nextValue) ifNil: [^ dataStream]. (nibB := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)) asCharacter. nibB := nibB bitAnd: 16rF. (nibC := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)) asCharacter. nibC := nibC bitAnd: 16r3. (nibD := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibC bitShift: 6) + nibD) asCharacter. ]. ^ dataStream
Base64MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:50 (changed) mimeDecodeToByteArray
"Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Return a whole stream for the user to read."
"Convert the stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Answer a whole stream for the user to read."
| nibA nibB nibC nibD | [mimeStream atEnd] whileFalse: [ (nibA := self nextValue) ifNil: [^ dataStream]. (nibB := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibA bitShift: 2) + (nibB bitShift: -4)). nibB := nibB bitAnd: 16rF. (nibC := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)). nibC := nibC bitAnd: 16r3. (nibD := self nextValue) ifNil: [^ dataStream]. dataStream nextPut: ((nibC bitShift: 6) + nibD). ]. ^ dataStream
Base64MimeConverterTest (changed)
- TestCase subclass: #Base64MimeConverterTest
instanceVariableNames: 'message'
- nil subclass: #Base64MimeConverterTest
classVariableNames: '' poolDictionaries: '' category: 'CollectionsTests-Streams'instanceVariableNames: ''
Base64MimeConverterTest class instanceVariableNames: ''
"This is the unit test for the class Base64MimeConverter. Unit tests are a good way to exercise the functionality of your system in a repeatable and automatic manner. They are therefore recommended if you plan to release anything. For more information, see: - http://www.c2.com/cgi/wiki?UnitTest - http://minnow.cc.gatech.edu/squeak/1547 - the sunit class category"
Base64MimeConverterTest>>testBase64Encoded {tests} · ct 5/18/2023 20:09 (changed) testBase64Encoded
- | encoded |
encoded := (Base64MimeConverter mimeEncode: message) contents.
self assert: encoded = 'Hi There!' base64Encoded.
encoded := (self classToBeTested mimeEncode: message) contents.
self assert: string base64Encoded equals: encoded.
Base64MimeConverterTest>>testMimeEncodeDecode {tests} · ct 5/18/2023 20:10 (changed) testMimeEncodeDecode
| encoded |
encoded := Base64MimeConverter mimeEncode: message.
self assert: (encoded contents = 'SGkgVGhlcmUh').
- self assert: ((Base64MimeConverter mimeDecodeToChars: encoded) contents = message contents).
encoded := self classToBeTested mimeEncode: message.
self assert: 'SGkgVGhlcmUh' equals: encoded contents.
self assert: message contents equals: (self classToBeTested mimeDecodeToChars: encoded) contents.
- "Encoding should start from the beginning of the stream." message reset. message skip: 2.
encoded := Base64MimeConverter mimeEncode: message.
self assert: (encoded contents = 'SGkgVGhlcmUh').
encoded := self classToBeTested mimeEncode: message.
self assert: 'SGkgVGhlcmUh' equals: encoded contents.
"Encoding should start from the current position of the stream." message reset. message skip: 2.
encoded := Base64MimeConverter mimeEncodeContinue: message.
self assert: (encoded contents = 'IFRoZXJlIQ==').
encoded := self classToBeTested mimeEncodeContinue: message.
self assert: 'IFRoZXJlIQ==' equals: encoded contents.
Base64MimeConverterTest>>testMimeEncodeDecodeMultiLine {tests} · ct 5/18/2023 20:11 (changed) testMimeEncodeDecodeMultiLine
| encoded |
encoded := (Base64MimeConverter mimeEncode: (String new: 100 withAll: $a) readStream) contents.
self assert: encoded =encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream) contents.
'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='.
encoded := (Base64MimeConverter mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents.
self assert: encoded =encoded := (self classToBeTested mimeEncode: (String new: 100 withAll: $a) readStream multiLine: false) contents.
- 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='.
- 'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=='.
Base64MimeConverterTest>>testOnByteArray {tests} · ct 5/18/2023 20:11 (changed) testOnByteArray
self assert: ('Hi There!' base64Encoded = 'Hi There!' asByteArray base64Encoded)
self assert: string base64Encoded = string asByteArray base64Encoded.
ByteArray>>base32Encoded {converting} · ct 5/18/2023 17:46
- base32Encoded
"Encode the receiver as base32"
"'Hello World' asByteArray base32Encoded"
^(Base32MimeConverter mimeEncode: self readStream) contents
MimeConverter class>>mimeDecode: {convenience} · ct 5/18/2023 20:21
- mimeDecode: aCollectionOrStream
^ String streamContents: [:out |
self mimeDecode: aCollectionOrStream to: out]
RFC4648MimeConverter
- MimeConverter subclass: #RFC4648MimeConverter
instanceVariableNames: 'data multiLine'
classVariableNames: ''
poolDictionaries: ''
category: 'Collections-Streams'
- RFC4648MimeConverter class
instanceVariableNames: ''
- "I am the abstract superclass for power-of-two-base mime converters according to RFC4648. We translate a whole stream at once, taking a Stream as input and giving one as output, and answer a whole stream for the sender to use."
RFC4648MimeConverter class>>decodeInteger: {convenience} · ct 5/18/2023 19:50
- decodeInteger: mimeString
"Decode the MIME string into an integer of any length"
| bytes sum |
bytes := (self mimeDecodeToBytes:
(ReadStream on: mimeString)) contents.
sum := 0.
bytes reverseDo: [:by | sum := sum * 256 + by].
^ sum
RFC4648MimeConverter class>>encodeInteger: {convenience} · ct 5/18/2023 19:51
- encodeInteger: anInteger
"Encode anInteger of any length and return the MIME string"
| stream |
stream := WriteStream on: (ByteArray new: anInteger digitLength).
1 to: anInteger digitLength do: [:ii | stream nextPut: (anInteger digitAt: ii)].
^ ((self mimeEncode: stream readStream) contents) copyUpTo: $= "remove padding"
RFC4648MimeConverter class>>mimeDecodeToBytes: {convenience} · ct 5/18/2023 19:53
- mimeDecodeToBytes: aStream
"Return a ReadStream of the original ByteArray. aStream has only 33/65/... innocuous character values. aStream is not binary. (See class comment). 8/4/... bytes in aStream goes to 5/3/... bytes in output."
| me |
aStream position: 0.
me := self new mimeStream: aStream.
me dataStream: (WriteStream on: (ByteArray new: aStream size * 3 // 4)).
me mimeDecodeToByteArray.
^ me dataStream readStream
RFC4648MimeConverter class>>mimeDecodeToChars: {convenience} · ct 5/18/2023 19:54
- mimeDecodeToChars: aStream
"Return a ReadWriteStream of the original String. aStream has only 33/65/... innocuous character values. It is not binary. (See class comment). 8/4/... bytes in aStream goes to 5/3/... bytes in output."
| me |
aStream position: 0.
me := self new mimeStream: aStream.
me dataStream: (WriteStream on: (String new: aStream size * 3 // 4)).
me mimeDecode.
^ me dataStream readStream
RFC4648MimeConverter class>>mimeEncode: {convenience} · ct 5/18/2023 19:54
- mimeEncode: aStream
"Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output."
self flag: #suspicious. "ct: All other classes answer a String, just this one answers a ReadStream. Unfortunately, expectations of senders are inconsistent as well ..."
^self mimeEncode: aStream multiLine: true atStart: true
RFC4648MimeConverter class>>mimeEncode:multiLine: {convenience} · ct 5/18/2023 19:55
- mimeEncode: aStream multiLine: aBool
"Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output."
^self mimeEncode: aStream multiLine: aBool atStart: true
RFC4648MimeConverter class>>mimeEncode:multiLine:atStart: {private - convenience} · ct 5/18/2023 19:55
- mimeEncode: aStream multiLine: aBool atStart: resetInput
"Return a ReadStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output."
| me |
resetInput ifTrue:[aStream position: 0].
me := self new dataStream: aStream.
me multiLine: aBool.
me mimeStream: (WriteStream on: (String new: aStream size + 20 * 4 // 3)).
me mimeEncode.
^ me mimeStream readStream
RFC4648MimeConverter class>>mimeEncodeContinue: {private - convenience} · ct 5/18/2023 19:55
- mimeEncodeContinue: aStream
"Return a ReadWriteStream of characters. The data of aStream is encoded as 33/65/... innocuous characters. (See class comment). 5/3/... bytes in aStream goes to 8/4/... bytes in output."
^self mimeEncode: aStream multiLine: true atStart: false
RFC4648MimeConverter>>fromCharTable {private} · ct 5/18/2023 19:46
- fromCharTable
^ self subclassResponsibility
RFC4648MimeConverter>>mimeDecode {conversion} · ct 5/18/2023 19:48
- mimeDecode
"Convert the stream in the respective base to a full byte stream of characters. Answer a whole stream for the user to read."
^ self subclassResponsibility
RFC4648MimeConverter>>mimeDecodeToByteArray {conversion} · ct 5/18/2023 19:49
- mimeDecodeToByteArray
"Convert the stream in the respective base to a full ByteArray of 0-255 values. Answer a whole stream for the user to read."
^ self subclassResponsibility
RFC4648MimeConverter>>mimeEncode {conversion} · ct 5/18/2023 19:45
- mimeEncode
"Convert from data to n bit characters."
^ self subclassResponsibility
RFC4648MimeConverter>>multiLine {accessing} · ar 4/15/2008 17:58
- multiLine
"Determines whether we allow multi-line encodings (the default) or force everything into a single line (for use with URLs etc. where the continuation marker and the line break cause problems)"
^multiLine
RFC4648MimeConverter>>multiLine: {accessing} · ar 4/15/2008 17:58
- multiLine: aBool
"Determines whether we allow multi-line encodings (the default) or force everything into a single line (for use with URLs etc. where the continuation marker and the line break cause problems)"
multiLine := aBool
RFC4648MimeConverter>>nextValue {conversion} · ct 5/18/2023 19:46
- nextValue
"The next n bits of data char from the mimeStream, or nil. Skip all other chars"
| raw num |
[raw := mimeStream next.
raw ifNil: [^ nil]. "end of stream"
raw == $= ifTrue: [^ nil].
num := self fromCharTable at: raw asciiValue + 1.
num ifNotNil: [^ num].
"else ignore space, return, tab, ..."
] repeat
RFC4648MimeConverterTest
- ClassTestCase subclass: #RFC4648MimeConverterTest
instanceVariableNames: 'string message'
classVariableNames: ''
poolDictionaries: ''
category: 'CollectionsTests-Streams'
- RFC4648MimeConverterTest class
instanceVariableNames: ''
- ""
RFC4648MimeConverterTest class>>isAbstract {testing} · ct 5/18/2023 20:08
- isAbstract
^ self name = #RFC4648MimeConverterTest
RFC4648MimeConverterTest>>setUp {running} · ct 5/18/2023 20:04
- setUp
string := 'Hi There!'.
message := string readStream.
String>>base32Decoded {converting} · ct 5/18/2023 19:38
- base32Decoded
"Decode the receiver from base 32"
"'JBSWY3DPEBLW64TMMQ======' base32Decoded"
^ Base32MimeConverter mimeDecode: self as: self class
String>>base32Encoded {converting} · ct 5/18/2023 19:15
- base32Encoded
"Encode the receiver as base32"
"'Hello World' base32Encoded"
^(Base32MimeConverter
mimeEncode: (ReadStream on: self)
multiLine: false) contents
StringTest>>testBase32 {tests - converting} · ct 5/18/2023 20:15
- testBase32
self
assert: 'JBSWY3DPEBLW64TMMQ======' base32Decoded = 'Hello World';
assert: 'Hello World' base32Encoded = 'JBSWY3DPEBLW64TMMQ======';
assert: (String new: 100 withAll: $x) base32Encoded = 'PB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DYPB4HQ6DY'.
Sent from Squeak Inbox Talkhttps://github.com/hpi-swa-lab/squeak-inbox-talk ["base32.4.cs"]
squeak-dev@lists.squeakfoundation.org