ByteString arrayOffset method

Motivation:
The ByteString class currently assumes the underlying array will be a complete representation of data. This is limiting as it does not allow a subsection of another array to be used. The forces copy operations to take place to compensate for the lack of API support.

Modifications:
- add arrayOffset method to ByteString
- modify all ByteString and AsciiString methods that loop over or index into the underlying array to use this offset
- update all code that uses ByteString.array to ensure it accounts for the offset
- add unit tests to test the implementation respects the offset

Result:
ByteString and AsciiString can represent a sub region of a byte[].
This commit is contained in:
Scott Mitchell 2015-04-15 13:55:42 -07:00
parent 70a2608325
commit f812180c2d
10 changed files with 592 additions and 326 deletions

View File

@ -610,7 +610,7 @@ public final class ByteBufUtil {
+ length + ") <= srcLen(" + thisLen + ')');
}
checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx, length);
checkNotNull(dst, "dst").setBytes(dstIdx, src.array(), srcIdx + src.arrayOffset(), length);
}
/**
@ -628,7 +628,7 @@ public final class ByteBufUtil {
+ length + ") <= srcLen(" + thisLen + ')');
}
checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx, length);
checkNotNull(dst, "dst").writeBytes(src.array(), srcIdx + src.arrayOffset(), length);
}
static final class ThreadLocalUnsafeDirectByteBuf extends UnpooledUnsafeDirectByteBuf {

View File

@ -109,7 +109,10 @@ public class DefaultHttp2HeadersEncoder implements Http2HeadersEncoder, Http2Hea
}
private void encodeHeader(ByteString key, ByteString value, OutputStream stream) throws IOException {
encoder.encodeHeader(stream, key.array(), value.array(), sensitivityDetector.isSensitive(key, value));
encoder.encodeHeader(stream,
key.isEntireArrayUsed() ? key.array() : new ByteString(key, true).array(),
value.isEntireArrayUsed() ? value.array() : new ByteString(value, true).array(),
sensitivityDetector.isSensitive(key, value));
}
/**

View File

@ -377,7 +377,7 @@ public final class HttpUtil {
throw streamError(streamId, PROTOCOL_ERROR,
"Invalid HTTP/2 header '%s' encountered in translation to HTTP/1.x", translatedName);
} else {
output.add(new AsciiString(translatedName.array(), false), new AsciiString(value.array(), false));
output.add(new AsciiString(translatedName, false), new AsciiString(value, false));
}
}
return true;

View File

@ -24,7 +24,6 @@ import io.netty.util.internal.PlatformDependent;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Pattern;
@ -69,11 +68,10 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
int length2 = o2.length();
int minLength = Math.min(length1, length2);
if (a1 != null && a2 != null) {
byte[] thisValue = a1.value;
byte[] thatValue = a2.value;
for (int i = 0; i < minLength; i++) {
byte v1 = thisValue[i];
byte v2 = thatValue[i];
final int a1Len = minLength + a1.arrayOffset();
for (int i = a1.arrayOffset(), j = a2.arrayOffset(); i < a1Len; i++, j++) {
byte v1 = a1.value[i];
byte v2 = a2.value[j];
if (v1 == v2) {
continue;
}
@ -85,20 +83,18 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
}
}
} else if (a1 != null) {
byte[] thisValue = a1.value;
for (int i = 0; i < minLength; i++) {
int c1 = toLowerCase(thisValue[i]);
int c2 = toLowerCase(o2.charAt(i));
for (int i = a1.arrayOffset(), j = 0; j < minLength; i++, j++) {
int c1 = toLowerCase(a1.value[i]);
int c2 = toLowerCase(o2.charAt(j));
result = c1 - c2;
if (result != 0) {
return result;
}
}
} else if (a2 != null) {
byte[] thatValue = a2.value;
for (int i = 0; i < minLength; i++) {
for (int i = 0, j = a2.arrayOffset(); i < minLength; i++, j++) {
int c1 = toLowerCase(o1.charAt(i));
int c2 = toLowerCase(thatValue[i]);
int c2 = toLowerCase(a2.value[j]);
result = c1 - c2;
if (result != 0) {
return result;
@ -134,31 +130,28 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
int length2 = o2.length();
int minLength = Math.min(length1, length2);
if (a1 != null && a2 != null) {
byte[] thisValue = a1.value;
byte[] thatValue = a2.value;
for (int i = 0; i < minLength; i++) {
byte v1 = thisValue[i];
byte v2 = thatValue[i];
final int a1Len = minLength + a1.arrayOffset();
for (int i = a1.arrayOffset(), j = a2.arrayOffset(); i < a1Len; i++, j++) {
byte v1 = a1.value[i];
byte v2 = a2.value[j];
result = v1 - v2;
if (result != 0) {
return result;
}
}
} else if (a1 != null) {
byte[] thisValue = a1.value;
for (int i = 0; i < minLength; i++) {
int c1 = thisValue[i];
int c2 = o2.charAt(i);
for (int i = a1.arrayOffset(), j = 0; j < minLength; i++, j++) {
int c1 = a1.value[i];
int c2 = o2.charAt(j);
result = c1 - c2;
if (result != 0) {
return result;
}
}
} else if (a2 != null) {
byte[] thatValue = a2.value;
for (int i = 0; i < minLength; i++) {
for (int i = 0, j = a2.arrayOffset(); i < minLength; i++, j++) {
int c1 = o1.charAt(i);
int c2 = thatValue[i];
int c2 = a2.value[j];
result = c1 - c2;
if (result != 0) {
return result;
@ -179,6 +172,16 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
}
};
/**
* Factory which uses the {@link #AsciiString(byte[], int, int, boolean)} constructor.
*/
private static final ByteStringFactory DEFAULT_FACTORY = new ByteStringFactory() {
@Override
public ByteString newInstance(byte[] value, int start, int length, boolean copy) {
return new AsciiString(value, start, length, copy);
}
};
/**
* Returns the case-insensitive hash code of the specified string. Note that this method uses the same hashing
* algorithm with {@link #hashCode()} so that you can put both {@link AsciiString}s and arbitrary
@ -186,15 +189,32 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
*/
public static int caseInsensitiveHashCode(CharSequence value) {
if (value instanceof AsciiString) {
return value.hashCode();
try {
ByteProcessor processor = new ByteProcessor() {
private int hash;
@Override
public boolean process(byte value) throws Exception {
hash = hash * HASH_CODE_PRIME ^ toLowerCase(value) & HASH_CODE_PRIME;
return true;
}
@Override
public int hashCode() {
return hash;
}
};
((AsciiString) value).forEachByte(processor);
return processor.hashCode();
} catch (Exception e) {
PlatformDependent.throwException(e);
}
}
int hash = 0;
final int end = value.length();
for (int i = 0; i < end; i++) {
hash = hash * HASH_CODE_PRIME ^ value.charAt(i) & HASH_CODE_PRIME;
hash = hash * HASH_CODE_PRIME ^ toLowerCase(value.charAt(i)) & HASH_CODE_PRIME;
}
return hash;
}
@ -267,20 +287,16 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
super(value, copy);
}
public AsciiString(byte[] value, int start, int length) {
super(value, start, length);
}
public AsciiString(byte[] value, int start, int length, boolean copy) {
super(value, start, length, copy);
}
public AsciiString(ByteBuffer value) {
super(value);
public AsciiString(ByteString value, boolean copy) {
super(value, copy);
}
public AsciiString(ByteBuffer value, int start, int length) {
super(value, start, length);
public AsciiString(ByteBuffer value) {
super(value);
}
public AsciiString(ByteBuffer value, int start, int length, boolean copy) {
@ -314,8 +330,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
+ ") <= " + "value.length(" + value.length() + ')');
}
for (int i = 0; i < length; i++) {
this.value[i] = c2b(value.charAt(start + i));
for (int i = 0, j = start; i < length; i++, j++) {
this.value[i] = c2b(value.charAt(j));
}
}
@ -324,6 +340,12 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return b2c(byteAt(index));
}
@Override
public void arrayChanged() {
string = null;
super.arrayChanged();
}
private static byte c2b(char c) {
if (c > MAX_CHAR_VALUE) {
return '?';
@ -381,6 +403,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
* positive integer if this string is after the specified string.
* @throws NullPointerException if {@code string} is {@code null}.
*/
@Override
public int compareTo(CharSequence string) {
if (this == string) {
return 0;
@ -390,9 +413,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
int length1 = length();
int length2 = string.length();
int minLength = Math.min(length1, length2);
byte[] value = this.value;
for (int i = 0, j = 0; j < minLength; i++, j++) {
result = b2c(value[i]) - string.charAt(j);
for (int i = 0, j = arrayOffset(); i < minLength; i++, j++) {
result = b2c(value[j]) - string.charAt(i);
if (result != 0) {
return result;
}
@ -438,9 +460,9 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return that;
}
byte[] newValue = Arrays.copyOf(value, thisLen + thatLen);
System.arraycopy(that.value, 0, newValue, thisLen, thatLen);
byte[] newValue = new byte[thisLen + thatLen];
System.arraycopy(value, arrayOffset(), newValue, 0, thisLen);
System.arraycopy(that.value, that.arrayOffset(), newValue, thisLen, thatLen);
return new AsciiString(newValue, false);
}
@ -448,9 +470,9 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return new AsciiString(string);
}
int newLen = thisLen + thatLen;
byte[] newValue = Arrays.copyOf(value, newLen);
for (int i = thisLen, j = 0; i < newLen; i++, j++) {
byte[] newValue = new byte[thisLen + thatLen];
System.arraycopy(value, arrayOffset(), newValue, 0, thisLen);
for (int i = thisLen, j = 0; i < newValue.length; i++, j++) {
newValue[i] = c2b(string.charAt(j));
}
@ -491,8 +513,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return false;
}
for (int i = 0; i < thisLen; i++) {
char c1 = b2c(value[i]);
for (int i = 0, j = arrayOffset(); i < thisLen; i++, j++) {
char c1 = b2c(value[j]);
char c2 = string.charAt(i);
if (c1 != c2 && toLowerCase(c1) != toLowerCase(c2)) {
return false;
@ -521,8 +543,13 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return EmptyArrays.EMPTY_CHARS;
}
if (start < 0 || length > length() - start) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
+ length + ") <= srcLen(" + length() + ')');
}
final char[] buffer = new char[length];
for (int i = 0, j = start; i < length; i++, j++) {
for (int i = 0, j = start + arrayOffset(); i < length; i++, j++) {
buffer[i] = b2c(value[j]);
}
return buffer;
@ -541,22 +568,30 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
throw new NullPointerException("dst");
}
final int thisLen = value.length;
if (srcIdx < 0 || length > thisLen - srcIdx) {
if (srcIdx < 0 || length > length() - srcIdx) {
throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
+ length + ") <= srcLen(" + thisLen + ')');
+ length + ") <= srcLen(" + length() + ')');
}
final int dstEnd = dstIdx + length;
for (int i = srcIdx, j = dstIdx; j < dstEnd; i++, j++) {
dst[j] = b2c(value[i]);
for (int i = dstIdx, j = srcIdx + arrayOffset(); i < dstEnd; i++, j++) {
dst[i] = b2c(value[j]);
}
}
@Override
public AsciiString subSequence(int start) {
return subSequence(start, length());
}
@Override
public AsciiString subSequence(int start, int end) {
return (AsciiString) super.subSequence(start, end);
return subSequence(start, end, true);
}
@Override
public AsciiString subSequence(int start, int end, boolean copy) {
return (AsciiString) super.subSequence(start, end, copy, DEFAULT_FACTORY);
}
/**
@ -587,7 +622,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
start = 0;
}
final int thisLen = value.length;
final int thisLen = length();
int subCount = subString.length();
if (subCount <= 0) {
@ -598,6 +633,9 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
}
final char firstChar = subString.charAt(0);
if (firstChar > MAX_CHAR_VALUE) {
return -1;
}
ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar);
try {
for (;;) {
@ -606,7 +644,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return -1; // handles subCount > count || start >= count
}
int o1 = i, o2 = 0;
while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
while (++o2 < subCount && b2c(value[++o1 + arrayOffset()]) == subString.charAt(o2)) {
// Intentionally empty
}
if (o2 == subCount) {
@ -645,7 +683,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
* @throws NullPointerException if {@code subString} is {@code null}.
*/
public int lastIndexOf(CharSequence subString, int start) {
final int thisLen = value.length;
final int thisLen = length();
final int subCount = subString.length();
if (subCount > thisLen || start < 0) {
@ -660,6 +698,9 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
// count and subCount are both >= 1
final char firstChar = subString.charAt(0);
if (firstChar > MAX_CHAR_VALUE) {
return -1;
}
ByteProcessor IndexOfVisitor = new IndexOfProcessor((byte) firstChar);
try {
for (;;) {
@ -668,7 +709,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return -1;
}
int o1 = i, o2 = 0;
while (++o2 < subCount && b2c(value[++o1]) == subString.charAt(o2)) {
while (++o2 < subCount && b2c(value[++o1 + arrayOffset()]) == subString.charAt(o2)) {
// Intentionally empty
}
if (o2 == subCount) {
@ -702,7 +743,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return false;
}
final int thisLen = value.length;
final int thisLen = length();
if (thisStart < 0 || thisLen - thisStart < length) {
return false;
}
@ -711,9 +752,9 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return true;
}
final int thisEnd = thisStart + length;
for (int i = thisStart, j = start; i < thisEnd; i++, j++) {
if (b2c(value[i]) != string.charAt(j)) {
final int thatEnd = start + length;
for (int i = start, j = thisStart + arrayOffset(); i < thatEnd; i++, j++) {
if (b2c(value[j]) != string.charAt(i)) {
return false;
}
}
@ -741,7 +782,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
throw new NullPointerException("string");
}
final int thisLen = value.length;
final int thisLen = length();
if (thisStart < 0 || length > thisLen - thisStart) {
return false;
}
@ -749,7 +790,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return false;
}
int thisEnd = thisStart + length;
thisStart += arrayOffset();
final int thisEnd = thisStart + length;
while (thisStart < thisEnd) {
char c1 = b2c(value[thisStart++]);
char c2 = string.charAt(start++);
@ -784,15 +826,14 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return this;
}
final int count = value.length;
final byte newCharByte = c2b(newChar);
byte[] buffer = new byte[count];
for (int i = 0, j = 0; i < count; i++, j++) {
byte b = value[i];
byte[] buffer = new byte[length()];
for (int i = 0, j = arrayOffset(); i < buffer.length; i++, j++) {
byte b = value[j];
if (b == oldCharByte) {
b = newCharByte;
}
buffer[j] = b;
buffer[i] = b;
}
return new AsciiString(buffer, false);
@ -831,7 +872,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
public AsciiString toLowerCase() {
boolean lowercased = true;
int i, j;
for (i = 0; i < value.length; ++i) {
final int len = length() + arrayOffset();
for (i = arrayOffset(); i < len; ++i) {
byte b = value[i];
if (b >= 'A' && b <= 'Z') {
lowercased = false;
@ -844,9 +886,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return this;
}
final int length = value.length;
final byte[] newValue = new byte[length];
for (i = 0, j = 0; i < length; ++i, ++j) {
final byte[] newValue = new byte[length()];
for (i = 0, j = arrayOffset(); i < newValue.length; ++i, ++j) {
newValue[i] = toLowerCase(value[j]);
}
@ -861,7 +902,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
public AsciiString toUpperCase() {
boolean uppercased = true;
int i, j;
for (i = 0; i < value.length; ++i) {
final int len = length() + arrayOffset();
for (i = arrayOffset(); i < len; ++i) {
byte b = value[i];
if (b >= 'a' && b <= 'z') {
uppercased = false;
@ -874,9 +916,8 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
return this;
}
final int length = value.length;
final byte[] newValue = new byte[length];
for (i = 0, j = 0; i < length; ++i, ++j) {
final byte[] newValue = new byte[length()];
for (i = 0, j = arrayOffset(); i < newValue.length; ++i, ++j) {
newValue[i] = toUpperCase(value[j]);
}
@ -889,7 +930,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
* @return a new string with characters {@code <= \\u0020} removed from the beginning and the end.
*/
public AsciiString trim() {
int start = 0, last = value.length;
int start = arrayOffset(), last = arrayOffset() + length();
int end = last;
while (start <= end && value[start] <= ' ') {
start++;
@ -969,13 +1010,13 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
final List<AsciiString> res = new ArrayList<AsciiString>();
int start = 0;
final int length = value.length;
final int length = length();
for (int i = start; i < length; i++) {
if (charAt(i) == delim) {
if (start == i) {
res.add(EMPTY_STRING);
} else {
res.add(new AsciiString(value, start, i - start, false));
res.add(new AsciiString(value, start + arrayOffset(), i - start, false));
}
start = i + 1;
}
@ -986,7 +1027,7 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
} else {
if (start != length) {
// Add the last element if it's not empty.
res.add(new AsciiString(value, start, length - start, false));
res.add(new AsciiString(value, start + arrayOffset(), length - start, false));
} else {
// Truncate trailing empty elements.
for (int i = res.size() - 1; i >= 0; i--) {
@ -1014,72 +1055,4 @@ public final class AsciiString extends ByteString implements CharSequence, Compa
}
return indexOf(cs) >= 0;
}
public int parseInt() {
return parseAsciiInt();
}
public int parseInt(int radix) {
return parseAsciiInt(radix);
}
public int parseInt(int start, int end) {
return parseAsciiInt(start, end);
}
public int parseInt(int start, int end, int radix) {
return parseAsciiInt(start, end, radix);
}
public long parseLong() {
return parseAsciiLong();
}
public long parseLong(int radix) {
return parseAsciiLong(radix);
}
public long parseLong(int start, int end) {
return parseAsciiLong(start, end);
}
public long parseLong(int start, int end, int radix) {
return parseAsciiLong(start, end, radix);
}
public char parseChar(int start) {
return charAt(start);
}
public short parseShort() {
return parseAsciiShort();
}
public short parseShort(int radix) {
return parseAsciiShort(radix);
}
public short parseShort(int start, int end) {
return parseAsciiShort(start, end);
}
public short parseShort(int start, int end, int radix) {
return parseAsciiShort(start, end, radix);
}
public float parseFloat() {
return parseAsciiFloat();
}
public float parseFloat(int start, int end) {
return parseAsciiFloat(start, end);
}
public double parseDouble() {
return parseAsciiDouble();
}
public double parseDouble(int start, int end) {
return parseAsciiDouble(start, end);
}
}

View File

@ -47,7 +47,7 @@ public class ByteString {
int length1 = o1.length();
int length2 = o2.length();
int minLength = Math.min(length1, length2);
for (int i = 0, j = 0; j < minLength; i++, j++) {
for (int i = o1.offset, j = o2.offset; i < minLength; i++, j++) {
result = o1.value[i] - o2.value[j];
if (result != 0) {
return result;
@ -57,13 +57,41 @@ public class ByteString {
return length1 - length2;
}
};
/**
* Allows sub classes to take advantage of {@link ByteString} operations which need to generate new
* ByteString objects.
*/
protected interface ByteStringFactory {
ByteString newInstance(byte[] value, int start, int length, boolean copy);
}
/**
* Factory which uses the {@link #ByteString(byte[], int, int, boolean)} constructor.
*/
private static final ByteStringFactory DEFAULT_FACTORY = new ByteStringFactory() {
@Override
public ByteString newInstance(byte[] value, int start, int length, boolean copy) {
return new ByteString(value, start, length, copy);
}
};
public static final ByteString EMPTY_STRING = new ByteString(0);
protected static final int HASH_CODE_PRIME = 31;
protected static final int HASH_CODE_PRIME = 31;;
/**
* If this value is modified outside the constructor then call {@link #arrayChanged()}.
*/
protected final byte[] value;
/**
* Offset into {@link #value} that all operations should use when acting upon {@link #value}.
*/
private final int offset;
/**
* Length in bytes for {@link #value} that we care about. This is independent from {@code value.length}
* because we may be looking at a subsection of the array.
*/
private final int length;
/**
* The hash code is cached after it is first computed. It can be reset with {@link #arrayChanged()}.
*/
@ -72,7 +100,11 @@ public class ByteString {
/**
* Used for classes which extend this class and want to initialize the {@link #value} array by them selves.
*/
ByteString(int length) { value = new byte[length]; }
ByteString(int length) {
value = new byte[length];
offset = 0;
this.length = length;
}
/**
* Initialize this byte string based upon a byte array. A copy will be made.
@ -86,24 +118,13 @@ public class ByteString {
* {@code copy} determines if a copy is made or the array is shared.
*/
public ByteString(byte[] value, boolean copy) {
if (copy) {
this.value = checkNotNull(value, "value").clone();
} else {
this.value = checkNotNull(value, "value");
}
}
/**
* Initialize this byte string based upon a range of a byte array. A copy will be made.
*/
public ByteString(byte[] value, int start, int length) {
this(value, start, length, true);
this(value, 0, checkNotNull(value, "value").length, copy);
}
/**
* Construct a new {@link BinaryString} object from a {@code byte[]} array.
* @param copy {@code true} then a copy of the memory will be made. {@code false} the underlying memory
* will be shared. If this shared memory changes then {@link #arrayChanged()} must be called.
* will be shared.
*/
public ByteString(byte[] value, int start, int length, boolean copy) {
if (start < 0 || start > checkNotNull(value, "value").length - length) {
@ -111,10 +132,34 @@ public class ByteString {
+ ") <= " + "value.length(" + value.length + ')');
}
if (copy || start != 0 || length != value.length) {
if (copy) {
this.value = Arrays.copyOfRange(value, start, start + length);
offset = 0;
this.length = length;
} else {
this.value = value;
this.offset = start;
this.length = length;
}
}
/**
* Create a new object which is equal to {@code value}.
* @param value The object to replicate.
* @param copy {@code true} mean the underlying storage will be copied.
* {@code false} means the underlying storage will be shared.
*/
public ByteString(ByteString value, boolean copy) {
checkNotNull(value, "value");
this.length = value.length();
this.hash = value.hash;
if (copy) {
this.value = new byte[length];
System.arraycopy(value.array(), value.arrayOffset(), this.value, 0, length);
this.offset = 0;
} else {
this.value = value.array();
this.offset = value.offset;
}
}
@ -123,32 +168,57 @@ public class ByteString {
* The copy will start at {@link ByteBuffer#position()} and copy {@link ByteBuffer#remaining()} bytes.
*/
public ByteString(ByteBuffer value) {
this.value = getBytes(value);
}
/**
* Create a copy of the underlying storage from {@link value}.
* The copy will start at {@code start} and copy {@code length} bytes.
*/
public ByteString(ByteBuffer value, int start, int length) {
this.value = getBytes(value, start, length, true);
this(value, true);
}
/**
* Initialize a {@link ByteString} based upon the underlying storage from {@link value}.
* The copy will start at {@code start} and copy {@code length} bytes.
* if {@code copy} is true a copy will be made of the memory.
* if {@code copy} is false the underlying storage will be shared, if possible.
* There is a potential to share the underlying array storage if {@link ByteBuffer#hasArray()} is {@code true}.
* if {@code copy} is {@code true} a copy will be made of the memory.
* if {@code copy} is {@code false} the underlying storage will be shared, if possible.
*/
public ByteString(ByteBuffer value, boolean copy) {
this(value, value.position(), checkNotNull(value, "value").remaining(), copy);
}
/**
* Initialize a {@link ByteString} based upon the underlying storage from {@link value}.
* There is a potential to share the underlying array storage if {@link ByteBuffer#hasArray()} is {@code true}.
* if {@code copy} is {@code true} a copy will be made of the memory.
* if {@code copy} is {@code false} the underlying storage will be shared, if possible.
*/
public ByteString(ByteBuffer value, int start, int length, boolean copy) {
this.value = getBytes(value, start, length, copy);
if (start < 0 || length > checkNotNull(value, "value").capacity() - start) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
+ ") <= " + "value.capacity(" + value.capacity() + ')');
}
if (value.hasArray()) {
if (copy) {
final int bufferOffset = value.arrayOffset() + start;
this.value = Arrays.copyOfRange(value.array(), bufferOffset, bufferOffset + length);
offset = 0;
this.length = length;
} else {
this.value = value.array();
this.offset = start;
this.length = length;
}
} else {
this.value = new byte[length];
int oldPos = value.position();
value.get(this.value, 0, length);
value.position(oldPos);
this.offset = 0;
this.length = length;
}
}
/**
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
*/
public ByteString(char[] value, Charset charset) {
this.value = getBytes(value, charset);
this(value, charset, 0, checkNotNull(value, "value").length);
}
/**
@ -156,80 +226,6 @@ public class ByteString {
* The copy will start at index {@code start} and copy {@code length} bytes.
*/
public ByteString(char[] value, Charset charset, int start, int length) {
this.value = getBytes(value, charset, start, length);
}
/**
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
*/
public ByteString(CharSequence value, Charset charset) {
this.value = getBytes(value, charset);
}
/**
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
* The copy will start at index {@code start} and copy {@code length} bytes.
*/
public ByteString(CharSequence value, Charset charset, int start, int length) {
this.value = getBytes(value, charset, start, length);
}
/**
* Create a copy of the underlying storage from {@link value} into a byte array.
* The copy will start at {@link ByteBuffer#position()} and copy {@link ByteBuffer#remaining()} bytes.
*/
private static byte[] getBytes(ByteBuffer value) {
return getBytes(value, value.position(), checkNotNull(value, "value").remaining());
}
/**
* Create a copy of the underlying storage from {@link value} into a byte array.
* The copy will start at {@code start} and copy {@code length} bytes.
*/
private static byte[] getBytes(ByteBuffer value, int start, int length) {
return getBytes(value, start, length, true);
}
/**
* Return an array of the underlying storage from {@link value} into a byte array.
* The copy will start at {@code start} and copy {@code length} bytes.
* if {@code copy} is true a copy will be made of the memory.
* if {@code copy} is false the underlying storage will be shared, if possible.
*/
private static byte[] getBytes(ByteBuffer value, int start, int length, boolean copy) {
if (start < 0 || length > checkNotNull(value, "value").capacity() - start) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
+ ") <= " + "value.capacity(" + value.capacity() + ')');
}
if (value.hasArray()) {
if (copy || start != 0 || length != value.capacity()) {
int baseOffset = value.arrayOffset() + start;
return Arrays.copyOfRange(value.array(), baseOffset, baseOffset + length);
} else {
return value.array();
}
}
byte[] v = new byte[length];
int oldPos = value.position();
value.get(v, 0, length);
value.position(oldPos);
return v;
}
/**
* Create a copy of {@link value} into a byte array using the encoding type of {@code charset}.
*/
private static byte[] getBytes(char[] value, Charset charset) {
return getBytes(value, charset, 0, checkNotNull(value, "value").length);
}
/**
* Create a copy of {@link value} into a byte array using the encoding type of {@code charset}.
* The copy will start at index {@code start} and copy {@code length} bytes.
*/
private static byte[] getBytes(char[] value, Charset charset, int start, int length) {
if (start < 0 || length > checkNotNull(value, "value").length - start) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
+ ") <= " + "length(" + length + ')');
@ -239,22 +235,24 @@ public class ByteString {
CharsetEncoder encoder = CharsetUtil.getEncoder(charset);
ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length));
encoder.encode(cbuf, nativeBuffer, true);
final int offset = nativeBuffer.arrayOffset();
return Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position());
final int bufferOffset = nativeBuffer.arrayOffset();
this.value = Arrays.copyOfRange(nativeBuffer.array(), bufferOffset, bufferOffset + nativeBuffer.position());
this.offset = 0;
this.length = this.value.length;
}
/**
* Create a copy of {@link value} into a byte array using the encoding type of {@code charset}.
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
*/
private static byte[] getBytes(CharSequence value, Charset charset) {
return getBytes(value, charset, 0, checkNotNull(value, "value").length());
public ByteString(CharSequence value, Charset charset) {
this(value, charset, 0, checkNotNull(value, "value").length());
}
/**
* Create a copy of {@link value} into a byte array using the encoding type of {@code charset}.
* Create a copy of {@link value} into a {@link ByteString} using the encoding type of {@code charset}.
* The copy will start at index {@code start} and copy {@code length} bytes.
*/
private static byte[] getBytes(CharSequence value, Charset charset, int start, int length) {
public ByteString(CharSequence value, Charset charset, int start, int length) {
if (start < 0 || length > checkNotNull(value, "value").length() - start) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
+ ") <= " + "length(" + value.length() + ')');
@ -265,7 +263,9 @@ public class ByteString {
ByteBuffer nativeBuffer = ByteBuffer.allocate((int) (encoder.maxBytesPerChar() * length));
encoder.encode(cbuf, nativeBuffer, true);
final int offset = nativeBuffer.arrayOffset();
return Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position());
this.value = Arrays.copyOfRange(nativeBuffer.array(), offset, offset + nativeBuffer.position());
this.offset = 0;
this.length = this.value.length;
}
/**
@ -275,7 +275,7 @@ public class ByteString {
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/
public final int forEachByte(ByteProcessor visitor) throws Exception {
return forEachByte(0, value.length, visitor);
return forEachByte(0, length(), visitor);
}
/**
@ -286,14 +286,15 @@ public class ByteString {
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/
public final int forEachByte(int index, int length, ByteProcessor visitor) throws Exception {
if (index < 0 || length > value.length - index) {
if (index < 0 || length > length() - index) {
throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length
+ ") <= " + "length(" + value.length + ')');
+ ") <= " + "length(" + length() + ')');
}
for (int i = index; i < length; ++i) {
final int len = offset + length;
for (int i = offset + index; i < len; ++i) {
if (!visitor.process(value[i])) {
return i;
return i - offset;
}
}
return -1;
@ -317,36 +318,42 @@ public class ByteString {
* The last-visited index If the {@link ByteProcessor#process(byte)} returned {@code false}.
*/
public final int forEachByteDesc(int index, int length, ByteProcessor visitor) throws Exception {
if (index < 0 || length > value.length - index) {
if (index < 0 || length > length() - index) {
throw new IndexOutOfBoundsException("expected: " + "0 <= index(" + index + ") <= start + length(" + length
+ ") <= " + "length(" + value.length + ')');
+ ") <= " + "length(" + length() + ')');
}
for (int i = index + length - 1; i >= index; --i) {
final int end = offset + index;
for (int i = offset + index + length - 1; i >= end; --i) {
if (!visitor.process(value[i])) {
return i;
return i - offset;
}
}
return -1;
}
public final byte byteAt(int index) {
return value[index];
// We must do a range check here to enforce the access does not go outside our sub region of the array.
// We rely on the array access itself to pick up the array out of bounds conditions
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException("index: " + index + " must be in the range [0," + length + ")");
}
return value[index + offset];
}
public final boolean isEmpty() {
return value.length == 0;
return length == 0;
}
public final int length() {
return value.length;
return length;
}
/**
* During normal use cases the {@link ByteString} should be immutable, but if the underlying array is shared,
* and changes then this needs to be called.
*/
public final void arrayChanged() {
public void arrayChanged() {
hash = 0;
}
@ -354,11 +361,30 @@ public class ByteString {
* This gives direct access to the underlying storage array.
* The {@link #toByteArray()} should be preferred over this method.
* If the return value is changed then {@link #arrayChanged()} must be called.
* @see #arrayOffset()
* @see #isEntireArrayUsed()
*/
public final byte[] array() {
return value;
}
/**
* The offset into {@link #array()} for which data for this ByteString begins.
* @see #array()
* @see #isEntireArrayUsed()
*/
public final int arrayOffset() {
return offset;
}
/**
* Determine if the storage represented by {@link #array()} is entirely used.
* @see #array()
*/
public final boolean isEntireArrayUsed() {
return offset == 0 && length == value.length;
}
/**
* Converts this string to a byte array.
*/
@ -371,7 +397,7 @@ public class ByteString {
* The subset is defined by the range [{@code start}, {@code end}).
*/
public final byte[] toByteArray(int start, int end) {
return Arrays.copyOfRange(value, start, end);
return Arrays.copyOfRange(value, start + offset, end + offset);
}
/**
@ -383,47 +409,79 @@ public class ByteString {
* @param length the number of characters to copy.
*/
public final void copy(int srcIdx, byte[] dst, int dstIdx, int length) {
final int thisLen = value.length;
if (srcIdx < 0 || length > thisLen - srcIdx) {
if (srcIdx < 0 || length > length() - srcIdx) {
throw new IndexOutOfBoundsException("expected: " + "0 <= srcIdx(" + srcIdx + ") <= srcIdx + length("
+ length + ") <= srcLen(" + thisLen + ')');
+ length + ") <= srcLen(" + length() + ')');
}
System.arraycopy(value, srcIdx, checkNotNull(dst, "dst"), dstIdx, length);
System.arraycopy(value, srcIdx + offset, checkNotNull(dst, "dst"), dstIdx, length);
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
for (int i = 0; i < value.length; ++i) {
final int end = offset + length;
for (int i = offset; i < end; ++i) {
h = h * HASH_CODE_PRIME ^ value[i] & HASH_CODE_PRIME;
}
hash = h;
}
return h;
return hash;
}
/**
* Copies a range of characters into a new string.
*
* @param start the offset of the first character.
* @param start the offset of the first character (inclusive).
* @return a new string containing the characters from start to the end of the string.
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
*/
public final ByteString subSequence(int start) {
public ByteString subSequence(int start) {
return subSequence(start, length());
}
/**
* Copies a range of characters into a new string.
* @param start the offset of the first character (inclusive).
* @param end The index to stop at (exclusive).
* @return a new string containing the characters from start to the end of the string.
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
*/
public ByteString subSequence(int start, int end) {
return subSequence(start, end, true);
}
/**
* Either copy or share a subset of underlying sub-sequence of bytes.
* @param start the offset of the first character (inclusive).
* @param end The index to stop at (exclusive).
* @param copy If {@code true} then a copy of the underlying storage will be made.
* If {@code false} then the underlying storage will be shared.
* @return a new string containing the characters from start to the end of the string.
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
*/
public ByteString subSequence(int start, int end, boolean copy) {
return subSequence(start, end, copy, DEFAULT_FACTORY);
}
/**
* Either copy or share a subset of underlying sub-sequence of bytes.
* @param start the offset of the first character (inclusive).
* @param end The index to stop at (exclusive).
* @param copy If {@code true} then a copy of the underlying storage will be made.
* If {@code false} then the underlying storage will be shared.
* @param factory The factory used to generate a new {@link ByteString} object.
* @return a new string containing the characters from start to the end of the string.
* @throws IndexOutOfBoundsException if {@code start < 0} or {@code start > length()}.
*/
protected ByteString subSequence(int start, int end, boolean copy, ByteStringFactory factory) {
if (start < 0 || start > end || end > length()) {
throw new IndexOutOfBoundsException("expected: 0 <= start(" + start + ") <= end (" + end + ") <= length("
+ length() + ')');
}
if (start == 0 && end == value.length) {
if (start == 0 && end == length()) {
return this;
}
@ -431,7 +489,7 @@ public class ByteString {
return EMPTY_STRING;
}
return new ByteString(value, start, end - start, false);
return factory.newInstance(value, start + offset, end - start, copy);
}
public final int parseAsciiInt() {
@ -458,7 +516,7 @@ public class ByteString {
int i = start;
boolean negative = byteAt(i) == '-';
if (negative && ++i == end) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
return parseAsciiInt(i, end, radix, negative);
@ -467,25 +525,25 @@ public class ByteString {
private int parseAsciiInt(int start, int end, int radix, boolean negative) {
int max = Integer.MIN_VALUE / radix;
int result = 0;
int offset = start;
while (offset < end) {
int digit = Character.digit((char) (value[offset++] & 0xFF), radix);
int currOffset = start;
while (currOffset < end) {
int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
if (digit == -1) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
if (max > result) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
int next = result * radix - digit;
if (next > result) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
result = next;
}
if (!negative) {
result = -result;
if (result < 0) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
}
return result;
@ -515,7 +573,7 @@ public class ByteString {
int i = start;
boolean negative = byteAt(i) == '-';
if (negative && ++i == end) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
return parseAsciiLong(i, end, radix, negative);
@ -524,25 +582,25 @@ public class ByteString {
private long parseAsciiLong(int start, int end, int radix, boolean negative) {
long max = Long.MIN_VALUE / radix;
long result = 0;
int offset = start;
while (offset < end) {
int digit = Character.digit((char) (value[offset++] & 0xFF), radix);
int currOffset = start;
while (currOffset < end) {
int digit = Character.digit((char) (value[currOffset++ + offset] & 0xFF), radix);
if (digit == -1) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
if (max > result) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
long next = result * radix - digit;
if (next > result) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
result = next;
}
if (!negative) {
result = -result;
if (result < 0) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
}
return result;
@ -553,11 +611,12 @@ public class ByteString {
}
public char parseChar(int start) {
if (start + 1 >= value.length) {
if (start + 1 >= length()) {
throw new IndexOutOfBoundsException("2 bytes required to convert to character. index " +
start + " would go out of bounds.");
}
return (char) (((value[start] & 0xFF) << 8) | (value[start + 1] & 0xFF));
final int startWithOffset = start + offset;
return (char) (((value[startWithOffset] & 0xFF) << 8) | (value[startWithOffset + 1] & 0xFF));
}
public final short parseAsciiShort() {
@ -576,7 +635,7 @@ public class ByteString {
int intValue = parseAsciiInt(start, end, radix);
short result = (short) intValue;
if (result != intValue) {
throw new NumberFormatException(subSequence(start, end).toString());
throw new NumberFormatException(subSequence(start, end, false).toString());
}
return result;
}
@ -605,9 +664,11 @@ public class ByteString {
if (this == obj) {
return true;
}
ByteString other = (ByteString) obj;
return hashCode() == other.hashCode() &&
PlatformDependent.equals(array(), 0, array().length, other.array(), 0, other.array().length);
PlatformDependent.equals(array(), arrayOffset(), arrayOffset() + length(),
other.array(), other.arrayOffset(), other.arrayOffset() + other.length());
}
/**
@ -645,6 +706,11 @@ public class ByteString {
return StringUtil.EMPTY_STRING;
}
return new String(value, start, length, charset);
if (start < 0 || length > length() - start) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= srcIdx + length("
+ length + ") <= srcLen(" + length() + ')');
}
return new String(value, start + offset, length, charset);
}
}

View File

@ -863,8 +863,9 @@ public final class PlatformDependent {
if (len1 != len2) {
return false;
}
for (int i = 0; i < len1; i++) {
if (bytes1[startPos1 + i] != bytes2[startPos2 + i]) {
final int end = startPos1 + len1;
for (int i = startPos1, j = startPos2; i < end; ++i, ++j) {
if (bytes1[i] != bytes2[j]) {
return false;
}
}

View File

@ -15,6 +15,7 @@
*/
package io.netty.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
import java.nio.charset.Charset;
@ -26,7 +27,6 @@ import org.junit.Test;
* Test for the {@link AsciiString} class
*/
public class AsciiStringTest {
@Test
public void testGetBytesStringBuilder() {
final StringBuilder b = new StringBuilder();
@ -78,4 +78,25 @@ public class AsciiStringTest {
AsciiString ascii = new AsciiString(string.toCharArray());
Assert.assertEquals(string, ascii.toString());
}
@Test
public void subSequenceTest() {
byte[] init = {'t', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 't', 'e', 's', 't' };
AsciiString ascii = new AsciiString(init);
final int start = 2;
final int end = init.length;
AsciiString sub1 = ascii.subSequence(start, end, false);
AsciiString sub2 = ascii.subSequence(start, end, true);
assertEquals(sub1, sub2);
for (int i = start; i < end; ++i) {
assertEquals(init[i], sub1.byteAt(i - start));
}
}
@Test
public void caseInsensativeHasher() {
String s1 = new String("TransfeR-EncodinG");
AsciiString s2 = new AsciiString("transfer-encoding");
assertEquals(AsciiString.caseInsensitiveHashCode(s1), AsciiString.caseInsensitiveHashCode(s2));
}
}

View File

@ -0,0 +1,204 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you 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.netty.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Before;
import org.junit.Test;
/**
* Test for the {@link ByteString} class.
*/
public class ByteStringTest {
private byte[] a;
private byte[] b;
private int aOffset = 22;
private int bOffset = 53;
private int length = 100;
private ByteString aByteString;
private ByteString bByteString;
private ByteString greaterThanAByteString;
private ByteString lessThanAByteString;
private Random r = new Random();
@Before
public void setup() {
a = new byte[128];
b = new byte[256];
r.nextBytes(a);
r.nextBytes(b);
aOffset = 22;
bOffset = 53;
length = 100;
System.arraycopy(a, aOffset, b, bOffset, length);
aByteString = new ByteString(a, aOffset, length, false);
bByteString = new ByteString(b, bOffset, length, false);
int i;
// Find an element that can be decremented
for (i = 1; i < length; ++i) {
if (a[aOffset + 1] > Byte.MIN_VALUE) {
--a[aOffset + 1];
break;
}
}
lessThanAByteString = new ByteString(a, aOffset, length, true);
++a[aOffset + i]; // Restore the a array to the original value
// Find an element that can be incremented
for (i = 1; i < length; ++i) {
if (a[aOffset + 1] < Byte.MAX_VALUE) {
++a[aOffset + 1];
break;
}
}
greaterThanAByteString = new ByteString(a, aOffset, length, true);
--a[aOffset + i]; // Restore the a array to the original value
}
@Test
public void testEqualsComparareSelf() {
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(aByteString, aByteString) == 0);
assertEquals(bByteString, bByteString);
}
@Test
public void testEqualsComparatorAgainstAnother1() {
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(bByteString, aByteString) == 0);
assertEquals(bByteString, aByteString);
assertEquals(bByteString.hashCode(), aByteString.hashCode());
}
@Test
public void testEqualsComparatorAgainstAnother2() {
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(aByteString, bByteString) == 0);
assertEquals(aByteString, bByteString);
assertEquals(aByteString.hashCode(), bByteString.hashCode());
}
@Test
public void testLessThan() {
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(lessThanAByteString, aByteString) < 0);
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(aByteString, lessThanAByteString) > 0);
}
@Test
public void testGreaterThan() {
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(greaterThanAByteString, aByteString) > 0);
assertTrue(ByteString.DEFAULT_COMPARATOR.compare(aByteString, greaterThanAByteString) < 0);
}
@Test
public void testSharedMemory() {
++a[aOffset];
ByteString aByteString1 = new ByteString(a, aOffset, length, true);
ByteString aByteString2 = new ByteString(a, aOffset, length, false);
assertEquals(aByteString, aByteString1);
assertEquals(aByteString, aByteString2);
for (int i = aOffset; i < length; ++i) {
assertEquals(a[i], aByteString.byteAt(i - aOffset));
}
}
@Test
public void testNotSharedMemory() {
ByteString aByteString1 = new ByteString(a, aOffset, length, true);
++a[aOffset];
assertNotEquals(aByteString, aByteString1);
int i = aOffset;
assertNotEquals(a[i], aByteString1.byteAt(i - aOffset));
++i;
for (; i < length; ++i) {
assertEquals(a[i], aByteString1.byteAt(i - aOffset));
}
}
@Test
public void forEachTest() throws Exception {
final AtomicReference<Integer> aCount = new AtomicReference<Integer>(0);
final AtomicReference<Integer> bCount = new AtomicReference<Integer>(0);
aByteString.forEachByte(new ByteProcessor() {
int i;
@Override
public boolean process(byte value) throws Exception {
assertEquals("failed at index: " + i, value, bByteString.byteAt(i++));
aCount.set(aCount.get() + 1);
return true;
}
});
bByteString.forEachByte(new ByteProcessor() {
int i;
@Override
public boolean process(byte value) throws Exception {
assertEquals("failed at index: " + i, value, aByteString.byteAt(i++));
bCount.set(bCount.get() + 1);
return true;
}
});
assertEquals(aByteString.length(), aCount.get().intValue());
assertEquals(bByteString.length(), bCount.get().intValue());
}
@Test
public void forEachDescTest() throws Exception {
final AtomicReference<Integer> aCount = new AtomicReference<Integer>(0);
final AtomicReference<Integer> bCount = new AtomicReference<Integer>(0);
aByteString.forEachByteDesc(new ByteProcessor() {
int i = 1;
@Override
public boolean process(byte value) throws Exception {
assertEquals("failed at index: " + i, value, bByteString.byteAt(bByteString.length() - (i++)));
aCount.set(aCount.get() + 1);
return true;
}
});
bByteString.forEachByteDesc(new ByteProcessor() {
int i = 1;
@Override
public boolean process(byte value) throws Exception {
assertEquals("failed at index: " + i, value, aByteString.byteAt(aByteString.length() - (i++)));
bCount.set(bCount.get() + 1);
return true;
}
});
assertEquals(aByteString.length(), aCount.get().intValue());
assertEquals(bByteString.length(), bCount.get().intValue());
}
@Test
public void subSequenceTest() {
final int start = 12;
final int end = aByteString.length();
ByteString aSubSequence = aByteString.subSequence(start, end, false);
ByteString bSubSequence = bByteString.subSequence(start, end, true);
assertEquals(aSubSequence, bSubSequence);
assertEquals(aSubSequence.hashCode(), bSubSequence.hashCode());
}
@Test
public void copyTest() {
byte[] aCopy = new byte[aByteString.length()];
aByteString.copy(0, aCopy, 0, aCopy.length);
ByteString aByteStringCopy = new ByteString(aCopy, false);
assertEquals(aByteString, aByteStringCopy);
}
}

View File

@ -18,6 +18,8 @@ package io.netty.microbench.internal;
import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.internal.PlatformDependent;
import java.util.Arrays;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
@ -28,25 +30,21 @@ import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import java.util.Arrays;
@Threads(1)
@State(Scope.Benchmark)
public class PlatformDependentBenchmark extends AbstractMicrobenchmark {
@Param({ "10", "50", "100", "1000", "10000", "100000" })
private String size;
private int size;
private byte[] bytes1;
private byte[] bytes2;
@Setup(Level.Trial)
public void setup() {
int size = Integer.parseInt(this.size);
bytes1 = new byte[size];
bytes2 = new byte[size];
for (int i = 0; i < size; i++) {
bytes1[i] = (byte) i;
bytes2[i] = (byte) i;
bytes1[i] = bytes2[i] = (byte) i;
}
}