author | Jan Vrany <jan.vrany@fit.cvut.cz> |
Wed, 01 Feb 2012 00:34:28 +0000 | |
changeset 97 | 2a7827f4dce2 |
parent 90 | 59f68d289949 |
child 111 | 44ac233b2f83 |
permissions | -rw-r--r-- |
10 | 1 |
"{ Package: 'stx:goodies/xtreams/transforms' }" |
2 |
||
3 |
"{ NameSpace: Xtreams }" |
|
4 |
||
5 |
ReadStream subclass:#EncodeReadStream |
|
6 |
instanceVariableNames:'transparent crPreceeding encoder buffer bufferWriting |
|
7 |
bufferReading' |
|
8 |
classVariableNames:'' |
|
97 | 9 |
poolDictionaries:'Xtreams::XtreamsPool' |
27
2cc5a8a3ca14
added XtreamsPool to fix DefaultBufferSize; set proper category names
mkobetic
parents:
10
diff
changeset
|
10 |
category:'Xtreams-Transforms' |
10 | 11 |
! |
12 |
||
13 |
EncodeReadStream comment:'Converts bytes into characters using pre-configured encoding. At the same time, if set to lineEndAuto (default) it can perform line-end translation, converting any line-end convention into CRs. The source stream must provide bytes (0...255). |
|
14 |
||
15 |
Instance Variables |
|
16 |
transparent <Boolean> should the stream perform line-end translations |
|
17 |
crPreceeding <Boolean> was previous character read a CR (used when not transparent) |
|
18 |
encoder <Encoder> converts bytes to characters |
|
19 |
buffer <Buffer on: ByteArray> used to optimize bulk reads |
|
20 |
bufferWriting <WriteStream> write stream on buffer |
|
21 |
bufferReading <ReadStream> read stream on buffer |
|
22 |
||
23 |
' |
|
24 |
! |
|
25 |
||
26 |
||
27 |
!EncodeReadStream class methodsFor:'instance creation'! |
|
28 |
||
29 |
on: aSource encoding: anEncoding |
|
30 |
^self new on: aSource encoding: anEncoding |
|
31 |
! ! |
|
32 |
||
33 |
!EncodeReadStream methodsFor:'accessing'! |
|
34 |
||
35 |
encoder |
|
36 |
||
37 |
^encoder |
|
38 |
! |
|
39 |
||
40 |
get |
|
41 |
| character | |
|
42 |
buffer hasDataToRead ifTrue: [^super get]. |
|
43 |
character := encoder decodeFrom: source. |
|
44 |
transparent ifFalse: |
|
45 |
[character == LF |
|
46 |
ifTrue: [crPreceeding |
|
47 |
ifTrue: |
|
48 |
[character := encoder decodeFrom: source. |
|
49 |
crPreceeding := character = CR] |
|
50 |
ifFalse: |
|
51 |
[crPreceeding := false. |
|
52 |
character := CR]] |
|
53 |
ifFalse: [crPreceeding := character = CR]]. |
|
54 |
^character |
|
55 |
! |
|
56 |
||
57 |
read: anInteger into: aSequenceableCollection at: startIndex |
|
58 |
||
59 |
| remaining position character bufferAvailable | |
|
60 |
remaining := anInteger. |
|
61 |
position := startIndex. |
|
62 |
[remaining > 0] whileTrue: [ |
|
63 |
| mark | |
|
64 |
"Top up our buffer if we have room and we need data" |
|
65 |
[bufferWriting write: (buffer writeSize min: remaining) from: source] on: Incomplete do: [:incomplete | |
|
66 |
(incomplete count == 0 and: [buffer hasDataToRead not]) ifTrue: [ |
|
67 |
(Incomplete on: aSequenceableCollection count: anInteger - remaining at: startIndex) raise]]. |
|
68 |
||
69 |
"We now conduct an inner loop that iterates over the buffer data while: |
|
70 |
a) we need to read more data |
|
71 |
b) there is data available in the buffer |
|
72 |
c) a character can successfully be decoded |
|
73 |
" |
|
74 |
||
75 |
"If our buffer size is too low before we begin our decode loop, we need to take an undo copy in case we cannot decode a character." |
|
76 |
buffer readSize < 10 ifTrue: |
|
77 |
[mark := buffer readPosition. |
|
78 |
encoder backupState ]. |
|
79 |
||
80 |
[["The following may raise an incomplete, which means we don't have enough data in the buffer to decode the full character. |
|
81 |
This is handled by the Incomplete exception capture before." |
|
82 |
character := encoder decodeFrom: bufferReading. |
|
83 |
||
84 |
"If we are not transparent, convert stray LFs in to CRs and CRLFs in to CRs" |
|
85 |
transparent ifFalse: [ |
|
86 |
character == LF |
|
87 |
ifTrue: [character := crPreceeding ifTrue: [nil] ifFalse: [CR]. crPreceeding := false] |
|
88 |
ifFalse: [crPreceeding := character = CR]]. |
|
89 |
||
90 |
"If we didn't filter out an LF at the tail of a CRLF, commit the character to the output." |
|
91 |
character == nil ifFalse: |
|
92 |
[aSequenceableCollection at: position put: character. |
|
93 |
remaining := remaining - 1. |
|
94 |
position := position + 1]. |
|
95 |
||
96 |
"Find out how much data we have left in the buffer. If it's too low we need to keep track of the undo record in case we cannot decode a character." |
|
97 |
(bufferAvailable := buffer readSize) < 10 ifTrue: |
|
98 |
[mark := buffer readPosition. |
|
99 |
encoder backupState ]. |
|
100 |
||
101 |
remaining > 0 and: [bufferAvailable > 0]] whileTrue] |
|
102 |
on: Incomplete do: [:incomplete | |
|
103 |
"We failed to decode a character, we've hit the end of the buffer and need to refill it. We rewind the buffer and leave the decoding loop |
|
104 |
to return to the main loop where more data will be fetched in to our buffer." |
|
105 |
buffer readPosition: mark. |
|
106 |
encoder restoreState]]. |
|
107 |
^anInteger |
|
108 |
! ! |
|
109 |
||
110 |
!EncodeReadStream methodsFor:'initialize-release'! |
|
111 |
||
112 |
close |
|
113 |
super close. |
|
114 |
buffer recycle. |
|
115 |
buffer := nil |
|
116 |
! |
|
117 |
||
118 |
contentsSpecies |
|
90 | 119 |
|
120 |
^encoder contentsSpecies |
|
10 | 121 |
! |
122 |
||
123 |
on: aSource encoding: anEncoding |
|
124 |
||
125 |
super on: aSource. |
|
126 |
encoder := Encoder for: anEncoding. |
|
127 |
buffer := RingBuffer new: DefaultBufferSize class: ByteArray. |
|
128 |
bufferReading := buffer reading. |
|
129 |
bufferWriting := buffer writing. |
|
130 |
transparent := false. |
|
131 |
crPreceeding := false. |
|
132 |
! ! |
|
133 |
||
134 |
!EncodeReadStream methodsFor:'line-end'! |
|
135 |
||
136 |
setLineEndAuto |
|
137 |
||
138 |
transparent := false |
|
139 |
! |
|
140 |
||
141 |
setLineEndTransparent |
|
142 |
||
143 |
transparent := true |
|
144 |
! ! |
|
145 |
||
146 |
!EncodeReadStream class methodsFor:'documentation'! |
|
147 |
||
148 |
version_SVN |
|
149 |
^ '$Id$' |
|
150 |
! ! |