Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@ public BasicCodePointArray slice(int i, int j) {
}

@Override
public int get(int index) {
checkElementIndex(index, size());
return codePoints()[offset() + index] & 0xffff;
public String substring(int i, int j) {
checkPositionIndexes(i, j, size());
return new String(codePoints(), offset() + i, j - i);
}

@Override
public final String toString() {
return new String(codePoints(), offset(), size());
public int get(int index) {
checkElementIndex(index, size());
return codePoints()[offset() + index] & 0xffff;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ public abstract class CelCodePointArray {
/** Returns a new {@link CelCodePointArray} that is a subview of this between [i, j). */
public abstract CelCodePointArray slice(int i, int j);

/**
* Returns the code points between [i, j) as a string.
*
* <p>Equivalent to {@code slice(i, j).toString()}, but does not materialize the intermediate
* view. Lexing and parsing call this for every literal and identifier.
*/
public abstract String substring(int i, int j);

/** Get the code point at the given index. */
public abstract int get(int index);

Expand All @@ -55,7 +63,9 @@ public boolean isEmpty() {
}

@Override
public abstract String toString();
public final String toString() {
return substring(0, size());
}

public static CelCodePointArray fromString(String text) {
if (isNullOrEmpty(text)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

package dev.cel.common.internal;

import static com.google.common.base.Preconditions.checkPositionIndexes;

import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.DoNotCall;
import com.google.errorprone.annotations.Immutable;
Expand Down Expand Up @@ -51,6 +53,12 @@ public int get(int index) {
String.format("index (%s) must not be greater than size (0)", index));
}

@Override
public String substring(int i, int j) {
checkPositionIndexes(i, j, 0);
return "";
}

@Override
public int size() {
return 0;
Expand All @@ -60,9 +68,4 @@ public int size() {
public ImmutableList<Integer> lineOffsets() {
return ImmutableList.of(1);
}

@Override
public String toString() {
return "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@ public Latin1CodePointArray slice(int i, int j) {
}

@Override
public int get(int index) {
checkElementIndex(index, size());
return Byte.toUnsignedInt(codePoints()[offset() + index]);
public String substring(int i, int j) {
checkPositionIndexes(i, j, size());
return new String(codePoints(), offset() + i, j - i, ISO_8859_1);
}

@Override
public final String toString() {
return new String(codePoints(), offset(), size(), ISO_8859_1);
public int get(int index) {
checkElementIndex(index, size());
return Byte.toUnsignedInt(codePoints()[offset() + index]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@ public SupplementalCodePointArray slice(int i, int j) {
}

@Override
public int get(int index) {
checkElementIndex(index, size());
return codePoints()[offset() + index];
public String substring(int i, int j) {
checkPositionIndexes(i, j, size());
return new String(codePoints(), offset() + i, j - i);
}

@Override
public final String toString() {
return new String(codePoints(), offset(), size());
public int get(int index) {
checkElementIndex(index, size());
return codePoints()[offset() + index];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package dev.cel.common.internal;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
Expand All @@ -39,6 +40,93 @@ public void computeLineOffset(
.inOrder();
}

@Test
public void substring_empty() {
CelCodePointArray empty = CelCodePointArray.fromString("");
assertThat(empty).isInstanceOf(EmptyCodePointArray.class);

assertThat(empty.substring(0, 0)).isEmpty();
assertThrows(IndexOutOfBoundsException.class, () -> empty.substring(0, 1));
assertThrows(IndexOutOfBoundsException.class, () -> empty.substring(-1, 0));
assertThrows(IndexOutOfBoundsException.class, () -> empty.substring(1, 0));
assertThrows(IndexOutOfBoundsException.class, () -> empty.substring(1, 1));
}

@Test
public void substring_latin1() {
CelCodePointArray latin1 = CelCodePointArray.fromString("hello world");
assertThat(latin1).isInstanceOf(Latin1CodePointArray.class);

assertThat(latin1.substring(0, 5)).isEqualTo("hello");
assertThat(latin1.substring(6, 11)).isEqualTo("world");
assertThat(latin1.substring(0, 11)).isEqualTo("hello world");
assertThat(latin1.substring(3, 3)).isEmpty();

assertThrows(IndexOutOfBoundsException.class, () -> latin1.substring(-1, 5));
assertThrows(IndexOutOfBoundsException.class, () -> latin1.substring(0, 12));
assertThrows(IndexOutOfBoundsException.class, () -> latin1.substring(5, 4));

// Test on a sliced subview to ensure bounds are checked against size(), not the backing buffer
// length
CelCodePointArray sliced = latin1.slice(1, 4); // "ell", size = 3, buffer length = 11
assertThat(sliced.substring(0, 3)).isEqualTo("ell");
assertThat(sliced.substring(1, 2)).isEqualTo("l");

assertThrows(IndexOutOfBoundsException.class, () -> sliced.substring(-1, 2));
assertThrows(IndexOutOfBoundsException.class, () -> sliced.substring(0, 4));
assertThrows(IndexOutOfBoundsException.class, () -> sliced.substring(2, 1));
}

@Test
public void substring_basic() {
CelCodePointArray basic = CelCodePointArray.fromString("abc \uff20 def");
assertThat(basic).isInstanceOf(BasicCodePointArray.class);

assertThat(basic.substring(0, 3)).isEqualTo("abc");
assertThat(basic.substring(4, 5)).isEqualTo("\uff20");
assertThat(basic.substring(6, 9)).isEqualTo("def");
assertThat(basic.substring(0, 9)).isEqualTo("abc \uff20 def");
assertThat(basic.substring(3, 3)).isEmpty();

assertThrows(IndexOutOfBoundsException.class, () -> basic.substring(-1, 5));
assertThrows(IndexOutOfBoundsException.class, () -> basic.substring(0, 10));
assertThrows(IndexOutOfBoundsException.class, () -> basic.substring(5, 4));

// Test on a sliced subview to ensure bounds are checked against size(), not the backing buffer
// length
CelCodePointArray sliced = basic.slice(1, 5); // "bc \uff20", size = 4, buffer length = 9
assertThat(sliced.substring(0, 4)).isEqualTo("bc \uff20");

assertThrows(IndexOutOfBoundsException.class, () -> sliced.substring(-1, 2));
assertThrows(IndexOutOfBoundsException.class, () -> sliced.substring(0, 5));
assertThrows(IndexOutOfBoundsException.class, () -> sliced.substring(2, 1));
}

@Test
public void substring_supplemental() {
CelCodePointArray supp = CelCodePointArray.fromString(" text 가나다 😦😁😑 ");
assertThat(supp).isInstanceOf(SupplementalCodePointArray.class);

assertThat(supp.substring(0, 5)).isEqualTo(" text");
assertThat(supp.substring(10, 13)).isEqualTo("😦😁😑");
assertThat(supp.substring(0, supp.size())).isEqualTo(" text 가나다 😦😁😑 ");
assertThat(supp.substring(3, 3)).isEmpty();

assertThrows(IndexOutOfBoundsException.class, () -> supp.substring(-1, 5));
int greaterThanSize = supp.size() + 1;
assertThrows(IndexOutOfBoundsException.class, () -> supp.substring(0, greaterThanSize));
assertThrows(IndexOutOfBoundsException.class, () -> supp.substring(5, 4));

// Test on a sliced subview to ensure bounds are checked against size(), not the backing buffer
// length
CelCodePointArray sliced = supp.slice(1, 5); // "text", size = 4, buffer length = 15
assertThat(sliced.substring(0, 4)).isEqualTo("text");

assertThrows(IndexOutOfBoundsException.class, () -> sliced.substring(-1, 2));
assertThrows(IndexOutOfBoundsException.class, () -> sliced.substring(0, 5));
assertThrows(IndexOutOfBoundsException.class, () -> sliced.substring(2, 1));
}

@AutoValue
abstract static class LineOffsetTestCase {
abstract String text();
Expand Down
1 change: 1 addition & 0 deletions parser/src/main/java/dev/cel/parser/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ java_library(
"//common:source_location",
"//common/ast",
"//common/internal",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
"@maven//:org_jspecify_jspecify",
],
Expand Down
Loading
Loading