1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
package java.io
import scala.annotation.tailrec
import java.nio._
import java.nio.charset._
class OutputStreamWriter(private[this] var out: OutputStream,
private[this] var enc: CharsetEncoder) extends Writer {
private[this] var closed: Boolean = false
/** Incoming buffer: pending Chars that have been written to this instance
* of OutputStreamWriter, but not yet encoded.
* Normally, this should always be at most 1 Char, if it is a high surrogate
* which ended up alone at the end of the input of a write().
*/
private[this] var inBuf: String = ""
/** Outgoing buffer: Bytes that have been decoded (from `inBuf`), but not
* yet written to the underlying output stream.
* The valid bytes are between 0 and outBuf.position.
*/
private[this] var outBuf: ByteBuffer = ByteBuffer.allocate(4096)
def this(out: OutputStream, cs: Charset) =
this(out,
cs.newEncoder
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE))
def this(out: OutputStream) =
this(out, Charset.defaultCharset)
def this(out: OutputStream, charsetName: String) =
this(out, Charset.forName(charsetName))
def getEncoding(): String =
if (closed) null else enc.charset.name
override def write(c: Int): Unit =
write(c.toChar.toString, 0, 1)
override def write(cbuf: Array[Char], off: Int, len: Int): Unit =
writeImpl(CharBuffer.wrap(cbuf, off, len))
override def write(str: String, off: Int, len: Int): Unit =
writeImpl(CharBuffer.wrap(str, off, len))
private def writeImpl(cbuf: CharBuffer): Unit = {
ensureOpen()
val cbuf1 = if (inBuf != "") {
val fullInput = CharBuffer.wrap(inBuf + cbuf.toString)
inBuf = ""
fullInput
} else cbuf
@inline
@tailrec
def loopEncode(): Unit = {
val result = enc.encode(cbuf1, outBuf, false)
if (result.isUnderflow) ()
else if (result.isOverflow) {
makeRoomInOutBuf()
loopEncode()
} else {
result.throwException()
throw new AssertionError("should not get here")
}
}
loopEncode()
if (cbuf1.hasRemaining)
inBuf = cbuf1.toString
}
override def flush(): Unit = {
ensureOpen()
flushBuffer()
out.flush()
}
override def close(): Unit = if (!closed) {
// Finish up the input
@inline
@tailrec
def loopEncode(): Unit = {
val cbuf = CharBuffer.wrap(inBuf)
val result = enc.encode(cbuf, outBuf, true)
if (result.isUnderflow) {
assert(!cbuf.hasRemaining,
"CharsetEncoder.encode() should not have returned UNDERFLOW when "+
"both endOfInput and inBuf.hasRemaining are true. It should have "+
"returned a MalformedInput error instead.")
} else if (result.isOverflow) {
makeRoomInOutBuf()
loopEncode()
} else {
result.throwException()
throw new AssertionError("should not get here")
}
}
@inline
@tailrec
def loopFlush(): Unit = {
if (enc.flush(outBuf).isOverflow) {
makeRoomInOutBuf()
loopFlush()
}
}
loopEncode()
loopFlush()
// Flush before closing
flush()
// Close the underlying stream
out.close()
// Clean up all the resources
closed = true
out = null
enc = null
inBuf = null
outBuf = null
}
private def ensureOpen(): Unit = {
if (closed)
throw new IOException("Closed writer.")
}
private def makeRoomInOutBuf(): Unit = {
if (outBuf.position != 0) {
flushBuffer()
} else {
// Very unlikely (outBuf.capacity is not enough to encode a single code point)
outBuf.flip()
val newBuf = ByteBuffer.allocate(outBuf.capacity * 2)
newBuf.put(outBuf)
outBuf = newBuf
}
}
/** Flushes the internal buffer of this writer, but not the underlying
* output stream.
*/
private[io] def flushBuffer(): Unit = {
ensureOpen()
// Don't use outBuf.flip() first, in case out.write() throws
// Hence, use 0 instead of position, and position instead of limit
out.write(outBuf.array, outBuf.arrayOffset, outBuf.position)
outBuf.clear()
}
}
|