Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
9812b66
optimize hot loops
yuzawa-san Jan 12, 2026
674d03f
clean up mutability collection flow
yuzawa-san Jan 12, 2026
cd9a876
use arrays
yuzawa-san Jan 12, 2026
bf62f5f
clean up dirty flow again
yuzawa-san Jan 12, 2026
56b930e
FixedIntegerList
yuzawa-san Jan 12, 2026
8f80001
avoid itable calls to IntegerSet
yuzawa-san Jan 12, 2026
1515377
update tests
yuzawa-san Jan 13, 2026
74870a8
fixed signed byte int limit
yuzawa-san Jan 13, 2026
92cbd01
javadoc
yuzawa-san Jan 13, 2026
5070310
DirtyableList
yuzawa-san Jan 13, 2026
06f6c28
fix test
yuzawa-san Jan 13, 2026
2b97c41
remove substring
yuzawa-san Jan 15, 2026
3ab0e44
fix tests
yuzawa-san Jan 15, 2026
35ace8d
fix copy
yuzawa-san Jan 16, 2026
ed4581a
own bitset
yuzawa-san Jan 16, 2026
ec6b366
wip
yuzawa-san Jan 16, 2026
c7cc6b6
remove imports
yuzawa-san Jan 16, 2026
22b1caa
style
yuzawa-san Jan 20, 2026
e966b1e
optimize fibonacci
yuzawa-san Jan 27, 2026
3645237
optimize encode
yuzawa-san Jan 27, 2026
b569dff
convert fields to enums
yuzawa-san Jan 21, 2026
af6ab8d
thin segments
yuzawa-san Jan 21, 2026
269e8a5
move
yuzawa-san Jan 21, 2026
61524c4
dry
yuzawa-san Jan 21, 2026
842af42
dry
yuzawa-san Jan 22, 2026
80abb08
use an abstract method
yuzawa-san Jan 22, 2026
e96873e
more dry
yuzawa-san Jan 22, 2026
bbaad3c
more dry
yuzawa-san Jan 22, 2026
900b542
clean up flows
yuzawa-san Jan 23, 2026
09ef2d9
clean up hierarchy
yuzawa-san Jan 23, 2026
b9817bb
style
yuzawa-san Jan 23, 2026
cd9bb63
remove extra class
yuzawa-san Jan 23, 2026
70c65ef
use fieldkey
yuzawa-san Jan 23, 2026
3f5f9c8
toString
yuzawa-san Jan 23, 2026
f9e14b0
user headers to store state
yuzawa-san Jan 24, 2026
7639aaf
clean up registry of sections
yuzawa-san Jan 24, 2026
a9e2109
cleanup
yuzawa-san Jan 24, 2026
14e87ae
notes
yuzawa-san Jan 24, 2026
45f0c2b
use array
yuzawa-san Jan 27, 2026
fba9fbb
cleanup
yuzawa-san Jan 27, 2026
647eb79
another size
yuzawa-san Jan 27, 2026
46843c7
fix sizing
yuzawa-san Jan 28, 2026
e50a578
fix dirty on init
yuzawa-san Jan 28, 2026
1ae9e6d
;
yuzawa-san Jan 28, 2026
4e73663
upgrade slicing methodology to use indexOf
yuzawa-san Jan 28, 2026
f463f6b
substring
yuzawa-san Jan 28, 2026
a8d48c5
clear only if something was there
yuzawa-san Jan 28, 2026
9ea3b08
do base64 in blocks
yuzawa-san Jan 28, 2026
5c1d473
add base 64 test
yuzawa-san Jan 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
454 changes: 155 additions & 299 deletions iabgpp-encoder/src/main/java/com/iab/gpp/encoder/GppModel.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.iab.gpp.encoder.base64;

import java.util.Arrays;
import com.iab.gpp.encoder.bitstring.BitSet;
import com.iab.gpp.encoder.bitstring.BitString;
import com.iab.gpp.encoder.bitstring.BitStringBuilder;
import com.iab.gpp.encoder.datatype.encoder.FixedIntegerEncoder;
Expand All @@ -11,18 +13,22 @@ public abstract class AbstractBase64UrlEncoder {
protected abstract void pad(BitStringBuilder bitString);

private static final int BASE64_BITS = 6;
private static final int NO_SYMBOL = -1;
/**
* Base 64 URL character set. Different from standard Base64 char set in that '+' and '/' are
* replaced with '-' and '_'.
*/
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];
private static final int[] REVERSE_DICT = new int[REVERSE_DICT_SIZE];
static {
Arrays.fill(REVERSE_DICT, NO_SYMBOL);
for (int i = 0; i < DICT.length(); i++) {
BitStringBuilder builder = new BitStringBuilder();
FixedIntegerEncoder.encode(builder, i, 6);
REVERSE_DICT[DICT.charAt(i)] = builder.build();
// NOTE: the bit string is stored in a long[] and read from LSB to MSB
// but each base64 digit is read from MSB to LSB
// so they need to be reversed.
int value = Integer.reverse(i) >>> -BASE64_BITS;
REVERSE_DICT[DICT.charAt(i)] = value;
}
}

Expand All @@ -35,9 +41,10 @@ public StringBuilder encode(BitStringBuilder bitStringBuilder) {
int index = 0;
while (index <= length - BASE64_BITS) {
try {
int n = FixedIntegerEncoder.decode(bitString, index, BASE64_BITS);
int nextIndex = index + BASE64_BITS;
int n = FixedIntegerEncoder.decode(bitString, index, nextIndex);
str.append(DICT.charAt(n));
index += BASE64_BITS;
index = nextIndex;
} catch (DecodingException e) {
throw new EncodingException("Unencodable Base64Url '" + bitString + "'");
}
Expand All @@ -47,20 +54,51 @@ public StringBuilder encode(BitStringBuilder bitStringBuilder) {
}

public BitString decode(CharSequence str) {
int length = str.length();
BitStringBuilder sb = new BitStringBuilder(length * BASE64_BITS);
for (int i = 0; i < length; i++) {
char c = str.charAt(i);
BitString n = null;
if (c < REVERSE_DICT_SIZE) {
n = REVERSE_DICT[c];
try {
int length = str.length();
int bitLength = length * BASE64_BITS;
int numBlocks = length >> 2;
byte[] words = new byte[(numBlocks + 1) * 3];
int limit = numBlocks << 2;
int dst = 0;
int src = 0;
while (src < limit) {
int b1 = REVERSE_DICT[str.charAt(src++)];
int b2 = REVERSE_DICT[str.charAt(src++)];
int b3 = REVERSE_DICT[str.charAt(src++)];
int b4 = REVERSE_DICT[str.charAt(src++)];
if ((b1 | b2 | b3 | b4) < 0) {
throw new DecodingException("Undecodable Base64URL string");
}
int bits0 = b4 << 18 | b3 << 12 | b2 << 6 | b1;
words[dst++] = (byte)(bits0);
words[dst++] = (byte)(bits0 >> 8);
words[dst++] = (byte)(bits0 >> 16);
}
if (n == null) {
throw new DecodingException("Undecodable Base64URL string");
if (length > limit) {
remainder(str, words, length, src, dst);
}
sb.append(n);
return new BitString(new BitSet(words), bitLength);
} catch (ArrayIndexOutOfBoundsException e) {
throw new DecodingException("Undecodable Base64URL string");
}
}

private static final void remainder(CharSequence str, byte[] words, int length, int src, int dst) {
int b1 = src < length ? REVERSE_DICT[str.charAt(src)] : 0;
src++;
int b2 = src < length ? REVERSE_DICT[str.charAt(src)] : 0;
src++;
int b3 = src < length ? REVERSE_DICT[str.charAt(src)] : 0;
src++;
int b4 = src < length ? REVERSE_DICT[str.charAt(src)] : 0;
src++;
if ((b1 | b2 | b3 | b4) < 0) {
throw new DecodingException("Undecodable Base64URL string");
}
return sb.build();
int bits0 = b4 << 18 | b3 << 12 | b2 << 6 | b1;
words[dst++] = (byte)(bits0);
words[dst++] = (byte)(bits0 >> 8);
words[dst++] = (byte)(bits0 >> 16);
}
}
107 changes: 107 additions & 0 deletions iabgpp-encoder/src/main/java/com/iab/gpp/encoder/bitstring/BitSet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.iab.gpp.encoder.bitstring;

import java.util.Arrays;
import java.util.Base64;
import com.iab.gpp.encoder.base64.TraditionalBase64UrlEncoder;
import com.iab.gpp.encoder.datatype.encoder.IntegerSet;
import com.iab.gpp.encoder.error.DecodingException;

// a thin version of java.util.BitSet
public final class BitSet {

private static final int ADDRESS_BITS_PER_WORD = 3;
public static final int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD;

/* Used to shift left or right for a partial word mask */
private static final int WORD_MASK = 0xffffffff;

private byte[] words;

public BitSet(byte[] words) {
this.words = words;
}

public BitSet(int initialCapacity) {
this(new byte[wordIndex(initialCapacity) + 1]);
}

public BitSet() {
this(new byte[0]);
}

public static int wordIndex(int index) {
if (index < 0) {
throw new DecodingException("got negative word index");
}
return index >> ADDRESS_BITS_PER_WORD;
}

private byte[] ensureIndex(int wordIndex) {
byte[] words = this.words;
int wordsUsed = words.length;
if (wordIndex >= wordsUsed) {
int request = Math.max(2 * wordsUsed, wordIndex + 1);
words = Arrays.copyOf(words, request);
this.words = words;
}
return words;
}

public boolean get(int bitIndex) {
int wordIndex = wordIndex(bitIndex);
byte[] words = this.words;
int bit = bitIndex % BITS_PER_WORD;
return (wordIndex < words.length)
&& ((words[wordIndex] >>> bit) & 1) == 1;
}

public void clear(int from, int to) {
for (int i = from; i < to; i++) {
clear(i);
}
}

public void clear(int bitIndex) {
int wordIndex = wordIndex(bitIndex);
byte[] words = this.words;
if (wordIndex < words.length) {
int bit = bitIndex % BITS_PER_WORD;
words[wordIndex] &= ~(1 << bit);
}
}

public int nextSetBit(int fromIndex) {
byte[] words = this.words;
int wordsInUse = words.length;
int u = wordIndex(fromIndex);
if (u >= wordsInUse) {
return -1;
}

int bit = fromIndex % BITS_PER_WORD;
int word = words[u] & (WORD_MASK << bit);

while (true) {
if (word != 0) {
return (u * BITS_PER_WORD) + Integer.numberOfTrailingZeros(word);
}
if (++u == wordsInUse) {
return -1;
}
word = words[u];
}
}

public void set(int from, int to) {
for (int i = from; i < to; i++) {
set(i);
}
}

public void set(int bitIndex) {
int wordIndex = wordIndex(bitIndex);
byte[] words = ensureIndex(wordIndex);
int bit = bitIndex % BITS_PER_WORD;
words[wordIndex] |= (1 << bit);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.iab.gpp.encoder.bitstring;

import java.util.BitSet;
import com.iab.gpp.encoder.datatype.encoder.IntegerBitSet;
import com.iab.gpp.encoder.datatype.encoder.IntegerSet;
import com.iab.gpp.encoder.error.DecodingException;

Expand All @@ -10,23 +8,13 @@ public final class BitString {
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 static final BitString EMPTY = new BitString(new BitSet(), 0, 0);

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

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

public static final BitString empty(int size) {
if (size == 0) {
return EMPTY;
}
return new BitString(new BitSet(size), 0, size);
this.length = length;
}

public static final BitString of(String str) {
Expand All @@ -45,54 +33,28 @@ public static final BitString of(String str) {
return builder.build();
}

public IntegerSet toIntegerSet() {
return new IntegerBitSet(bitSet, from, to, 1);
public IntegerSet toIntegerSet(int from, int to) {
return new IntegerSet(bitSet, from, to, 1);
}

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

public boolean getValue(int i) {
return bitSet.get(from + i);
}

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

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");
if (i >= length) {
throw new DecodingException("Bit string access out of range");
}
int oldFrom = this.from;
return new BitString(bitSet, oldFrom + newFrom, oldFrom + newTo);
return bitSet.get(i);
}

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();
public int length() {
return length;
}

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

import java.util.BitSet;

public final class BitStringBuilder {
private final BitSet bitSet;
Expand All @@ -15,7 +14,12 @@ public BitStringBuilder() {
}

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

public BitStringBuilder extend(int length) {
this.length += length;
return this;
}

public BitStringBuilder append(boolean value) {
Expand All @@ -26,9 +30,8 @@ public BitStringBuilder append(boolean value) {
return this;
}

public BitStringBuilder append(BitString other) {
int otherLength = other.length();
for (int i = 0; i < otherLength; i++) {
public BitStringBuilder append(BitString other, int from, int to) {
for (int i = from; i < to; i++) {
append(other.getValue(i));
}
return this;
Expand Down
Loading
Loading