From bdbe039b4d7a5b8f7a3e0cc66d5fedd0ad572a06 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 17 Sep 2026 13:29:01 +0200 Subject: [PATCH 1/3] UnboundList: More compact string representation using Unicode --- shared/util/codeql/util/UnboundList.qll | 38 +++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/shared/util/codeql/util/UnboundList.qll b/shared/util/codeql/util/UnboundList.qll index 622895a6bd01..e4104e06222c 100644 --- a/shared/util/codeql/util/UnboundList.qll +++ b/shared/util/codeql/util/UnboundList.qll @@ -52,7 +52,35 @@ module Make Input> { /** Gets the rank of element `e`, which is used internally in the string encoding. */ int getRank(Element e) { e = DenseRank::denseRank(result) } - private string encode(Element e) { result = getRank(e).toString() } + pragma[nomagic] + private string interpretUnicodeCodePoint(int codePoint) { + codePoint = [0, getRank(_)] and + codePoint.toUnicode() = result and + result != "." // used as element separator + } + + private int unicodeCodePoints() { result = strictcount(interpretUnicodeCodePoint(_)) } + + private int getUnicodeCodePointPart(Element e, int i) { + result = getRank(e) and + i = 0 + or + exists(int mid | + mid = getUnicodeCodePointPart(e, i - 1) and + mid > 0 and + result = mid / unicodeCodePoints() + ) + } + + pragma[nomagic] + private string encode(Element e) { + result = + strictconcat(string s, int i | + s = interpretUnicodeCodePoint(getUnicodeCodePointPart(e, i) % unicodeCodePoints()) + | + s order by i + ) + } bindingset[s] private Element decode(string s) { encode(result) = s } @@ -88,7 +116,7 @@ module Make Input> { // Same as // `result = count(this.indexOf("."))` // but performs better because it doesn't use an aggregate - result = this.regexpReplaceAll("[0-9]+", "").length() + result = this.regexpReplaceAll("[^\\.]+", "").length() } /** Gets the list obtained by appending `suffix` onto this list. */ @@ -123,7 +151,7 @@ module Make Input> { // `regexpCapture` will then always join in both groups, only to afterwards filter // based on the requested group (the group number is not part of the binding set // of `regexpCapture`) - elem = this.regexpCapture("^([0-9]+)\\..*$", 1) and + elem = this.regexpCapture("^([^\\.]+)\\..*$", 1) and e = decode(elem) and suffix = this.suffix(elem.length() + 1) ) @@ -133,7 +161,7 @@ module Make Input> { bindingset[this] predicate isSnoc(UnboundList prefix, Element e) { // same remark as above about not using multiple capture groups - prefix = this.regexpCapture("^(|.+\\.)[0-9]+\\.$", 1) and + prefix = this.regexpCapture("^(|.+\\.)[^\\.]+\\.$", 1) and e = decode(this.substring(prefix.stringLength(), this.stringLength() - 1)) } @@ -148,7 +176,7 @@ module Make Input> { */ bindingset[this] UnboundList getProperPrefix(int i) { - exists(string regexp, int occurrenceOffset | regexp = "[0-9]+\\." | + exists(string regexp, int occurrenceOffset | regexp = "[^\\.]+\\." | exists(this.regexpFind(regexp, i, occurrenceOffset)) and result = this.prefix(occurrenceOffset) ) From 449e377da1e8f4f95c0b6e4f714df0c6096a46a2 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 17 Sep 2026 14:47:47 +0200 Subject: [PATCH 2/3] UnboundList: Use ASCII encoding instead of Unicode --- shared/util/codeql/util/UnboundList.qll | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/shared/util/codeql/util/UnboundList.qll b/shared/util/codeql/util/UnboundList.qll index e4104e06222c..dd92ff24ffcf 100644 --- a/shared/util/codeql/util/UnboundList.qll +++ b/shared/util/codeql/util/UnboundList.qll @@ -10,6 +10,7 @@ overlay[local?] module; private import Location +private import Strings /** Provides the input to `Make`. */ signature module InputSig { @@ -53,22 +54,25 @@ module Make Input> { int getRank(Element e) { e = DenseRank::denseRank(result) } pragma[nomagic] - private string interpretUnicodeCodePoint(int codePoint) { - codePoint = [0, getRank(_)] and - codePoint.toUnicode() = result and - result != "." // used as element separator + private string interpretAsciiCode(int code) { + exists(int dot, int c | + c = code + 1 and + // `.` is used as element separator, so cannot be used to encode elements + dot = asciiPrintable(".") and + if c < dot then c = asciiPrintable(result) else c + 1 = asciiPrintable(result) + ) } - private int unicodeCodePoints() { result = strictcount(interpretUnicodeCodePoint(_)) } + private int asciiCodes() { result = strictcount(interpretAsciiCode(_)) } - private int getUnicodeCodePointPart(Element e, int i) { + private int getAsciiCodePart(Element e, int i) { result = getRank(e) and i = 0 or exists(int mid | - mid = getUnicodeCodePointPart(e, i - 1) and + mid = getAsciiCodePart(e, i - 1) and mid > 0 and - result = mid / unicodeCodePoints() + result = mid / asciiCodes() ) } @@ -76,7 +80,7 @@ module Make Input> { private string encode(Element e) { result = strictconcat(string s, int i | - s = interpretUnicodeCodePoint(getUnicodeCodePointPart(e, i) % unicodeCodePoints()) + s = interpretAsciiCode(getAsciiCodePart(e, i) % asciiCodes()) | s order by i ) From cfb2f0a3879f0c45296dcdd27c3582ebc97fceba Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 18 Sep 2026 09:04:51 +0200 Subject: [PATCH 3/3] Address review comments --- shared/util/codeql/util/UnboundList.qll | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/shared/util/codeql/util/UnboundList.qll b/shared/util/codeql/util/UnboundList.qll index dd92ff24ffcf..a08f76f9c362 100644 --- a/shared/util/codeql/util/UnboundList.qll +++ b/shared/util/codeql/util/UnboundList.qll @@ -53,6 +53,7 @@ module Make Input> { /** Gets the rank of element `e`, which is used internally in the string encoding. */ int getRank(Element e) { e = DenseRank::denseRank(result) } + /** Gets the ASCII printable excluding `.` with zero-based index `code`. */ pragma[nomagic] private string interpretAsciiCode(int code) { exists(int dot, int c | @@ -65,14 +66,18 @@ module Make Input> { private int asciiCodes() { result = strictcount(interpretAsciiCode(_)) } + /** + * Gets the `i`th digit (modulo `asciiCodes()`) in a base-`asciiCodes()` integer + * representation of `getRank(e)`. + */ private int getAsciiCodePart(Element e, int i) { result = getRank(e) and i = 0 or exists(int mid | mid = getAsciiCodePart(e, i - 1) and - mid > 0 and - result = mid / asciiCodes() + result = mid / asciiCodes() and + result > 0 ) }