org.wildfly.common.bytes.ByteStringBuilder Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 org.wildfly.common.bytes;
import static org.wildfly.common._private.CommonMessages.msg;
import java.security.DigestException;
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Mac;
import org.wildfly.common.iteration.ByteIterator;
import org.wildfly.common.iteration.CodePointIterator;
/**
* A builder for byte arrays.
*/
public final class ByteStringBuilder {
private byte[] content;
private int length;
public ByteStringBuilder() {
this.content = new byte[16];
}
public ByteStringBuilder(final byte[] content) {
if (content != null && content.length != 0) {
this.content = content.clone();
this.length = this.content.length;
} else {
this.content = new byte[16];
}
}
public ByteStringBuilder append(boolean b) {
appendLatin1(Boolean.toString(b));
return this;
}
public ByteStringBuilder append(byte b) {
doAppend(b);
return this;
}
public ByteStringBuilder append(char c) {
return appendUtf8Raw((int) c);
}
public ByteStringBuilder appendUtf8Raw(int codePoint) {
if (codePoint < 0) {
throw new IllegalArgumentException();
} else if (codePoint < 0x80) {
doAppend((byte) codePoint);
} else if (codePoint < 0x800) {
doAppend((byte) (0xC0 | 0x1F & codePoint >>> 6));
doAppend((byte) (0x80 | 0x3F & codePoint));
} else if (codePoint < 0x10000) {
doAppend((byte) (0xE0 | 0x0F & codePoint >>> 12));
doAppend((byte) (0x80 | 0x3F & codePoint >>> 6));
doAppend((byte) (0x80 | 0x3F & codePoint));
} else if (codePoint < 0x110000) {
doAppend((byte) (0xF0 | 0x07 & codePoint >>> 18));
doAppend((byte) (0x80 | 0x3F & codePoint >>> 12));
doAppend((byte) (0x80 | 0x3F & codePoint >>> 6));
doAppend((byte) (0x80 | 0x3F & codePoint));
} else {
throw new IllegalArgumentException();
}
return this;
}
public ByteStringBuilder appendUtf8(CodePointIterator iterator) {
while (iterator.hasNext()) {
appendUtf8Raw(iterator.next());
}
return this;
}
public ByteStringBuilder appendLatin1(CodePointIterator iterator) {
int cp;
while (iterator.hasNext()) {
cp = iterator.next();
if (cp > 255) throw new IllegalArgumentException();
append((byte) cp);
}
return this;
}
public ByteStringBuilder appendAscii(CodePointIterator iterator) {
int cp;
while (iterator.hasNext()) {
cp = iterator.next();
if (cp > 127) throw new IllegalArgumentException();
append((byte) cp);
}
return this;
}
public ByteStringBuilder append(ByteIterator iterator) {
return iterator.appendTo(this);
}
public ByteStringBuilder append(byte[] bytes) {
int length = this.length;
int bl = bytes.length;
reserve(bl, false);
System.arraycopy(bytes, 0, content, length, bl);
this.length = length + bl;
return this;
}
public ByteStringBuilder append(byte[] bytes, int offs, int len) {
reserve(len, false);
int length = this.length;
System.arraycopy(bytes, offs, content, length, len);
this.length = length + len;
return this;
}
public ByteStringBuilder appendLatin1(CharSequence s) {
int len = s.length();
reserve(len, false);
char c;
for (int i = 0; i < len; i ++) {
c = s.charAt(i);
if (c > 255) throw new IllegalArgumentException();
doAppendNoCheck((byte) c);
}
return this;
}
public ByteStringBuilder appendLatin1(CharSequence s, int offs, int len) {
reserve(len, false);
char c;
for (int i = 0; i < len; i ++) {
c = s.charAt(i + offs);
if (c > 255) throw new IllegalArgumentException();
doAppendNoCheck((byte) c);
}
return this;
}
public ByteStringBuilder appendLatin1(String s) {
int len = s.length();
reserve(len, false);
char c;
for (int i = 0; i < len; i ++) {
c = s.charAt(i);
if (c > 255) throw new IllegalArgumentException();
doAppendNoCheck((byte) c);
}
return this;
}
public ByteStringBuilder appendLatin1(String s, int offs, int len) {
reserve(len, false);
char c;
for (int i = 0; i < len; i ++) {
c = s.charAt(i + offs);
if (c > 255) throw new IllegalArgumentException();
doAppendNoCheck((byte) c);
}
return this;
}
public ByteStringBuilder append(CharSequence s) {
return append(s, 0, s.length());
}
public ByteStringBuilder append(CharSequence s, int offs, int len) {
int c;
int i = 0;
while (i < len) {
c = s.charAt(offs + i++);
if (Character.isHighSurrogate((char) c)) {
if (i < len) {
char t = s.charAt(offs + i ++);
if (! Character.isLowSurrogate(t)) {
throw new IllegalArgumentException();
}
c = Character.toCodePoint((char) c, t);
} else {
throw new IllegalArgumentException();
}
}
appendUtf8Raw(c);
}
return this;
}
public ByteStringBuilder append(String s) {
return append(s, 0, s.length());
}
public ByteStringBuilder append(String s, int offs, int len) {
int c;
int i = 0;
while (i < len) {
c = s.charAt(offs + i++);
if (Character.isHighSurrogate((char) c)) {
if (i < len) {
char t = s.charAt(offs + i ++);
if (! Character.isLowSurrogate(t)) {
throw new IllegalArgumentException();
}
c = Character.toCodePoint((char) c, t);
} else {
throw new IllegalArgumentException();
}
}
appendUtf8Raw(c);
}
return this;
}
public ByteStringBuilder appendPackedUnsignedBE(int v) {
if (v > 0) {
final int bits = Integer.numberOfTrailingZeros(Integer.highestOneBit(v)) + 1;
final int size = (bits + 7) / 7;
for (int x = 0, b = (size - 1) * 7; x < size - 1; x ++, b -= 7) {
doAppend((byte) (0x80 | v >>> b));
}
}
doAppend((byte) (~0x80 & v));
return this;
}
public ByteStringBuilder appendPackedUnsignedBE(long v) {
if (v > 0) {
final int bits = Long.numberOfTrailingZeros(Long.highestOneBit(v)) + 1;
final int size = (bits + 7) / 7;
for (int x = 0, b = (size - 1) * 7; x < size - 1; x ++, b -= 7) {
doAppend((byte) (0x80L | v >>> b));
}
}
doAppend((byte) (~0x80L & v));
return this;
}
public ByteStringBuilder appendBE(short s) {
doAppend((byte) (s >>> 8));
doAppend((byte) s);
return this;
}
public ByteStringBuilder appendNumber(int i) {
appendLatin1(Integer.toString(i));
return this;
}
public ByteStringBuilder appendBE(int i) {
doAppend((byte) (i >>> 24));
doAppend((byte) (i >>> 16));
doAppend((byte) (i >>> 8));
doAppend((byte) i);
return this;
}
public ByteStringBuilder appendNumber(long l) {
appendLatin1(Long.toString(l));
return this;
}
public ByteStringBuilder appendBE(long l) {
doAppend((byte) (l >>> 56));
doAppend((byte) (l >>> 48));
doAppend((byte) (l >>> 40));
doAppend((byte) (l >>> 32));
doAppend((byte) (l >>> 24));
doAppend((byte) (l >>> 16));
doAppend((byte) (l >>> 8));
doAppend((byte) l);
return this;
}
public ByteStringBuilder appendObject(Object o) {
appendLatin1(String.valueOf(o));
return this;
}
public ByteStringBuilder append(ByteStringBuilder other) {
append(other.content, 0, other.length);
return this;
}
public ByteStringBuilder updateDigest(final MessageDigest messageDigest) {
messageDigest.update(content, 0, length);
return this;
}
public ByteStringBuilder appendDigestResult(final MessageDigest messageDigest) throws DigestException {
reserve(messageDigest.getDigestLength(), false);
final int length = this.length;
final byte[] content = this.content;
this.length = length + messageDigest.digest(content, length, content.length - length);
return this;
}
public ByteStringBuilder updateMac(final Mac mac) {
mac.update(content, 0, length);
return this;
}
public byte[] toArray() {
return Arrays.copyOf(content, length);
}
public byte byteAt(int index) {
if (index < 0 || index > length) throw new IndexOutOfBoundsException();
return content[index];
}
public int capacity() {
return content.length;
}
public int length() {
return length;
}
public void setLength(int newLength) {
if (newLength > length) {
// grow
reserve(newLength - length, true);
}
length = newLength;
}
public boolean contentEquals(final byte[] other) {
return contentEquals(other, 0, other.length);
}
public boolean contentEquals(final byte[] other, final int offs, final int length) {
if (length != this.length) return false;
for (int i = 0; i < length; i++) {
if (content[i] != other[offs + i]) {
return false;
}
}
return true;
}
private void reserve(final int count, final boolean clear) {
final int length = this.length;
final byte[] content = this.content;
int cl = content.length;
if (cl - length >= count) {
if (clear) Arrays.fill(content, length, length + count, (byte) 0);
return;
}
// clear remainder
if (clear) Arrays.fill(content, length, cl, (byte) 0);
do {
// not enough space... grow by 1.5x
cl = cl + (cl + 1 >> 1);
if (cl < 0) throw msg.tooLarge();
} while (cl - length < count);
this.content = Arrays.copyOf(content, cl);
}
private void doAppend(final byte b) {
byte[] content = this.content;
final int cl = content.length;
final int length = this.length;
if (length == cl) {
content = this.content = Arrays.copyOf(content, cl + (cl + 1 >> 1)); // content must not be blank
}
content[length] = b;
this.length = length + 1;
}
private void doAppendNoCheck(final byte b) {
content[length ++] = b;
}
public ByteIterator iterate() {
return ByteIterator.ofBytes(content, 0, length);
}
}