base64: Import Pharo's copy of the Base64MimeConverter
Import the functions we are needing from Pharo.
This commit is contained in:
parent
30a301f246
commit
03fb87b322
|
@ -0,0 +1,225 @@
|
|||
Eval [
|
||||
'From Pharo-1.1-11411 of 17 July 2010 [Latest update: #11411] on 19 February 2011 at 9:09:05 pm'
|
||||
]
|
||||
|
||||
ReadWriteStream subclass: MIMERWStream [
|
||||
| readLimit |
|
||||
|
||||
nextPut: aInt [
|
||||
| res |
|
||||
res := super nextPut: aInt.
|
||||
readLimit := self readLimit max: self position.
|
||||
^ res
|
||||
]
|
||||
|
||||
contents [
|
||||
^ collection copyFrom: 1 to: self readLimit.
|
||||
]
|
||||
|
||||
readLimit [
|
||||
^ readLimit ifNil: [readLimit := 0].
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
Object subclass: MimeConverter [
|
||||
| dataStream mimeStream |
|
||||
|
||||
<category: 'Network-MIME'>
|
||||
|
||||
dataStream [
|
||||
<category: 'accessing'>
|
||||
^dataStream
|
||||
]
|
||||
|
||||
dataStream: anObject [
|
||||
<category: 'accessing'>
|
||||
dataStream := anObject
|
||||
]
|
||||
|
||||
mimeStream [
|
||||
<category: 'accessing'>
|
||||
^mimeStream
|
||||
]
|
||||
|
||||
mimeStream: anObject [
|
||||
<category: 'accessing'>
|
||||
mimeStream := anObject
|
||||
]
|
||||
|
||||
mimeDecode [
|
||||
"Do conversion reading from mimeStream writing to dataStream"
|
||||
|
||||
<category: 'conversion'>
|
||||
self subclassResponsibility
|
||||
]
|
||||
|
||||
mimeEncode [
|
||||
"Do conversion reading from dataStream writing to mimeStream"
|
||||
|
||||
<category: 'conversion'>
|
||||
self subclassResponsibility
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
|
||||
MimeConverter subclass: Base64MimeConverter [
|
||||
| data |
|
||||
|
||||
<comment: '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.'>
|
||||
<category: 'Network-MIME'>
|
||||
|
||||
FromCharTable := nil.
|
||||
ToCharTable := nil.
|
||||
|
||||
Base64MimeConverter class >> initialize [
|
||||
FromCharTable := Array new: 256. "nils"
|
||||
ToCharTable := Array new: 64.
|
||||
($A asciiValue to: $Z asciiValue) doWithIndex:
|
||||
[:val :ind |
|
||||
FromCharTable at: val + 1 put: ind - 1.
|
||||
ToCharTable at: ind put: val asCharacter].
|
||||
($a asciiValue to: $z asciiValue) doWithIndex:
|
||||
[:val :ind |
|
||||
FromCharTable at: val + 1 put: ind + 25.
|
||||
ToCharTable at: ind + 26 put: val asCharacter].
|
||||
($0 asciiValue to: $9 asciiValue) doWithIndex:
|
||||
[:val :ind |
|
||||
FromCharTable at: val + 1 put: ind + 25 + 26.
|
||||
ToCharTable at: ind + 26 + 26 put: val asCharacter].
|
||||
FromCharTable at: $+ asciiValue + 1 put: 62.
|
||||
ToCharTable at: 63 put: $+.
|
||||
FromCharTable at: $/ asciiValue + 1 put: 63.
|
||||
ToCharTable at: 64 put: $/
|
||||
]
|
||||
|
||||
Base64MimeConverter class >> mimeDecodeToBytes: aStream [
|
||||
"Return a RWBinaryOrTextStream 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: (MIMERWStream
|
||||
on: (ByteArray new: aStream size * 3 // 4)).
|
||||
me mimeDecodeToByteArray.
|
||||
me dataStream position: 0.
|
||||
^me dataStream
|
||||
]
|
||||
|
||||
Base64MimeConverter class >> 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."
|
||||
|
||||
| me |
|
||||
me := self new dataStream: aStream.
|
||||
me
|
||||
mimeStream: (MIMERWStream on: (String new: (aStream size + 20) * 4 // 3)).
|
||||
me mimeEncode.
|
||||
me mimeStream position: 0.
|
||||
^me mimeStream
|
||||
]
|
||||
|
||||
mimeDecode [
|
||||
"Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full byte stream of characters. Reutrn a whole stream for the user to read."
|
||||
|
||||
<category: 'conversion'>
|
||||
| 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: 15.
|
||||
(nibC := self nextValue) ifNil: [^dataStream].
|
||||
dataStream nextPut: ((nibB bitShift: 4) + (nibC bitShift: -2)) asCharacter.
|
||||
nibC := nibC bitAnd: 3.
|
||||
(nibD := self nextValue) ifNil: [^dataStream].
|
||||
dataStream nextPut: ((nibC bitShift: 6) + nibD) asCharacter].
|
||||
^dataStream
|
||||
]
|
||||
|
||||
mimeDecodeToByteArray [
|
||||
"Convert a stream in base 64 with only a-z,A-Z,0-9,+,/ to a full ByteArray of 0-255 values. Reutrn a whole stream for the user to read."
|
||||
|
||||
<category: 'conversion'>
|
||||
| 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: 15.
|
||||
(nibC := self nextValue) ifNil: [^dataStream].
|
||||
dataStream nextPut: (nibB bitShift: 4) + (nibC bitShift: -2).
|
||||
nibC := nibC bitAnd: 3.
|
||||
(nibD := self nextValue) ifNil: [^dataStream].
|
||||
dataStream nextPut: (nibC bitShift: 6) + nibD].
|
||||
^dataStream
|
||||
]
|
||||
|
||||
mimeEncode [
|
||||
"Convert from data to 6 bit characters."
|
||||
|
||||
<category: 'conversion'>
|
||||
| phase1 phase2 raw nib lineLength |
|
||||
phase1 := phase2 := false.
|
||||
lineLength := 0.
|
||||
[dataStream atEnd] whileFalse:
|
||||
[lineLength >= 70
|
||||
ifTrue:
|
||||
[mimeStream cr.
|
||||
lineLength := 0].
|
||||
data := raw := dataStream next asInteger.
|
||||
nib := (data bitAnd: 252) bitShift: -2.
|
||||
mimeStream nextPut: (ToCharTable at: nib + 1).
|
||||
(raw := dataStream next) ifNil:
|
||||
[raw := 0.
|
||||
phase1 := true].
|
||||
data := ((data bitAnd: 3) bitShift: 8) + raw asInteger.
|
||||
nib := (data bitAnd: 1008) bitShift: -4.
|
||||
mimeStream nextPut: (ToCharTable at: nib + 1).
|
||||
(raw := dataStream next) ifNil:
|
||||
[raw := 0.
|
||||
phase2 := true].
|
||||
data := ((data bitAnd: 15) bitShift: 8) + raw asInteger.
|
||||
nib := (data bitAnd: 4032) bitShift: -6.
|
||||
mimeStream nextPut: (ToCharTable at: nib + 1).
|
||||
nib := data bitAnd: 63.
|
||||
mimeStream nextPut: (ToCharTable at: nib + 1).
|
||||
lineLength := lineLength + 4].
|
||||
phase1
|
||||
ifTrue:
|
||||
[mimeStream
|
||||
skip: -2;
|
||||
nextPut: $=;
|
||||
nextPut: $=.
|
||||
^mimeStream].
|
||||
phase2
|
||||
ifTrue:
|
||||
[mimeStream
|
||||
skip: -1;
|
||||
nextPut: $=.
|
||||
^mimeStream]
|
||||
]
|
||||
|
||||
nextValue [
|
||||
"The next six bits of data char from the mimeStream, or nil. Skip all other chars"
|
||||
|
||||
<category: 'conversion'>
|
||||
| raw num |
|
||||
|
||||
[raw := mimeStream next.
|
||||
raw ifNil: [^nil]. "end of stream"
|
||||
raw == $= ifTrue: [^nil].
|
||||
num := FromCharTable at: raw asciiValue + 1.
|
||||
num ifNotNil: [^num].
|
||||
"else ignore space, return, tab, ..."
|
||||
true]
|
||||
whileTrue
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
|
||||
Eval [
|
||||
Base64MimeConverter initialize
|
||||
]
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
Eval [
|
||||
'From Pharo-1.1-11411 of 17 July 2010 [Latest update: #11411] on 19 February 2011 at 9:09:10 pm'
|
||||
]
|
||||
|
||||
|
||||
TestCase subclass: Base64MimeConverterTest [
|
||||
| message |
|
||||
|
||||
<comment: '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'>
|
||||
<category: 'CollectionsTests-Streams'>
|
||||
|
||||
setUp [
|
||||
<category: 'setup'>
|
||||
message := 'Hi There!' readStream
|
||||
]
|
||||
|
||||
testBase64Encoded [
|
||||
"self run: #testBase64Encoded"
|
||||
|
||||
<category: 'tests'>
|
||||
| encoded |
|
||||
encoded := (Base64MimeConverter mimeEncode: message) contents.
|
||||
self assert: encoded = 'Hi There!' base64Encoded
|
||||
]
|
||||
|
||||
testMimeEncodeDecode [
|
||||
"self run: #testMimeEncodeDecode"
|
||||
|
||||
<category: 'tests'>
|
||||
| encoded |
|
||||
encoded := Base64MimeConverter mimeEncode: message.
|
||||
self assert: encoded contents = 'SGkgVGhlcmUh'.
|
||||
self assert: (Base64MimeConverter mimeDecodeToBytes: encoded) contents asString
|
||||
= message contents.
|
||||
|
||||
"Encoding should proceed from the current stream position."
|
||||
message reset.
|
||||
message skip: 2.
|
||||
encoded := Base64MimeConverter mimeEncode: message.
|
||||
self assert: encoded contents = 'IFRoZXJlIQ=='.
|
||||
]
|
||||
|
||||
testOnByteArray [
|
||||
"self run: #testOnByteArray"
|
||||
|
||||
<category: 'tests'>
|
||||
self
|
||||
assert: 'Hi There!' base64Encoded = 'Hi There!' asByteArray base64Encoded
|
||||
]
|
||||
]
|
||||
|
|
@ -45,6 +45,30 @@ String extend [
|
|||
res ifNil: [^ 0]
|
||||
ifNotNil: [^ res first]
|
||||
]
|
||||
|
||||
base64Encoded [
|
||||
<category: '*network-mime'>
|
||||
"Encode the receiver as base64"
|
||||
"'Hello World' base64Encoded"
|
||||
^(Base64MimeConverter mimeEncode: self readStream) contents
|
||||
]
|
||||
|
||||
base64Decoded [
|
||||
<category: '*network-mime'>
|
||||
"Decode the receiver from base 64"
|
||||
"'SGVsbG8gV29ybGQ=' base64Decoded"
|
||||
^(Base64MimeConverter mimeDecode: self as: self class)
|
||||
]
|
||||
]
|
||||
|
||||
ByteArray extend [
|
||||
base64Encoded [
|
||||
<category: '*network-mime'>
|
||||
"Encode the receiver as base64"
|
||||
"'Hello World' base64Encoded"
|
||||
^(Base64MimeConverter mimeEncode: self readStream) contents
|
||||
]
|
||||
|
||||
]
|
||||
|
||||
UndefinedObject extend [
|
||||
|
|
Reference in New Issue