/*
* Copyright (C) 2015 SoftIndex LLC.
*
* 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.
*/
package io.datakernel.bytebuf;
import io.datakernel.exception.ParseException;
@SuppressWarnings({"ThrowableInstanceNeverThrown", "WeakerAccess", "unused"})
public final class ByteBufStrings {
public static final ParseException READ_PAST_LIMIT = new ParseException("Malformed utf-8 input: Read past end");
public static final ParseException READ_PAST_ARRAY_LENGTH = new ParseException("Malformed utf-8 input");
public static final byte CR = (byte) '\r';
public static final byte LF = (byte) '\n';
public static final byte SP = (byte) ' ';
public static final byte HT = (byte) '\t';
private ByteBufStrings() {
}
// ASCII
public static void encodeAscii(byte[] array, int pos, String string) {
for (int i = 0; i < string.length(); i++) {
array[pos++] = (byte) string.charAt(i);
}
}
public static byte[] encodeAscii(String string) {
byte[] array = new byte[string.length()];
for (int i = 0; i < string.length(); i++) {
array[i] = (byte) string.charAt(i);
}
return array;
}
public static void putAscii(ByteBuf buf, String string) {
encodeAscii(buf.array(), buf.writePosition(), string);
buf.moveWritePosition(string.length());
}
public static ByteBuf wrapAscii(String string) {
ByteBuf buf = ByteBufPool.allocate(string.length());
byte[] array = buf.array();
for (int i = 0; i < string.length(); i++) {
array[i] = (byte) string.charAt(i);
}
buf.moveWritePosition(string.length());
return buf;
}
public static String decodeAscii(byte[] array, int pos, int len, char[] tmpBuffer) {
int charIndex = 0, end = pos + len;
while (pos < end) {
int c = array[pos++] & 0xff;
tmpBuffer[charIndex++] = (char) c;
}
return new String(tmpBuffer, 0, charIndex);
}
public static String decodeAscii(byte[] array, int pos, int len) {
return decodeAscii(array, pos, len, new char[len]);
}
public static String decodeAscii(ByteBuf buf, char[] tmpBuffer) {
return decodeAscii(buf.array(), buf.readPosition(), buf.readRemaining(), tmpBuffer);
}
public static String decodeAscii(ByteBuf buf) {
return decodeAscii(buf.array(), buf.readPosition(), buf.readRemaining(), new char[buf.readRemaining()]);
}
public static String decodeAscii(byte[] array) {
return decodeAscii(array, 0, array.length, new char[array.length]);
}
public static void toLowerCaseAscii(byte[] bytes, int pos, int len) {
for (int i = pos; i < pos + len; i++) {
byte b = bytes[i];
if (b >= 'A' && b <= 'Z') {
bytes[i] = (byte) (b + ((byte) 'a' - (byte) 'A'));
}
}
}
public static void toLowerCaseAscii(byte[] bytes) {
toLowerCaseAscii(bytes, 0, bytes.length);
}
public static void toLowerCaseAscii(ByteBuf buf) {
toLowerCaseAscii(buf.array(), buf.readPosition(), buf.readRemaining());
}
public static void toUpperCaseAscii(byte[] bytes, int pos, int len) {
for (int i = pos; i < pos + len; i++) {
byte b = bytes[i];
if (b >= 'a' && b <= 'z') {
bytes[i] = (byte) (b + ((byte) 'A' - (byte) 'a'));
}
}
}
public static void toUpperCaseAscii(byte[] bytes) {
toUpperCaseAscii(bytes, 0, bytes.length);
}
public static void toUpperCaseAscii(ByteBuf buf) {
toUpperCaseAscii(buf.array(), buf.readPosition(), buf.readRemaining());
}
public static boolean equalsLowerCaseAscii(byte[] lowerCasePattern, byte[] array, int offset, int size) {
if (lowerCasePattern.length != size)
return false;
for (int i = 0; i < lowerCasePattern.length; i++) {
byte p = lowerCasePattern[i];
assert p < 'A' || p > 'Z';
byte a = array[offset + i];
if (a >= 'A' && a <= 'Z')
a += 'a' - 'A';
if (a != p)
return false;
}
return true;
}
public static boolean equalsUpperCaseAscii(byte[] upperCasePattern, byte[] array, int offset, int size) {
if (upperCasePattern.length != size)
return false;
for (int i = 0; i < upperCasePattern.length; i++) {
byte p = upperCasePattern[i];
assert p < 'a' || p > 'z';
byte a = array[offset + i];
if (a >= 'a' && a <= 'z')
a += 'A' - 'z';
if (a != p)
return false;
}
return true;
}
public static int hashCode(byte[] array, int offset, int size) {
int result = 1;
for (int i = offset; i < offset + size; i++) {
byte a = array[i];
result = 31 * result + a;
}
return result;
}
public static int hashCode(byte[] array) {
return hashCode(array, 0, array.length);
}
public static int hashCode(ByteBuf buf) {
return hashCode(buf.array(), buf.readPosition(), buf.readRemaining());
}
public static int hashCodeLowerCaseAscii(byte[] array, int offset, int size) {
int result = 1;
for (int i = offset; i < offset + size; i++) {
byte a = array[i];
if (a >= 'A' && a <= 'Z')
a += 'a' - 'A';
result = 31 * result + a;
}
return result;
}
public static int hashCodeLowerCaseAscii(byte[] array) {
return hashCodeLowerCaseAscii(array, 0, array.length);
}
public static int hashCodeLowerCaseAscii(ByteBuf buf) {
return hashCodeLowerCaseAscii(buf.array(), buf.readPosition(), buf.readRemaining());
}
public static int hashCodeUpperCaseAscii(byte[] array, int offset, int size) {
int result = 1;
for (int i = offset; i < offset + size; i++) {
byte a = array[i];
if (a >= 'a' && a <= 'z')
a += 'A' - 'a';
result = 31 * result + a;
}
return result;
}
public static int hashCodeUpperCaseAscii(byte[] array) {
return hashCodeUpperCaseAscii(array, 0, array.length);
}
public static int hashCodeUpperCaseAscii(ByteBuf buf) {
return hashCodeUpperCaseAscii(buf.array(), buf.readPosition(), buf.readRemaining());
}
// UTF-8
public static int encodeUtf8(byte[] array, int pos, String string) {
int p = pos;
for (int i = 0; i < string.length(); i++) {
p += encodeUtf8(array, p, string.charAt(i));
}
return p - pos;
}
public static int encodeUtf8(byte[] array, int pos, char c) {
int p = pos;
if (c <= 0x007F) {
array[p++] = (byte) c;
} else if (c > 0x07FF) {
array[p++] = ((byte) (0xE0 | c >> 12 & 0x0F));
array[p++] = ((byte) (0x80 | c >> 6 & 0x3F));
array[p++] = ((byte) (0x80 | c & 0x3F));
} else {
array[p++] = ((byte) (0xC0 | c >> 6 & 0x1F));
array[p++] = ((byte) (0x80 | c & 0x3F));
}
return p - pos;
}
public static void putUtf8(ByteBuf buf, String string) {
int size = encodeUtf8(buf.array(), buf.writePosition(), string);
buf.moveWritePosition(size);
}
public static void putUtf8(ByteBuf buf, char c) {
int size = encodeUtf8(buf.array(), buf.writePosition(), c);
buf.moveWritePosition(size);
}
public static ByteBuf wrapUtf8(String string) {
ByteBuf byteBuffer = ByteBufPool.allocate(string.length() * 3);
int size = encodeUtf8(byteBuffer.array(), 0, string);
byteBuffer.moveWritePosition(size);
return byteBuffer;
}
public static String decodeUtf8(byte[] array, int pos, int len, char[] tmpBuffer) throws ParseException {
int c, charIndex = 0, end = pos + len;
try {
while (pos < end) {
c = array[pos++] & 0xff;
switch ((c >> 4) & 0x0F) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
tmpBuffer[charIndex++] = (char) c;
break;
case 12:
case 13:
tmpBuffer[charIndex++] = (char) ((c & 0x1F) << 6 | array[pos++] & 0x3F);
break;
case 14:
tmpBuffer[charIndex++] = (char) ((c & 0x0F) << 12 | (array[pos++] & 0x3F) << 6 | (array[pos++] & 0x3F));
break;
}
}
if (pos > end) throw READ_PAST_LIMIT;
} catch (ArrayIndexOutOfBoundsException e) {
throw READ_PAST_ARRAY_LENGTH;
}
return new String(tmpBuffer, 0, charIndex);
}
public static String decodeUtf8(byte[] array, int pos, int len) throws ParseException {
return decodeUtf8(array, pos, len, new char[len]);
}
public static String decodeUtf8(ByteBuf buf, char[] tmpBuffer) throws ParseException {
return decodeUtf8(buf.array(), buf.readPosition(), buf.readRemaining(), tmpBuffer);
}
public static String decodeUtf8(ByteBuf buf) throws ParseException {
return decodeUtf8(buf.array(), buf.readPosition(), buf.readRemaining(), new char[buf.readRemaining()]);
}
public static String decodeUtf8(byte[] array) throws ParseException {
return decodeUtf8(array, 0, array.length, new char[array.length]);
}
// Decimal (unsigned)
public static int encodeDecimal(byte[] array, int pos, int value) {
int digits = digits(value);
for (int i = pos + digits - 1; i >= pos; i--) {
int digit = value % 10;
value = value / 10;
array[i] = (byte) ('0' + digit);
}
return digits;
}
public static void putDecimal(ByteBuf buf, int value) {
int digits = encodeDecimal(buf.array(), buf.writePosition(), value);
buf.moveWritePosition(digits);
}
public static ByteBuf wrapDecimal(int value) {
int digits = digits(value);
ByteBuf buf = ByteBufPool.allocate(digits);
byte[] array = buf.array();
for (int i = digits - 1; i >= 0; i--) {
int digit = value % 10;
value = value / 10;
array[i] = (byte) ('0' + digit);
}
return buf;
}
public static int decodeDecimal(byte[] array, int pos, int len) throws ParseException {
int result = 0;
int offsetLeft = trimOffsetLeft(array, pos, len);
pos = pos + offsetLeft;
len = len - offsetLeft;
len = len - trimOffsetRight(array, pos, len);
for (int i = pos; i < pos + len; i++) {
byte b = (byte) (array[i] - '0');
if (b < 0 || b >= 10) {
throw new ParseException("Not a decimal value" + new String(array, pos, len));
}
result = b + result * 10;
if (result < 0) {
throw new ParseException("Bigger than max int value: " + new String(array, pos, len));
}
}
return result;
}
private static int trimOffsetLeft(byte[] array, int pos, int len) {
for (int i = 0; i < len; i++) {
if (array[pos + i] != SP && array[pos + i] != HT)
return i;
}
return 0;
}
private static int trimOffsetRight(byte[] array, int pos, int len) {
for (int i = len - 1; i >= 0; i--) {
if (array[pos + i] != SP && array[pos + i] != HT)
return len - i - 1;
}
return 0;
}
private static int digits(int x) {
int limit = 10;
for (int i = 1; i <= 9; i++) {
if (x < limit)
return i;
limit *= 10;
}
return 10;
}
}