java streamencoder_LearningJDK/StreamEncoder.java at master · zihuaVeryGood/LearningJDK · GitHub

/*

* Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved.

* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.

*

* This code is free software; you can redistribute it and/or modify it

* under the terms of the GNU General Public License version 2 only, as

* published by the Free Software Foundation. Oracle designates this

* particular file as subject to the "Classpath" exception as provided

* by Oracle in the LICENSE file that accompanied this code.

*

* This code is distributed in the hope that it will be useful, but WITHOUT

* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or

* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License

* version 2 for more details (a copy is included in the LICENSE file that

* accompanied this code).

*

* You should have received a copy of the GNU General Public License version

* 2 along with this work; if not, write to the Free Software Foundation,

* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.

*

* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA

* or visit www.oracle.com if you need additional information or have any

* questions.

*/

/*

*/

package sun.nio.cs;

import java.io.*;

import java.nio.*;

import java.nio.channels.*;

import java.nio.charset.*;

// 输出流编码器:将字符序列编码为字节后,写入到字节输出流

public class StreamEncoder extends Writer {

private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;

private CharsetEncoder encoder; // 字符编码器。字符进来,字节出去,完成对字符序列的编码操作

private Charset cs; // 字符编码器对应的字符集

/** Exactly one of these is non-null */

private final OutputStream out; // 字节输出流(最终输出流)

/** Factory for java.nio.channels.Channels.newWriter */

private WritableByteChannel ch; // 输出目的地关联的通道

private ByteBuffer bb; // 内部使用的堆内存缓冲区,存储解码后的字节

/**

* All synchronization and state/argument checking is done in these public methods;

* the concrete stream-encoder subclasses defined below need not do any such checking.

* Leftover first char in a surrogate pair

*/

private boolean haveLeftoverChar = false; // 上一轮编码后,是否残留一个未处理字符

private char leftoverChar; // 保存一个未处理字符

private CharBuffer lcb = null; // 临时存储待处理的字符

private volatile boolean closed;

/*▼ 构造器 ████████████████████████████████████████████████████████████████████████████████┓ */

private StreamEncoder(OutputStream out, Object lock, Charset cs) {

this(out, lock, cs.newEncoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE));

}

private StreamEncoder(OutputStream out, Object lock, CharsetEncoder enc) {

super(lock);

this.out = out;

this.ch = null;

this.cs = enc.charset();

this.encoder = enc;

/* This path disabled until direct buffers are faster */

// 已禁用此途径,将来文件使用直接缓冲区更快时,可能会解禁此途径

if(false && out instanceof FileOutputStream) {

// 获取当前文件输入流关联的只读通道

ch = ((FileOutputStream) out).getChannel();

if(ch != null) {

// 创建直接内存缓冲区DirectByteBuffer

bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);

}

}

if(ch == null) {

// 创建堆内存缓冲区HeapByteBuffer

bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);

}

}

private StreamEncoder(WritableByteChannel ch, CharsetEncoder enc, int mbc) {

this.out = null;

this.ch = ch;

this.cs = enc.charset();

this.encoder = enc;

this.bb = ByteBuffer.allocate(mbc<0 ? DEFAULT_BYTE_BUFFER_SIZE : mbc);

}

/*▲ 构造器 ████████████████████████████████████████████████████████████████████████████████┛ */

/*▼ 写 ████████████████████████████████████████████████████████████████████████████████┓ */

// 将指定的字符写入到输出流

public void write(int c) throws IOException {

char[] cbuf = new char[1];

cbuf[0] = (char) c;

write(cbuf, 0, 1);

}

// 将字符数组cbuf中off处起的len个字符写入到输出流

public void write(char[] cbuf, int off, int len) throws IOException {

synchronized(lock) {

ensureOpen();

if((off<0) || (off>cbuf.length) || (len<0) || ((off + len)>cbuf.length) || ((off + len)<0)) {

throw new IndexOutOfBoundsException();

} else if(len == 0) {

return;

}

implWrite(cbuf, off, len);

}

}

// 将字符串str中off处起的len个字符写入到输出流

public void write(String str, int off, int len) throws IOException {

/* Check the len before creating a char buffer */

if(len<0) {

throw new IndexOutOfBoundsException();

}

char[] cbuf = new char[len];

str.getChars(off, off + len, cbuf, 0);

write(cbuf, 0, len);

}

// 将字符缓冲区cb中的字符写入到输出流

public void write(CharBuffer cb) throws IOException {

int position = cb.position();

try {

synchronized(lock) {

ensureOpen();

// 从cb中读取字符,并对其编码后写到输出流

implWrite(cb);

}

} finally {

cb.position(position);

}

}

/*▲ 写 ████████████████████████████████████████████████████████████████████████████████┛ */

/*▼ 杂项 ████████████████████████████████████████████████████████████████████████████████┓ */

// 刷新缓冲区:将内部缓冲区中的字节写到最终输出流中(该操作不会刷新最终输出流)

public void flushBuffer() throws IOException {

synchronized(lock) {

if(isOpen()) {

implFlushBuffer();

} else {

throw new IOException("Stream closed");

}

}

}

// 刷新流,不仅刷新当前输出流编码器的内部缓冲区,还刷新最终字节输出流的内部缓冲区(如果存在)

public void flush() throws IOException {

synchronized(lock) {

ensureOpen();

implFlush();

}

}

// 关闭输出流编码器

public void close() throws IOException {

synchronized(lock) {

if(closed) {

return;

}

implClose();

closed = true;

}

}

// 返回字节编码器使用的字符集名称

public String getEncoding() {

if(isOpen()) {

return encodingName();

}

return null;

}

/*▲ 杂项 ████████████████████████████████████████████████████████████████████████████████┛ */

/*▼ 工厂方法 ████████████████████████████████████████████████████████████████████████████████┓ */

// 返回输出流编码器,out是输出目的地

public static StreamEncoder forOutputStreamWriter(OutputStream out, Object lock, String charsetName) throws UnsupportedEncodingException {

String csn = charsetName;

if(csn == null)

csn = Charset.defaultCharset().name();

try {

if(Charset.isSupported(csn)) {

return new StreamEncoder(out, lock, Charset.forName(csn));

}

} catch(IllegalCharsetNameException x) {

}

throw new UnsupportedEncodingException(csn);

}

// 返回输出流编码器,out是输出目的地

public static StreamEncoder forOutputStreamWriter(OutputStream out, Object lock, Charset cs) {

return new StreamEncoder(out, lock, cs);

}

// 返回输出流编码器,out是输出目的地

public static StreamEncoder forOutputStreamWriter(OutputStream out, Object lock, CharsetEncoder enc) {

return new StreamEncoder(out, lock, enc);

}

// 返回输出流编码器,ch是输出目的地,minBufferCap是内部缓冲区的最小容量

public static StreamEncoder forEncoder(WritableByteChannel ch, CharsetEncoder enc, int minBufferCap) {

return new StreamEncoder(ch, enc, minBufferCap);

}

/*▲ 工厂方法 ████████████████████████████████████████████████████████████████████████████████┛ */

// 从字符数组cbuf中off处起读取len个字符,并对其编码后写到输出流

void implWrite(char[] cbuf, int off, int len) throws IOException {

// 包装字符数组cbuf到缓冲区cb

CharBuffer cb = CharBuffer.wrap(cbuf, off, len);

// 从cb中读取字符,并对其编码后写到输出流

implWrite(cb);

}

// 从cb中读取字符,并对其编码后写到输出流

void implWrite(CharBuffer cb) throws IOException {

// 如果包含至少一个未处理字符

if(haveLeftoverChar) {

/*

* 对上一轮写入中残留下的那个字符进行编码,并将编码后的字节后写入到输出流

* 当发生下溢时,不会抛异常,因为可以期待后续的输入

*/

flushLeftoverChar(cb, false);

}

// 如果缓冲区还有未读字符

while(cb.hasRemaining()) {

/*

* 从给定的输入缓冲区cb中编码尽可能多的字符,将结果写入给定的输出缓冲区bb。

* 当发生下溢时,可能还会有后续的输入,不会抛异常

*/

CoderResult cr = encoder.encode(cb, bb, false);

/*

* 发生下溢,输出缓冲区bb仍有空闲

*

* 出现此情形很可能遇到了四字节符号

*/

if(cr.isUnderflow()) {

assert (cb.remaining()<=1) : cb.remaining();

// 如果ch中仅剩一个未读的字符了

if(cb.remaining() == 1) {

// 暂存该未处理的字符

haveLeftoverChar = true;

leftoverChar = cb.get();

}

break;

}

// 发生上溢,输出缓冲区已经满了

if(cr.isOverflow()) {

assert bb.position()>0;

// 将内部缓冲区中的字节写到最终输出流中

writeBytes();

continue;

}

cr.throwException();

}

}

// 刷新流,不仅刷新当前输出流编码器的内部缓冲区,还刷新字节输出流(的内部缓冲区)

void implFlush() throws IOException {

implFlushBuffer();

if(out != null) {

out.flush();

}

}

// 刷新缓冲区:将内部缓冲区中的字节写到最终输出流中

void implFlushBuffer() throws IOException {

// 如果内部缓冲区中包含数据

if(bb.position()>0) {

// 将内部缓冲区中的字节写到最终输出流中

writeBytes();

}

}

// 关闭输出流编码器

void implClose() throws IOException {

/*

* 对上一轮写入中残留下的那个字符进行编码,并将编码后的字节后写入到输出流

* 当发生下溢时,可能会抛异常(遇到了有缺陷的输入)

*/

flushLeftoverChar(null, true);

try {

for(; ; ) {

// 刷新内部缓冲区

CoderResult cr = encoder.flush(bb);

if(cr.isUnderflow()) {

break;

}

if(cr.isOverflow()) {

assert bb.position()>0;

// 将内部缓冲区中的字节写到最终输出流中

writeBytes();

continue;

}

cr.throwException();

}

if(bb.position()>0) {

// 将内部缓冲区中的字节写到最终输出流中

writeBytes();

}

if(ch != null) {

ch.close();

} else {

out.close();

}

} catch(IOException x) {

encoder.reset();

throw x;

}

}

/*

* 对上一轮写入中残留下的那个字符进行编码,并将编码后的字节后写入到输出流

*

* 如果之前有残留的字符,则需要从cb中再多读一个字符以配合编码,如果此时cb中已经没有可读字符,则该字符继续被残留。

* 如果之前没有残留的字符,则需要从cb中读取一个或读取两个字符进行编码

*

* endOfInput=true :当发生下溢时,可能会抛异常(遇到了有缺陷的输入)

* endOfInput=false:当发生下溢时,不会抛异常,因为可以期待后续的输入

*/

private void flushLeftoverChar(CharBuffer cb, boolean endOfInput) throws IOException {

// 之前没有残留未处理字符,且不需要关闭输入流

if(!haveLeftoverChar && !endOfInput) {

return;

}

/* 至此,说明之前残留待处理字符,或(且)需要立即关闭输入流 */

if(lcb == null) {

/*

* 构造非直接缓冲区HeapCharBuffer:将缓冲区建立在JVM的内存中

* 这里容量分配为2的原因是现存的任何符号,最多用两个char就可以表示了

*/

lcb = CharBuffer.allocate(2);

} else {

// 如果不为空,先清空它

lcb.clear();

}

if(haveLeftoverChar) {

// 向lcb中存入之前缓存的一个待读字符

lcb.put(leftoverChar);

}

// 如果cb中仍有可读的字符

if((cb != null) && cb.hasRemaining()) {

// 继续从cb中读取一个char存入lcb

lcb.put(cb.get());

}

// 将lcb从写模式转为读模式

lcb.flip();

// 如果lcb中还有待读字符,或者中断了输入

while(lcb.hasRemaining() || endOfInput) {

/*

* 从给定的输入缓冲区lcb中编码尽可能多的字符,将结果写入给定的输出缓冲区bb。

*

* endOfInput=true表示当发生下溢时,输入立即结束,即不会再提供更多输入

* endOfInput=false表示当发生下溢时,可能还会有后续的输入

*/

CoderResult cr = encoder.encode(lcb, bb, endOfInput);

/*

* 发生下溢,输出缓冲区仍有空闲

*

* 可能是lcb进来时有2个char,但是都被处理完了,那么后续就break了

* 可能是lcb进来时有1个char,但这个char来自cb,此时需要如果ch处理完了,直接return,如果cb没处理完,则尝试从cb中再读一个char进来处理

* 可能是lcb进来时有1个char,但这个char来自leftoverChar,此时cb已处理完,则leftoverChar重新记下该char,并且return

*/

if(cr.isUnderflow()) {

// 如果lcb中还有待读字符

if(lcb.hasRemaining()) {

// 将lcb中剩下未处理的那个char缓存到leftoverChar中

leftoverChar = lcb.get();

// 如果cb中仍有可读的字符

if(cb != null && cb.hasRemaining()) {

// 清空,进入写模式

lcb.clear();

// 重复向lcb填充2个char,填充完成后切换到读模式

lcb.put(leftoverChar).put(cb.get()).flip();

continue;

}

return;

}

break;

}

// 发生上溢,输出缓冲区已经满了

if(cr.isOverflow()) {

assert bb.position()>0;

// 将内部缓冲区中的字节写到最终输出流中

writeBytes();

continue;

}

cr.throwException();

}

haveLeftoverChar = false;

}

// 将内部缓冲区中的字节写到最终输出流中

private void writeBytes() throws IOException {

bb.flip(); // 将内部缓冲区从写模式切换到读模式

int lim = bb.limit();

int pos = bb.position();

assert (pos<=lim);

int rem = (pos<=lim ? lim - pos : 0);

if(rem>0) {

if(ch != null) {

assert ch.write(bb) == rem : rem;

} else {

// 返回该buffer内部的数组

byte[] b = bb.array();

int off = bb.arrayOffset() + pos;

// 将字节数组b中off处起的rem个字节写入到输出流

out.write(b, off, rem);

}

}

// 清理缓冲区,重置标记

bb.clear();

}

// 返回字节编码器使用的字符集名称

String encodingName() {

return ((cs instanceof HistoricallyNamedCharset) ? ((HistoricallyNamedCharset) cs).historicalName() : cs.name());

}

// 判断输出流编码器是否处于开启状态

private boolean isOpen() {

return !closed;

}

// 确保输出流编码器处于开启状态

private void ensureOpen() throws IOException {

if(closed) {

throw new IOException("Stream closed");

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值