org.rapidoid.writable.WritableUtils Maven / Gradle / Ivy
/*-
* #%L
* rapidoid-commons
* %%
* Copyright (C) 2014 - 2018 Nikolche Mihajlovski and contributors
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.rapidoid.writable;
import org.rapidoid.RapidoidThing;
import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import static org.rapidoid.util.Constants.*;
@Authors("Nikolche Mihajlovski")
@Since("5.3.4")
public class WritableUtils extends RapidoidThing {
private static final String MIN_VALUE = Long.MIN_VALUE + "";
public static void putNumAsText(Writable out, long n) {
if (n >= 0) {
writeDigits(out, n);
} else {
if (n == Long.MIN_VALUE) {
writeAscii(out, MIN_VALUE);
} else {
out.writeByte((byte) '-');
putNumAsText(out, -n);
}
}
}
private static void writeDigits(Writable out, long n) {
int digit = (int) (n % 10);
if (n >= 10) {
writeDigits(out, n / 10);
}
out.writeByte((byte) (digit + '0'));
}
public static void writeAscii(Writable out, String s) {
for (int i = 0; i < s.length(); i++) {
out.writeByte((byte) s.charAt(i));
}
}
public static void writeAscii(Writable out, byte[] bytes) {
out.writeBytes(bytes);
}
public static void writeUTF8(Writable out, String src) {
int limit = src.length();
for (int i = 0; i < limit; i++) {
char c = src.charAt(i);
if (c < 128) {
out.writeByte((byte) c);
} else {
// https://en.wikipedia.org/wiki/UTF-8
if (c < 0x800) {
// [110xxxxx, 10xxxxxx]
out.writeByte((byte) (UTF8_2_BYTES_LEAD | c >> 6)); // highest 5 bits
out.writeByte((byte) (UTF8_CONTINUATION | c & LAST_6)); // lowest 6 bits
} else if (Character.isSurrogate(c)) {
if (Character.isHighSurrogate(c)) {
int next = i + 1;
if (next < limit) {
char nextChar = src.charAt(next);
if (Character.isLowSurrogate(nextChar)) {
int cp = Character.toCodePoint(c, nextChar);
// [11110xxx, 10xxxxxx, 10xxxxxx, 10xxxxxx]
out.writeByte((byte) (UTF8_4_BYTES_LEAD | cp >> 18)); // highest 3 bits
out.writeByte((byte) (UTF8_CONTINUATION | cp >> 12 & LAST_6)); // next 6 bits
out.writeByte((byte) (UTF8_CONTINUATION | cp >> 6 & LAST_6)); // next 6 bits
out.writeByte((byte) (UTF8_CONTINUATION | cp & LAST_6)); // lowest 6 bits
i++; // the next char was successfully consumed
} else {
out.writeByte(MALFORMED_CHAR); // expected low surrogate
}
} else {
out.writeByte(MALFORMED_CHAR); // expected one more character
}
} else {
out.writeByte(MALFORMED_CHAR); // expected high surrogate
}
} else {
// [1110xxxx, 10xxxxxx, 10xxxxxx]
out.writeByte((byte) (UTF8_3_BYTES_LEAD | c >> 12)); // highest 4 bits
out.writeByte((byte) (UTF8_CONTINUATION | c >> 6 & LAST_6)); // next 6 bits
out.writeByte((byte) (UTF8_CONTINUATION | c & LAST_6)); // lowest 6 bits
}
}
}
}
public static void writeUTF8HtmlEscaped(Writable out, String src) {
int limit = src.length();
for (int i = 0; i < limit; i++) {
char c = src.charAt(i);
if (c < 128) {
switch (c) {
case '<': // <
out.writeByte((byte) '&');
out.writeByte((byte) 'l');
out.writeByte((byte) 't');
out.writeByte((byte) ';');
break;
case '>': // >
out.writeByte((byte) '&');
out.writeByte((byte) 'g');
out.writeByte((byte) 't');
out.writeByte((byte) ';');
break;
case '&': // &
out.writeByte((byte) '&');
out.writeByte((byte) 'a');
out.writeByte((byte) 'm');
out.writeByte((byte) 'p');
out.writeByte((byte) ';');
break;
case '"': // "
out.writeByte((byte) '&');
out.writeByte((byte) 'q');
out.writeByte((byte) 'u');
out.writeByte((byte) 'o');
out.writeByte((byte) 't');
out.writeByte((byte) ';');
break;
case '\'': // '
out.writeByte((byte) '&');
out.writeByte((byte) 'a');
out.writeByte((byte) 'p');
out.writeByte((byte) 'o');
out.writeByte((byte) 's');
out.writeByte((byte) ';');
break;
default:
out.writeByte((byte) c);
break;
}
} else {
// https://en.wikipedia.org/wiki/UTF-8
if (c < 0x800) {
// [110xxxxx, 10xxxxxx]
out.writeByte((byte) (UTF8_2_BYTES_LEAD | c >> 6)); // highest 5 bits
out.writeByte((byte) (UTF8_CONTINUATION | c & LAST_6)); // lowest 6 bits
} else if (Character.isSurrogate(c)) {
if (Character.isHighSurrogate(c)) {
int next = i + 1;
if (next < limit) {
char nextChar = src.charAt(next);
if (Character.isLowSurrogate(nextChar)) {
int cp = Character.toCodePoint(c, nextChar);
// [11110xxx, 10xxxxxx, 10xxxxxx, 10xxxxxx]
out.writeByte((byte) (UTF8_4_BYTES_LEAD | cp >> 18)); // highest 3 bits
out.writeByte((byte) (UTF8_CONTINUATION | cp >> 12 & LAST_6)); // next 6 bits
out.writeByte((byte) (UTF8_CONTINUATION | cp >> 6 & LAST_6)); // next 6 bits
out.writeByte((byte) (UTF8_CONTINUATION | cp & LAST_6)); // lowest 6 bits
i++; // the next char was successfully consumed
} else {
out.writeByte(MALFORMED_CHAR); // expected low surrogate
}
} else {
out.writeByte(MALFORMED_CHAR); // expected one more character
}
} else {
out.writeByte(MALFORMED_CHAR); // expected high surrogate
}
} else {
// [1110xxxx, 10xxxxxx, 10xxxxxx]
out.writeByte((byte) (UTF8_3_BYTES_LEAD | c >> 12)); // highest 4 bits
out.writeByte((byte) (UTF8_CONTINUATION | c >> 6 & LAST_6)); // next 6 bits
out.writeByte((byte) (UTF8_CONTINUATION | c & LAST_6)); // lowest 6 bits
}
}
}
}
}