/*
* 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 com.addthis.hydra.store.util;
import com.addthis.basis.util.LessBytes;
import com.addthis.codec.annotations.FieldConfig;
import com.addthis.codec.codables.BytesCodable;
import com.google.common.primitives.UnsignedBytes;
import javax.annotation.Nonnull;
public final class Raw implements Comparable<Raw>, BytesCodable {
private static final boolean padprintable = System.getProperty("abyss.raw.padprintable", "0").equals("1");
private static boolean longcompare = System.getProperty("abyss.raw.longcompare", "0").equals("1");
private static final boolean showinfo = System.getProperty("abyss.raw.showinfo", "0").equals("1");
static {
if (showinfo) {
System.out.println("Raw padprintable=" + padprintable + " longcompare=" + longcompare);
}
}
public static void useLongCompare(boolean b) {
longcompare = b;
}
public static final Raw get(String s) {
return new Raw(s);
}
public static final Raw get(char[] c) {
return new Raw(LessBytes.toBytes(c));
}
public static final Raw get(byte[] b) {
return new Raw(b);
}
private Raw(String s) {
this.raw = LessBytes.toBytes(s);
}
private Raw(byte[] b) {
this.raw = b;
}
public Raw() {
}
@FieldConfig(codable = true)
private byte[] raw;
private int hashcode;
private long[] compare;
@Override
public int hashCode() {
if (hashcode == 0) {
int h = 0;
byte[] val = raw;
for (int i = 0, l = val.length; i < l; i++) {
h = 31 * h + val[i];
}
hashcode = h;
}
return hashcode;
}
@Override
public boolean equals(Object o) {
if (o != null && o.getClass() == Raw.class) {
Raw ro = (Raw) o;
return (o == this) || revsame(raw, ro.raw);
} else {
return false;
}
}
// compare in reverse
private static boolean revsame(byte[] a, byte[] b) {
if (a == null || b == null) {
return a == b;
}
if (a.length != b.length) {
return false;
}
for (int l = a.length - 1, i = l; i >= 0; i--) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
// compare forward
private static boolean fwdsame(byte[] a, byte[] b) {
if (a.length != b.length) {
return false;
}
for (int l = a.length, i = 0; i < l; i++) {
if ((int) a[i] != (int) b[i]) {
return false;
}
}
return true;
}
public String toPrintable() {
StringBuilder sb = new StringBuilder();
for (int i = 0; raw != null && i < raw.length; i++) {
if (raw[i] < 32 || raw[i] > 126) {
sb.append("(" + ((int) (raw[i] & 0xff)) + ")");
} else {
sb.append(((char) (raw[i] & 0xff)));
}
if (padprintable) {
sb.append(' ');
}
}
return sb.toString();
}
public String toString() {
return LessBytes.toString(raw);
}
public char[] toChars() {
return LessBytes.toChars(raw);
}
public byte[] toBytes() {
return raw;
}
public Raw cat(byte[] b) {
return get(LessBytes.cat(raw, b));
}
public Raw cat(String b) {
return get(LessBytes.cat(raw, LessBytes.toBytes(b)));
}
public Raw cat(Raw ab) {
return cat(ab.raw);
}
@Override public int compareTo(Raw o) {
if (longcompare) {
return compare(getLongs(), o.getLongs());
}
return UnsignedBytes.lexicographicalComparator().compare(raw, o.raw);
}
private long[] getLongs() {
if (compare == null) {
compare = bytesToLong(raw);
}
return compare;
}
private int compare(long[] a, long[] b) {
for (int al = a.length, bl = b.length, i = 0; i < al; i++) {
if (bl <= i) {
return 1;
}
long val = a[i] - b[i];
if (val == 0) {
continue;
}
if (val < 0) {
return -1;
}
return 1;
}
return a.length == b.length ? 0 : -1;
}
private long[] bytesToLong(byte[] data) {
if (data.length % 8 != 0) {
data = LessBytes.cat(data, new byte[8 - (data.length % 8)]);
}
long[] l = new long[data.length / 8];
for (int i = 0; i < l.length; i++) {
int off = i * 8;
l[i] = (long) (
((data[off] & 0xffL) << 56) | ((data[off + 1] & 0xffL) << 48) |
((data[off + 2] & 0xffL) << 40) | ((data[off + 3] & 0xffL) << 32) |
((data[off + 4] & 0xffL) << 24) | ((data[off + 5] & 0xffL) << 16) |
((data[off + 6] & 0xffL) << 8) | ((data[off + 7] & 0xffL)));
}
return l;
}
@Override
public byte[] bytesEncode(long version) {
return toBytes();
}
@Override
public void bytesDecode(byte[] b, long version) {
this.raw = b;
}
}