Skip to content

Commit

Permalink
cpu and memory optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
yuzawa-san committed Jul 23, 2024
1 parent fcd6061 commit 5e4b971
Show file tree
Hide file tree
Showing 74 changed files with 684 additions and 448 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.iab.gpp.encoder.base64;

import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.iab.gpp.encoder.bitstring.BitString;
import com.iab.gpp.encoder.bitstring.BitStringBuilder;
import com.iab.gpp.encoder.datatype.encoder.FixedIntegerEncoder;
import com.iab.gpp.encoder.error.DecodingException;
import com.iab.gpp.encoder.error.EncodingException;
Expand All @@ -12,68 +12,55 @@ public abstract class AbstractBase64UrlEncoder {

abstract protected String pad(String bitString);

private static final int BASE64_BITS = 6;
/**
* Base 64 URL character set. Different from standard Base64 char set in that '+' and '/' are
* replaced with '-' and '_'.
*/
private static String DICT = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
// prettier-ignore
private static Map<Character, Integer> REVERSE_DICT = Stream
.of(new Object[][] {{'A', 0}, {'B', 1}, {'C', 2}, {'D', 3}, {'E', 4}, {'F', 5}, {'G', 6}, {'H', 7}, {'I', 8},
{'J', 9}, {'K', 10}, {'L', 11}, {'M', 12}, {'N', 13}, {'O', 14}, {'P', 15}, {'Q', 16}, {'R', 17}, {'S', 18},
{'T', 19}, {'U', 20}, {'V', 21}, {'W', 22}, {'X', 23}, {'Y', 24}, {'Z', 25}, {'a', 26}, {'b', 27}, {'c', 28},
{'d', 29}, {'e', 30}, {'f', 31}, {'g', 32}, {'h', 33}, {'i', 34}, {'j', 35}, {'k', 36}, {'l', 37}, {'m', 38},
{'n', 39}, {'o', 40}, {'p', 41}, {'q', 42}, {'r', 43}, {'s', 44}, {'t', 45}, {'u', 46}, {'v', 47}, {'w', 48},
{'x', 49}, {'y', 50}, {'z', 51}, {'0', 52}, {'1', 53}, {'2', 54}, {'3', 55}, {'4', 56}, {'5', 57}, {'6', 58},
{'7', 59}, {'8', 60}, {'9', 61}, {'-', 62}, {'_', 63}})
.collect(Collectors.toMap(data -> (Character) data[0], data -> (Integer) data[1]));

private static Pattern BITSTRING_VERIFICATION_PATTERN = Pattern.compile("^[0-1]*$", Pattern.CASE_INSENSITIVE);
private static Pattern BASE64URL_VERIFICATION_PATTERN =
Pattern.compile("^[A-Za-z0-9\\-_]*$", Pattern.CASE_INSENSITIVE);

public String encode(String bitString) {
// should only be 0 or 1
if (!BITSTRING_VERIFICATION_PATTERN.matcher(bitString).matches()) {
throw new EncodingException("Unencodable Base64Url '" + bitString + "'");
private static final String DICT = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
private static final int REVERSE_DICT_SIZE = 128;
private static final BitString[] REVERSE_DICT = new BitString[REVERSE_DICT_SIZE];
static {
for (int i = 0; i < DICT.length(); i++) {
REVERSE_DICT[DICT.charAt(i)] = BitString.of(FixedIntegerEncoder.encode(i, 6));
}
}

public String encode(String bitString) {
bitString = pad(bitString);

String str = "";
int length = bitString.length();
StringBuilder str = new StringBuilder(length / BASE64_BITS);

int index = 0;
while (index <= bitString.length() - 6) {
String s = bitString.substring(index, index + 6);

while (index <= length - BASE64_BITS) {
try {
int n = FixedIntegerEncoder.decode(s);
Character c = AbstractBase64UrlEncoder.DICT.charAt(n);
str += c;
index += 6;
int n = FixedIntegerEncoder.decode(bitString, index, BASE64_BITS);
str.append(DICT.charAt(n));
index += BASE64_BITS;
} catch (DecodingException e) {
throw new EncodingException("Unencodable Base64Url '" + bitString + "'");
}
}

return str;
return str.toString();
}

public String decode(String str) {
// should contain only characters from the base64url set
if (!BASE64URL_VERIFICATION_PATTERN.matcher(str).matches()) {
throw new DecodingException("Undecodable Base64URL string");
}

String bitString = "";

for (int i = 0; i < str.length(); i++) {
public BitString decode(String str) {
int length = str.length();
BitStringBuilder sb = new BitStringBuilder(length * BASE64_BITS);
for (int i = 0; i < length; i++) {
char c = str.charAt(i);
Integer n = AbstractBase64UrlEncoder.REVERSE_DICT.get(c);
String s = FixedIntegerEncoder.encode(n, 6);
bitString += s;
}
BitString n = null;
if (c < REVERSE_DICT_SIZE) {
n = REVERSE_DICT[c];
}
if (n == null) {
throw new DecodingException("Undecodable Base64URL string");
}
sb.append(n);

return bitString;
}
return sb.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.iab.gpp.encoder.bitstring;

import java.util.AbstractList;
import java.util.BitSet;

import com.iab.gpp.encoder.error.DecodingException;

public final class BitString extends AbstractList<Boolean> {
public static final char TRUE = '1';
public static final char FALSE = '0';
public static final String TRUE_STRING = new String(new char[] {TRUE});
public static final String FALSE_STRING = new String(new char[] {FALSE});

private final BitSet bitSet;
private final int from;
private final int to;

BitString(BitSet bitSet, int from, int to) {
this.bitSet = bitSet;
this.from = from;
this.to = to;
}

public static final BitString of(String str) {
int length = str.length();
BitStringBuilder builder = new BitStringBuilder(length);
for (int i = 0; i < length; i++) {
char c = str.charAt(i);
if (c == TRUE) {
builder.append(true);
} else if (c == FALSE) {
builder.append(false);
} else {
throw new DecodingException("Invalid bit string");
}
}
return builder.build();
}

public String toString() {
StringBuilder sb = new StringBuilder(length());
for (int i = from; i < to; i++) {
sb.append(bitSet.get(i) ? TRUE : FALSE);
}
return sb.toString();
}

/**
* This is the fast getter without boxing
* @param i index
* @return the value at that index
*/
public boolean getValue(int i) {
return bitSet.get(from + i);
}

@Override
public Boolean get(int i) {
return getValue(i);
}

@Override
public Boolean set(int index, Boolean element) {
Boolean old = get(index);
bitSet.set(from + index, element);
return old;
}

public int length() {
return to - from;
}

@Override
public int size() {
return length();
}

public BitString substring(int i) {
return substring(i, length());
}

public BitString substring(int newFrom, int newTo) {
int length = length();
if (newFrom > newTo || newFrom < 0 || newFrom > length || newTo > length) {
throw new IllegalArgumentException("Invalid substring");
}
int oldFrom = this.from;
return new BitString(bitSet, oldFrom + newFrom, oldFrom + newTo);
}

public int indexOf(String string) {
return indexOf(string, 0);
}

public int indexOf(String string, int startIndex) {
int stringLength = string.length();
for (int i = startIndex, to = this.to; i < to; i++) {
int match = 0;
for (int j = 0; j < stringLength; j++) {
if ((string.charAt(j) == TRUE) == bitSet.get(from + i + j)) {
match++;
}
}
if (match == stringLength) {
return i;
}
}
return -1;
}

public boolean isEmpty() {
return length() == 0;
}

public BitString expandTo(int target) {
int needed = target - length();
if (needed == 0) {
return this;
}
if (needed < 0) {
return substring(0, target);
}
BitStringBuilder sb = new BitStringBuilder(target);
sb.append(this);
for (int i = 0; i < needed; i++) {
sb.append(false);
}
return sb.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.iab.gpp.encoder.bitstring;

import java.util.BitSet;

public final class BitStringBuilder {
private final BitSet bitSet;
private int length;

public BitStringBuilder(int initialCapacity) {
this.bitSet = new BitSet(initialCapacity);
}

public BitStringBuilder() {
this.bitSet = new BitSet();
}

public BitString build() {
return new BitString(bitSet, 0, length);
}

public void append(boolean value) {
int idx = length++;
if (value) {
bitSet.set(idx);
}
}

public void append(BitString other) {
int length = other.length();
for (int i = 0; i < length; i++) {
append(other.getValue(i));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,28 @@ public static BitStringEncoder getInstance() {
}

public String encode(EncodableBitStringFields fields, List<String> fieldNames) {
String bitString = "";
StringBuilder bitString = new StringBuilder();
for (int i = 0; i < fieldNames.size(); i++) {
String fieldName = fieldNames.get(i);
if (fields.containsKey(fieldName)) {
AbstractEncodableBitStringDataType<?> field = fields.get(fieldName);
bitString += field.encode();
bitString.append(field.encode());
} else {
throw new EncodingException("Field not found: '" + fieldName + "'");
}
}

return bitString;
return bitString.toString();
}

public void decode(String bitString, List<String> fieldNames, EncodableBitStringFields fields) {
public void decode(BitString bitString, List<String> fieldNames, EncodableBitStringFields fields) {
int index = 0;
for (int i = 0; i < fieldNames.size(); i++) {
String fieldName = fieldNames.get(i);
if (fields.containsKey(fieldName)) {
AbstractEncodableBitStringDataType<?> field = fields.get(fieldName);
try {
String substring = field.substring(bitString, index);
BitString substring = field.substring(bitString, index);
field.decode(substring);
index += substring.length();
} catch (SubstringException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.iab.gpp.encoder.bitstring.BitString;
import com.iab.gpp.encoder.error.ValidationException;

public abstract class AbstractEncodableBitStringDataType<T> implements EncodableDataType<T> {
Expand Down Expand Up @@ -50,8 +52,8 @@ public boolean getHardFailIfMissing() {

public abstract String encode();

public abstract void decode(String bitString);
public abstract void decode(BitString bitString);

public abstract String substring(String bitString, int fromIndex) throws SubstringException;
public abstract BitString substring(BitString bitString, int fromIndex) throws SubstringException;

}
Loading

0 comments on commit 5e4b971

Please sign in to comment.