331 lines
11 KiB
Java
331 lines
11 KiB
Java
/*
|
|
* Copyright 2016-2020 chronicle.software
|
|
*
|
|
* https://chronicle.software
|
|
*
|
|
* 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 net.openhft.chronicle.bytes;
|
|
|
|
import net.openhft.chronicle.core.ClassLocal;
|
|
import net.openhft.chronicle.core.Jvm;
|
|
import net.openhft.chronicle.core.Maths;
|
|
import net.openhft.chronicle.core.UnsafeMemory;
|
|
import net.openhft.chronicle.core.io.AbstractReferenceCounted;
|
|
import net.openhft.chronicle.core.io.IORuntimeException;
|
|
import net.openhft.chronicle.core.io.IOTools;
|
|
import net.openhft.chronicle.core.util.StringUtils;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import sun.misc.Unsafe;
|
|
|
|
import java.io.*;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Modifier;
|
|
import java.net.URL;
|
|
import java.nio.BufferUnderflowException;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.ByteOrder;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
import static net.openhft.chronicle.core.io.IOTools.*;
|
|
|
|
@SuppressWarnings("rawtypes")
|
|
public enum BytesUtil {
|
|
;
|
|
private static final int[] NO_INTS = {};
|
|
private static final ClassLocal<int[]> TRIVIALLY_COPYABLE = ClassLocal.withInitial(BytesUtil::isTriviallyCopyable0);
|
|
|
|
/**
|
|
* Is the whole class trivially copyable
|
|
*
|
|
* @param clazz to check
|
|
* @return true if the whole class is trivially copyable
|
|
*/
|
|
public static boolean isTriviallyCopyable(Class clazz) {
|
|
return TRIVIALLY_COPYABLE.get(clazz).length > 0;
|
|
}
|
|
|
|
static int[] isTriviallyCopyable0(Class clazz) {
|
|
if (clazz.isArray()) {
|
|
Class componentType = clazz.getComponentType();
|
|
if (componentType.isPrimitive())
|
|
return new int[]{UnsafeMemory.UNSAFE.arrayBaseOffset(clazz)};
|
|
return NO_INTS;
|
|
}
|
|
List<Field> fields = new ArrayList<>();
|
|
while (clazz != null && clazz != Object.class) {
|
|
Collections.addAll(fields, clazz.getDeclaredFields());
|
|
clazz = clazz.getSuperclass();
|
|
}
|
|
int min = Integer.MAX_VALUE;
|
|
int max = Integer.MIN_VALUE;
|
|
for (Field field : fields) {
|
|
int modifiers = field.getModifiers();
|
|
if (Modifier.isStatic(modifiers))
|
|
continue;
|
|
long offset2 = UnsafeMemory.UNSAFE.objectFieldOffset(field);
|
|
int size = sizeOf(field.getType());
|
|
min = (int) Math.min(min, offset2);
|
|
max = (int) Math.max(max, offset2 + size);
|
|
if (Modifier.isTransient(modifiers))
|
|
return NO_INTS;
|
|
if (!field.getType().isPrimitive())
|
|
return NO_INTS;
|
|
}
|
|
return new int[]{min, max};
|
|
}
|
|
|
|
/**
|
|
* Are all the fields in the range given trivially copyable
|
|
*
|
|
* @param clazz to check
|
|
* @return true if the fields in range are trivially copyable.
|
|
*/
|
|
public static boolean isTriviallyCopyable(Class clazz, int offset, int length) {
|
|
int[] ints = TRIVIALLY_COPYABLE.get(clazz);
|
|
if (ints.length == 0)
|
|
return false;
|
|
return offset >= ints[0] && (ints.length == 1 || offset + length <= ints[1]);
|
|
}
|
|
|
|
public static int[] triviallyCopyableRange(Class clazz) {
|
|
return TRIVIALLY_COPYABLE.get(clazz);
|
|
}
|
|
|
|
private static int sizeOf(Class<?> type) {
|
|
return type == boolean.class || type == byte.class ? 1
|
|
: type == short.class || type == char.class ? 2
|
|
: type == int.class || type == float.class ? 4
|
|
: type == long.class || type == double.class ? 8
|
|
: Unsafe.ARRAY_OBJECT_INDEX_SCALE;
|
|
}
|
|
|
|
public static String findFile( String name) throws FileNotFoundException {
|
|
File file = new File(name);
|
|
URL url = null;
|
|
if (!file.exists()) {
|
|
url = urlFor(name);
|
|
String file2 = url.getFile()
|
|
.replace("target/test-classes", "src/test/resources");
|
|
file = new File(file2);
|
|
}
|
|
if (!file.exists())
|
|
throw new FileNotFoundException(name);
|
|
return file.getAbsolutePath();
|
|
}
|
|
|
|
public static Bytes readFile( String name) throws IOException {
|
|
if (name.startsWith("=")) {
|
|
return Bytes.from(name.substring(1));
|
|
}
|
|
File file = new File(name);
|
|
URL url = null;
|
|
if (!file.exists()) {
|
|
url = urlFor(name);
|
|
file = new File(url.getFile());
|
|
}
|
|
return // name.endsWith(".gz") || !file.exists() || OS.isWindows() ?
|
|
Bytes.wrapForRead(readAsBytes(url == null ? new FileInputStream(file) : open(url)));
|
|
//: MappedFile.readOnly(file).acquireBytesForRead(0);
|
|
|
|
}
|
|
|
|
public static void writeFile(String file, Bytes<byte[]> bytes) throws IOException {
|
|
try (OutputStream os = new FileOutputStream(file)) {
|
|
os.write(bytes.underlyingObject());
|
|
}
|
|
}
|
|
|
|
public static boolean bytesEqual(
|
|
RandomDataInput a, long offset,
|
|
RandomDataInput second, long secondOffset, long len)
|
|
throws BufferUnderflowException {
|
|
long i = 0;
|
|
while (len - i >= 8L) {
|
|
if (a.readLong(offset + i) != second.readLong(secondOffset + i))
|
|
return false;
|
|
i += 8L;
|
|
}
|
|
if (len - i >= 4L) {
|
|
if (a.readInt(offset + i) != second.readInt(secondOffset + i))
|
|
return false;
|
|
i += 4L;
|
|
}
|
|
if (len - i >= 2L) {
|
|
if (a.readShort(offset + i) != second.readShort(secondOffset + i))
|
|
return false;
|
|
i += 2L;
|
|
}
|
|
if (i < len)
|
|
return a.readByte(offset + i) == second.readByte(secondOffset + i);
|
|
return true;
|
|
}
|
|
|
|
public static boolean bytesEqual( CharSequence cs, RandomDataInput bs, long offset, int length) {
|
|
if (cs == null || cs.length() != length)
|
|
return false;
|
|
for (int i = 0; i < length; i++) {
|
|
if (cs.charAt(i) != bs.readUnsignedByte(offset + i))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static boolean equals(Object o1, Object o2) {
|
|
if (o1 == o2) return true;
|
|
if (o1 instanceof CharSequence && o2 instanceof CharSequence)
|
|
return StringUtils.isEqual((CharSequence) o1, (CharSequence) o2);
|
|
return o1 != null && o1.equals(o2);
|
|
}
|
|
|
|
public static int asInt( String str) {
|
|
ByteBuffer bb = ByteBuffer.wrap(str.getBytes(StandardCharsets.ISO_8859_1)).order(ByteOrder.nativeOrder());
|
|
return bb.getInt();
|
|
}
|
|
|
|
public static int stopBitLength(long n) {
|
|
if ((n & ~0x7F) == 0) {
|
|
return 1;
|
|
}
|
|
if ((n & ~0x3FFF) == 0) {
|
|
return 2;
|
|
}
|
|
return BytesInternal.stopBitLength0(n);
|
|
}
|
|
|
|
|
|
public static char[] toCharArray( Bytes bytes) {
|
|
final char[] chars = new char[Maths.toUInt31(bytes.readRemaining())];
|
|
|
|
for (int i = 0; i < bytes.readRemaining(); i++) {
|
|
chars[i] = (char) bytes.readUnsignedByte(i + bytes.readPosition());
|
|
}
|
|
return chars;
|
|
}
|
|
|
|
|
|
public static char[] toCharArray( Bytes bytes, long position, int length) {
|
|
final char[] chars = new char[length];
|
|
|
|
int j = 0;
|
|
for (int i = 0; i < length; i++) {
|
|
chars[j++] = (char) bytes.readUnsignedByte(position + i);
|
|
}
|
|
return chars;
|
|
}
|
|
|
|
public static long readStopBit( StreamingDataInput in) throws IORuntimeException {
|
|
return BytesInternal.readStopBit(in);
|
|
}
|
|
|
|
public static void writeStopBit( StreamingDataOutput out, long n) {
|
|
BytesInternal.writeStopBit(out, n);
|
|
}
|
|
|
|
public static void parseUtf8(
|
|
StreamingDataInput in, Appendable appendable, int utflen)
|
|
throws UTFDataFormatRuntimeException {
|
|
BytesInternal.parseUtf8(in, appendable, true, utflen);
|
|
}
|
|
|
|
public static void appendUtf8( StreamingDataOutput out, CharSequence cs) {
|
|
BytesInternal.appendUtf8(out, cs, 0, cs.length());
|
|
}
|
|
|
|
// used by Chronicle FIX.
|
|
public static void appendBytesFromStart( Bytes bytes, long startPosition, StringBuilder sb) {
|
|
try {
|
|
BytesInternal.parse8bit(startPosition, bytes, sb, (int) (bytes.readPosition() - startPosition));
|
|
sb.append('\u2016');
|
|
sb.append(bytes);
|
|
} catch (IOException e) {
|
|
throw new IORuntimeException(e);
|
|
}
|
|
}
|
|
|
|
public static void readMarshallable( ReadBytesMarshallable marshallable, BytesIn bytes) {
|
|
BytesMarshaller.BYTES_MARSHALLER_CL.get(marshallable.getClass())
|
|
.readMarshallable(marshallable, bytes);
|
|
}
|
|
|
|
public static void writeMarshallable( WriteBytesMarshallable marshallable, BytesOut bytes) {
|
|
BytesMarshaller.BYTES_MARSHALLER_CL.get(marshallable.getClass())
|
|
.writeMarshallable(marshallable, bytes);
|
|
}
|
|
|
|
@Deprecated(/* to be removed in x.22 */)
|
|
public static long utf8Length( CharSequence toWrite) {
|
|
return AppendableUtil.findUtf8Length(toWrite);
|
|
}
|
|
|
|
static String asString(String s, Throwable t) {
|
|
StringWriter sw = new StringWriter();
|
|
sw.append(s).append("\n");
|
|
t.printStackTrace(new PrintWriter(sw));
|
|
return sw.toString();
|
|
}
|
|
|
|
// calls the BackgroundResourceReleaser and AbstractCloseable.assertCloseableClose first.
|
|
public static void checkRegisteredBytes() {
|
|
AbstractReferenceCounted.assertReferencesReleased();
|
|
}
|
|
|
|
public static boolean byteToBoolean(byte b) {
|
|
return b != 0 && b != 'N' && b != 'n';
|
|
}
|
|
|
|
public static long roundUpTo64ByteAlign(long x) {
|
|
return (x + 63L) & ~63L;
|
|
}
|
|
|
|
public static long roundUpTo8ByteAlign(long x) {
|
|
return (x + 7L) & ~7L;
|
|
}
|
|
|
|
public static void read8ByteAlignPadding(Bytes<?> bytes) {
|
|
bytes.readPosition(roundUpTo8ByteAlign(bytes.readPosition()));
|
|
}
|
|
|
|
public static void write8ByteAlignPadding(Bytes<?> bytes) {
|
|
long start = bytes.writePosition();
|
|
long end = roundUpTo8ByteAlign(start);
|
|
bytes.writePosition(end);
|
|
bytes.zeroOut(start, end);
|
|
}
|
|
|
|
public static String toDebugString( RandomDataInput bytes, long start, long maxLength) {
|
|
BytesStore bytes2 = bytes.subBytes(start, maxLength);
|
|
return bytes2.toDebugString(maxLength);
|
|
}
|
|
|
|
@Deprecated(/* to be removed in x.22 */)
|
|
public static boolean unregister(BytesStore bs) {
|
|
IOTools.unmonitor(bs);
|
|
return true;
|
|
}
|
|
|
|
static class WarnUncheckedElasticBytes {
|
|
static {
|
|
Jvm.debug().on(WarnUncheckedElasticBytes.class, "Wrapping elastic bytes with unchecked() will require calling ensureCapacity() as needed!");
|
|
}
|
|
|
|
static void warn() {
|
|
// static block does the work.
|
|
}
|
|
}
|
|
}
|