diff --git a/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiRequest.kt b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiRequest.kt index 93d0d20c3af4..35161ea62ed6 100644 --- a/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiRequest.kt +++ b/platform/built-in-server/src/org/jetbrains/io/fastCgi/FastCgiRequest.kt @@ -4,11 +4,11 @@ import com.intellij.openapi.util.io.FileUtil import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBufAllocator import io.netty.buffer.ByteBufUtil -import io.netty.buffer.ByteBufUtilEx import io.netty.channel.Channel import io.netty.handler.codec.http.FullHttpRequest import org.jetbrains.builtInWebServer.PathInfo import org.jetbrains.io.serverHeaderValue +import org.jetbrains.io.writeUtf8 import java.net.InetSocketAddress import java.util.* @@ -67,7 +67,7 @@ class FastCgiRequest(val requestId: Int, allocator: ByteBufAllocator) { } ByteBufUtil.writeAscii(buffer, key) - ByteBufUtilEx.writeUtf8(buffer, value) + buffer!!.writeUtf8(value) } fun writeHeaders(request: FullHttpRequest, clientChannel: Channel) { diff --git a/platform/built-in-server/src/org/jetbrains/io/jsonRpc/JsonRpcServer.kt b/platform/built-in-server/src/org/jetbrains/io/jsonRpc/JsonRpcServer.kt index 90873948a465..fd48276dbf1d 100644 --- a/platform/built-in-server/src/org/jetbrains/io/jsonRpc/JsonRpcServer.kt +++ b/platform/built-in-server/src/org/jetbrains/io/jsonRpc/JsonRpcServer.kt @@ -22,6 +22,7 @@ import org.jetbrains.concurrency.Promise import org.jetbrains.io.JsonReaderEx import org.jetbrains.io.JsonUtil import org.jetbrains.io.releaseIfError +import org.jetbrains.io.writeUtf8 import java.io.IOException import java.lang.reflect.Method import java.util.concurrent.atomic.AtomicInteger @@ -295,7 +296,7 @@ class JsonRpcServer(private val clientManager: ClientManager) : MessageServer { } @Suppress("UNCHECKED_CAST") (param as Consumer).consume(sb) - ByteBufUtilEx.writeUtf8(buffer, sb) + buffer.writeUtf8(sb) sb.setLength(0) } else { diff --git a/platform/platform-impl/src/io/netty/buffer/ByteBufUtf8Writer.java b/platform/platform-impl/src/io/netty/buffer/ByteBufUtf8Writer.java index d68b7740cae2..a6eaab4ad5eb 100644 --- a/platform/platform-impl/src/io/netty/buffer/ByteBufUtf8Writer.java +++ b/platform/platform-impl/src/io/netty/buffer/ByteBufUtf8Writer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package io.netty.buffer; import com.intellij.util.text.CharArrayCharSequence; import org.jetbrains.annotations.NotNull; +import org.jetbrains.io.NettyKt; import java.io.IOException; import java.io.InputStream; @@ -37,19 +38,14 @@ public final class ByteBufUtf8Writer extends Writer { buffer.ensureWritable(minWritableBytes); } - @Override - public void write(int c) { - buffer.writerIndex(ByteBufUtilEx.writeChar(ByteBufUtilEx.getBuf(buffer), buffer.writerIndex(), c)); - } - @Override public void write(char[] chars, int off, int len) { - ByteBufUtilEx.writeUtf8(buffer, new CharArrayCharSequence(chars, off, off + len)); + NettyKt.writeUtf8(buffer, new CharArrayCharSequence(chars, off, off + len)); } @Override public void write(String str) { - ByteBufUtilEx.writeUtf8(buffer, str); + NettyKt.writeUtf8(buffer, str); } @Override @@ -63,7 +59,7 @@ public final class ByteBufUtf8Writer extends Writer { ByteBufUtil.writeAscii(buffer, "null"); } else { - ByteBufUtilEx.writeUtf8(buffer, csq); + NettyKt.writeUtf8(buffer, csq); } return this; } diff --git a/platform/platform-impl/src/io/netty/buffer/ByteBufUtilEx.java b/platform/platform-impl/src/io/netty/buffer/ByteBufUtilEx.java index 3911c9cadfc4..3122cf9f7c3c 100644 --- a/platform/platform-impl/src/io/netty/buffer/ByteBufUtilEx.java +++ b/platform/platform-impl/src/io/netty/buffer/ByteBufUtilEx.java @@ -16,13 +16,12 @@ package io.netty.buffer; import io.netty.util.CharsetUtil; -import org.jetbrains.annotations.NotNull; + +import static io.netty.util.internal.StringUtil.isSurrogate; // todo pull request public class ByteBufUtilEx { - public static int writeUtf8(ByteBuf buf, CharSequence seq) { - return writeUtf8(buf, seq, 0, seq.length()); - } + private static final byte WRITE_UTF_UNKNOWN = (byte) '?'; public static int writeUtf8(ByteBuf buf, CharSequence seq, int start, int end) { if (buf == null) { @@ -57,10 +56,49 @@ public class ByteBufUtilEx { } int writerIndex = oldWriterIndex; - // We can use the _set methods as these not need to do any index checks and reference checks. - // This is possible as we called ensureWritable(...) before. for (int i = start; i < end; i++) { - writerIndex = writeChar(buffer, writerIndex, seq.charAt(i)); + char c = seq.charAt(i); + if (c < 0x80) { + buffer._setByte(writerIndex++, (byte)c); + } + else if (c < 0x800) { + buffer._setByte(writerIndex++, (byte)(0xc0 | (c >> 6))); + buffer._setByte(writerIndex++, (byte)(0x80 | (c & 0x3f))); + } + else if (isSurrogate(c)) { + if (!Character.isHighSurrogate(c)) { + buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); + continue; + } + final char c2; + try { + // Surrogate Pair consumes 2 characters. Optimistically try to get the next character to avoid + // duplicate bounds checking with charAt. If an IndexOutOfBoundsException is thrown we will + // re-throw a more informative exception describing the problem. + //noinspection AssignmentToForLoopParameter + c2 = seq.charAt(++i); + } + catch (IndexOutOfBoundsException e) { + buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); + break; + } + if (!Character.isLowSurrogate(c2)) { + buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); + buffer._setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2); + continue; + } + int codePoint = Character.toCodePoint(c, c2); + // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. + buffer._setByte(writerIndex++, (byte)(0xf0 | (codePoint >> 18))); + buffer._setByte(writerIndex++, (byte)(0x80 | ((codePoint >> 12) & 0x3f))); + buffer._setByte(writerIndex++, (byte)(0x80 | ((codePoint >> 6) & 0x3f))); + buffer._setByte(writerIndex++, (byte)(0x80 | (codePoint & 0x3f))); + } + else { + buffer._setByte(writerIndex++, (byte)(0xe0 | (c >> 12))); + buffer._setByte(writerIndex++, (byte)(0x80 | ((c >> 6) & 0x3f))); + buffer._setByte(writerIndex++, (byte)(0x80 | (c & 0x3f))); + } } // update the writerIndex without any extra checks for performance reasons @@ -72,30 +110,4 @@ public class ByteBufUtilEx { } return writerIndex - oldWriterIndex; } - - static int writeChar(AbstractByteBuf buffer, int writerIndex, int c) { - if (c < 0x80) { - buffer._setByte(writerIndex++, (byte)c); - } - else if (c < 0x800) { - buffer._setByte(writerIndex++, (byte)(0xc0 | (c >> 6))); - buffer._setByte(writerIndex++, (byte)(0x80 | (c & 0x3f))); - } - else { - buffer._setByte(writerIndex++, (byte)(0xe0 | (c >> 12))); - buffer._setByte(writerIndex++, (byte)(0x80 | ((c >> 6) & 0x3f))); - buffer._setByte(writerIndex++, (byte)(0x80 | (c & 0x3f))); - } - return writerIndex; - } - - @NotNull - static AbstractByteBuf getBuf(@NotNull ByteBuf buffer) { - if (buffer instanceof AbstractByteBuf) { - return (AbstractByteBuf)buffer; - } - else { - return (AbstractByteBuf)((WrappedByteBuf)buffer).buf; - } - } } diff --git a/platform/platform-impl/src/org/jetbrains/io/netty.kt b/platform/platform-impl/src/org/jetbrains/io/netty.kt index fe102ed62ccd..e1129c35102a 100644 --- a/platform/platform-impl/src/org/jetbrains/io/netty.kt +++ b/platform/platform-impl/src/org/jetbrains/io/netty.kt @@ -198,4 +198,5 @@ fun HttpRequest.isWriteFromBrowserWithoutOrigin(): Boolean { return origin.isNullOrEmpty() && isRegularBrowser() && (method == HttpMethod.POST || method == HttpMethod.PATCH || method == HttpMethod.PUT || method == HttpMethod.DELETE) } -fun ByteBuf.readUtf8() = toString(Charsets.UTF_8) \ No newline at end of file +fun ByteBuf.readUtf8() = toString(Charsets.UTF_8) +fun ByteBuf.writeUtf8(data: CharSequence) = writeCharSequence(data, Charsets.UTF_8) \ No newline at end of file diff --git a/platform/script-debugger/protocol/protocol-reader-runtime/src/org/jetbrains/jsonProtocol/OutMessage.kt b/platform/script-debugger/protocol/protocol-reader-runtime/src/org/jetbrains/jsonProtocol/OutMessage.kt index 8dd96eebaaad..4e04af8cb877 100644 --- a/platform/script-debugger/protocol/protocol-reader-runtime/src/org/jetbrains/jsonProtocol/OutMessage.kt +++ b/platform/script-debugger/protocol/protocol-reader-runtime/src/org/jetbrains/jsonProtocol/OutMessage.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,8 @@ import gnu.trove.TIntHashSet import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBufAllocator import io.netty.buffer.ByteBufUtf8Writer -import io.netty.buffer.ByteBufUtilEx import org.jetbrains.io.JsonUtil +import org.jetbrains.io.writeUtf8 import java.io.IOException open class OutMessage() { @@ -46,8 +46,8 @@ open class OutMessage() { beginArguments() writer.name(name) writer.beginObject() - for (entry in value.entries) { - writer.name(entry.key).value(entry.value) + for ((key, value1) in value) { + writer.name(key).value(value1) } writer.endObject() } @@ -213,7 +213,7 @@ open class OutMessage() { } fun doWriteRaw(message: OutMessage, rawValue: String) { - ByteBufUtilEx.writeUtf8(message.buffer, rawValue) + message.buffer.writeUtf8(rawValue) } } }