<div dir="ltr">Brainstorming time:<div>I think Brett's suggestion makes sense: BufferedWriter now can handle a mix of String and char[] inputs. So if the input is all LATIN1 Strings, we can allow them to stay LATIN1 in the StringBuilder and let OutputStreamWriter decide if the OSW has a fast path for LATIN1 when it is passed the SB through append(CharSequence).</div><div><br></div><div>At my first glance, this proposal might be broken into a few actions:</div><div>1. BufferedWriter now buffers to a StringBuilder (which can consider resetting the coder when length is reset to 0)</div><div>2. OutputStreamWriter now has a fast path for append(CharSequence csq) when this method receives a StringBuilder - if the LATIN1 or UTF16 bytes are compatible, we can trivially perform a write.</div><div>3. (Optional) StringBuilder can reset its coder to LATIN1 if it is called setLength(0)</div><div>4. (Alternative) StreamEncoder can define append(CharSequence) itself to handle StringBuilder, or even make use of the latest addition of CharSequence.getChars (but we need to defend against untrusted arrays). We have the ArrayEncoder interface that we might use.</div><div><br></div><div>However, this implies potential incompatibilities as in:</div><div>1. BufferedWriter previously only calls write(char[], int, int) to the delegated writer. Calling other methods may introduce incompatibilities.</div><div>2. Performing checks on mutable structures, such as StringBuilder and/or its array, is inherently risky to TOCTOU.</div><div><br></div><div>These are just the opportunities and the risks I have discovered on a first quick glance. I believe the whole situation is much more than what I have here, but we might start from some small, uncontroversial, and low-risk gains. For example, I would consider resetting StringBuilder coder to LATIN1 upon setLength(0) as a small gain with lower risk, though it does not directly relate to this effort of speeding up the write buffering.</div><div><br></div><div>Chen</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Sun, Jun 29, 2025 at 11:51 PM wenshao <<a href="mailto:shaojin.wensj@alibaba-inc.com">shaojin.wensj@alibaba-inc.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><span style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><br></span></div><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><span style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><span>Both Writer and CharsetEncoder are designed for char[]. Converting BufferedWriter to use byte[] value + byte coder like StringBuilder will also require redundant encoding conversion when using LATIN1 String, and the performance will not be good.</span></span></div><blockquote style="margin-right:0px;margin-top:0px;margin-bottom:0px;font-family:Tahoma,Arial,STHeiti,SimSun;font-size:14px;color:rgb(0,0,0)"><div><div style="clear:both">------------------------------------------------------------------</div><div style="clear:both">发件人:Brett Okken <<a href="mailto:brett.okken.os@gmail.com" target="_blank">brett.okken.os@gmail.com</a>></div><div style="clear:both">发送时间:2025年6月30日(周一) 11:39</div><div style="clear:both">收件人:"温绍锦(高铁)"<<a href="mailto:shaojin.wensj@alibaba-inc.com" target="_blank">shaojin.wensj@alibaba-inc.com</a>></div><div style="clear:both">抄 送:"core-libs-dev"<<a href="mailto:core-libs-dev@openjdk.org" target="_blank">core-libs-dev@openjdk.org</a>></div><div style="clear:both">主 题:Re: Eliminate unnecessary buffering and encoding conversion in BufferedWriter</div><div style="clear:both"><br></div><div>Maybe another option would be to implement BufferedWriter with a StringBuilder rather than a char[]. This would remove the force to utf-16</div><div><br><div class="gmail_quote"><div class="gmail_attr">On Sun, Jun 29, 2025 at 10:36 PM Brett Okken <<a href="mailto:brett.okken.os@gmail.com" target="_blank">brett.okken.os@gmail.com</a>> wrote:<br></div><div style="margin:14px 40px"><div>Is StreamEncoder buffering content to only write to the underlying OutputStream when some threshold is hit? While the layers of conversions are unfortunate, it seems there could be negative performance implications of having many extremely small writes (such as 1 character/byte) at a time to the underlying OutputStream.</div><div><br></div><div>Presumably this is a common pattern, as it is recommended:</div><div><div><a href="https://github.com/openjdk/jdk/blob/4dd1b3a6100f9e379c7cee3c699d63d0d01144a7/src/java.base/share/classes/java/io/OutputStreamWriter.java#L45" target="_blank">https://github.com/openjdk/jdk/blob/4dd1b3a6100f9e379c7cee3c699d63d0d01144a7/src/java.base/share/classes/java/io/OutputStreamWriter.java#L45</a></div><br></div><div><br><div class="gmail_quote"><div class="gmail_attr">On Sun, Jun 29, 2025 at 11:04 AM wenshao <<a href="mailto:shaojin.wensj@alibaba-inc.com" target="_blank">shaojin.wensj@alibaba-inc.com</a>> wrote:<br></div><div style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><span style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun">BufferedWriter -> OutputStreamWriter -> StreamEncoder</span><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><br></div><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun">In this call chain, BufferedWriter has a char[] buffer, and StreamEncoder has a ByteBuffer. There are two layers of cache here, or the BufferedWriter layer can be removed. </div><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><br></div><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><span style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;word-spacing:0px;white-space:normal;text-decoration-style:initial;text-decoration-color:initial;float:none;background-color:rgb(255,255,255);color:rgb(0,0,0);display:inline">LATIN1 (byte[]) -> UTF16 (char[]) -> UTF8 (byte[])</span></div><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><span style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;word-spacing:0px;white-space:normal;text-decoration-style:initial;text-decoration-color:initial;float:none;background-color:rgb(255,255,255);color:rgb(0,0,0);display:inline"><br></span></div><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun">And when charset is UTF8, if the content of write(String) is LATIN1, a conversion from LATIN1 to UTF16 and then to LATIN1 will occur here.</div><span style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><span style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><br></span></div>We can improve BufferedWriter. When the parameter Writer instanceof OutputStreamWriter is passed in, remove the cache and call it directly. In addition, improve write(String) in StreamEncoder to avoid unnecessary encoding conversion.</span></div><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><span style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><br></span></div><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><span style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun">-</span></div><div style="clear:both;font-family:Tahoma,Arial,STHeitiSC-Light,SimSun"><span style="font-family:Tahoma,Arial,STHeitiSC-Light,SimSun">Shaojin Wen</span></div></div></div></div></div></div></div></div></blockquote></div></div></blockquote></div>