Skip to content

Commit 2db8192

Browse files
committed
UnboundList: More compact string representation using Unicode
1 parent 7a367c9 commit 2db8192

1 file changed

Lines changed: 33 additions & 5 deletions

File tree

shared/util/codeql/util/UnboundList.qll

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,35 @@ module Make<LocationSig Location, InputSig<Location> Input> {
5252
/** Gets the rank of element `e`, which is used internally in the string encoding. */
5353
int getRank(Element e) { e = DenseRank<DenseRankInput>::denseRank(result) }
5454

55-
private string encode(Element e) { result = getRank(e).toString() }
55+
pragma[nomagic]
56+
private string interpretUnicodeCodePoint(int codePoint) {
57+
codePoint = [0, getRank(_)] and
58+
codePoint.toUnicode() = result and
59+
result != "." // used as element separator
60+
}
61+
62+
private int unicodeCodePoints() { result = strictcount(interpretUnicodeCodePoint(_)) }
63+
64+
private int getUnicodeCodePointPart(Element e, int i) {
65+
result = getRank(e) and
66+
i = 0
67+
or
68+
exists(int mid |
69+
mid = getUnicodeCodePointPart(e, i - 1) and
70+
mid > 0 and
71+
result = mid / unicodeCodePoints()
72+
)
73+
}
74+
75+
pragma[nomagic]
76+
private string encode(Element e) {
77+
result =
78+
strictconcat(string s, int i |
79+
s = interpretUnicodeCodePoint(getUnicodeCodePointPart(e, i) % unicodeCodePoints())
80+
|
81+
s order by i
82+
)
83+
}
5684

5785
bindingset[s]
5886
private Element decode(string s) { encode(result) = s }
@@ -88,7 +116,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
88116
// Same as
89117
// `result = count(this.indexOf("."))`
90118
// but performs better because it doesn't use an aggregate
91-
result = this.regexpReplaceAll("[0-9]+", "").length()
119+
result = this.regexpReplaceAll("[^\\.]+", "").length()
92120
}
93121

94122
/** Gets the list obtained by appending `suffix` onto this list. */
@@ -123,7 +151,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
123151
// `regexpCapture` will then always join in both groups, only to afterwards filter
124152
// based on the requested group (the group number is not part of the binding set
125153
// of `regexpCapture`)
126-
elem = this.regexpCapture("^([0-9]+)\\..*$", 1) and
154+
elem = this.regexpCapture("^([^\\.]+)\\..*$", 1) and
127155
e = decode(elem) and
128156
suffix = this.suffix(elem.length() + 1)
129157
)
@@ -133,7 +161,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
133161
bindingset[this]
134162
predicate isSnoc(UnboundList prefix, Element e) {
135163
// same remark as above about not using multiple capture groups
136-
prefix = this.regexpCapture("^(|.+\\.)[0-9]+\\.$", 1) and
164+
prefix = this.regexpCapture("^(|.+\\.)[^\\.]+\\.$", 1) and
137165
e = decode(this.substring(prefix.stringLength(), this.stringLength() - 1))
138166
}
139167

@@ -148,7 +176,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
148176
*/
149177
bindingset[this]
150178
UnboundList getProperPrefix(int i) {
151-
exists(string regexp, int occurrenceOffset | regexp = "[0-9]+\\." |
179+
exists(string regexp, int occurrenceOffset | regexp = "[^\\.]+\\." |
152180
exists(this.regexpFind(regexp, i, occurrenceOffset)) and
153181
result = this.prefix(occurrenceOffset)
154182
)

0 commit comments

Comments
 (0)