Skip to content

Commit bd75795

Browse files
authored
Merge pull request #22606 from hvitved/unbound-list-unicode-encoding
UnboundList: More compact string representation
2 parents 5f5db99 + cfb2f0a commit bd75795

1 file changed

Lines changed: 42 additions & 5 deletions

File tree

shared/util/codeql/util/UnboundList.qll

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ overlay[local?]
1010
module;
1111

1212
private import Location
13+
private import Strings
1314

1415
/** Provides the input to `Make`. */
1516
signature module InputSig<LocationSig Location> {
@@ -52,7 +53,43 @@ module Make<LocationSig Location, InputSig<Location> Input> {
5253
/** Gets the rank of element `e`, which is used internally in the string encoding. */
5354
int getRank(Element e) { e = DenseRank<DenseRankInput>::denseRank(result) }
5455

55-
private string encode(Element e) { result = getRank(e).toString() }
56+
/** Gets the ASCII printable excluding `.` with zero-based index `code`. */
57+
pragma[nomagic]
58+
private string interpretAsciiCode(int code) {
59+
exists(int dot, int c |
60+
c = code + 1 and
61+
// `.` is used as element separator, so cannot be used to encode elements
62+
dot = asciiPrintable(".") and
63+
if c < dot then c = asciiPrintable(result) else c + 1 = asciiPrintable(result)
64+
)
65+
}
66+
67+
private int asciiCodes() { result = strictcount(interpretAsciiCode(_)) }
68+
69+
/**
70+
* Gets the `i`th digit (modulo `asciiCodes()`) in a base-`asciiCodes()` integer
71+
* representation of `getRank(e)`.
72+
*/
73+
private int getAsciiCodePart(Element e, int i) {
74+
result = getRank(e) and
75+
i = 0
76+
or
77+
exists(int mid |
78+
mid = getAsciiCodePart(e, i - 1) and
79+
result = mid / asciiCodes() and
80+
result > 0
81+
)
82+
}
83+
84+
pragma[nomagic]
85+
private string encode(Element e) {
86+
result =
87+
strictconcat(string s, int i |
88+
s = interpretAsciiCode(getAsciiCodePart(e, i) % asciiCodes())
89+
|
90+
s order by i
91+
)
92+
}
5693

5794
bindingset[s]
5895
private Element decode(string s) { encode(result) = s }
@@ -88,7 +125,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
88125
// Same as
89126
// `result = count(this.indexOf("."))`
90127
// but performs better because it doesn't use an aggregate
91-
result = this.regexpReplaceAll("[0-9]+", "").length()
128+
result = this.regexpReplaceAll("[^\\.]+", "").length()
92129
}
93130

94131
/** Gets the list obtained by appending `suffix` onto this list. */
@@ -123,7 +160,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
123160
// `regexpCapture` will then always join in both groups, only to afterwards filter
124161
// based on the requested group (the group number is not part of the binding set
125162
// of `regexpCapture`)
126-
elem = this.regexpCapture("^([0-9]+)\\..*$", 1) and
163+
elem = this.regexpCapture("^([^\\.]+)\\..*$", 1) and
127164
e = decode(elem) and
128165
suffix = this.suffix(elem.length() + 1)
129166
)
@@ -133,7 +170,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
133170
bindingset[this]
134171
predicate isSnoc(UnboundList prefix, Element e) {
135172
// same remark as above about not using multiple capture groups
136-
prefix = this.regexpCapture("^(|.+\\.)[0-9]+\\.$", 1) and
173+
prefix = this.regexpCapture("^(|.+\\.)[^\\.]+\\.$", 1) and
137174
e = decode(this.substring(prefix.stringLength(), this.stringLength() - 1))
138175
}
139176

@@ -148,7 +185,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
148185
*/
149186
bindingset[this]
150187
UnboundList getProperPrefix(int i) {
151-
exists(string regexp, int occurrenceOffset | regexp = "[0-9]+\\." |
188+
exists(string regexp, int occurrenceOffset | regexp = "[^\\.]+\\." |
152189
exists(this.regexpFind(regexp, i, occurrenceOffset)) and
153190
result = this.prefix(occurrenceOffset)
154191
)

0 commit comments

Comments
 (0)