Skip to content

Commit 97f7960

Browse files
committed
Copy NumberUtils.qll from Ruby into shared util pack
1 parent 4fa484d commit 97f7960

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

shared/util/codeql/util/Numbers.qll

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
* Provides predicates for working with numeric values and their string
3+
* representations.
4+
*/
5+
6+
/**
7+
* Gets the integer value of `binary` when interpreted as binary. `binary` must
8+
* contain only the digits 0 and 1. For values greater than
9+
* 01111111111111111111111111111111 (2^31-1, the maximum value that `int` can
10+
* represent), there is no result.
11+
*
12+
* ```
13+
* "0" => 0
14+
* "01" => 1
15+
* "1010101" => 85
16+
* ```
17+
*/
18+
bindingset[binary]
19+
int parseBinaryInt(string binary) {
20+
exists(string stripped | stripped = stripLeadingZeros(binary) |
21+
stripped.length() <= 31 and
22+
result >= 0 and
23+
result =
24+
sum(int index, string c, int digit |
25+
c = stripped.charAt(index) and
26+
digit = "01".indexOf(c)
27+
|
28+
twoToThe(stripped.length() - 1 - index) * digit
29+
)
30+
)
31+
}
32+
33+
/**
34+
* Gets the integer value of `hex` when interpreted as hex. `hex` must be a
35+
* valid hexadecimal string. For values greater than 7FFFFFFF (2^31-1, the
36+
* maximum value that `int` can represent), there is no result.
37+
*
38+
* ```
39+
* "0" => 0
40+
* "FF" => 255
41+
* "f00d" => 61453
42+
* ```
43+
*/
44+
bindingset[hex]
45+
int parseHexInt(string hex) {
46+
exists(string stripped | stripped = stripLeadingZeros(hex) |
47+
stripped.length() <= 8 and
48+
result >= 0 and
49+
result =
50+
sum(int index, string c |
51+
c = stripped.charAt(index)
52+
|
53+
sixteenToThe(stripped.length() - 1 - index) * toHex(c)
54+
)
55+
)
56+
}
57+
58+
/**
59+
* Gets the integer value of `octal` when interpreted as octal. `octal` must be
60+
* a valid octal string containing only the digits 0-7. For values greater than
61+
* 17777777777 (2^31-1, the maximum value that `int` can represent), there is no
62+
* result.
63+
*
64+
* ```
65+
* "0" => 0
66+
* "77" => 63
67+
* "76543210" => 16434824
68+
* ```
69+
*/
70+
bindingset[octal]
71+
int parseOctalInt(string octal) {
72+
exists(string stripped | stripped = stripLeadingZeros(octal) |
73+
stripped.length() <= 11 and
74+
result >= 0 and
75+
result =
76+
sum(int index, string c, int digit |
77+
c = stripped.charAt(index) and
78+
digit = "01234567".indexOf(c)
79+
|
80+
eightToThe(stripped.length() - 1 - index) * digit
81+
)
82+
)
83+
}
84+
85+
/** Gets the integer value of the `hex` char. */
86+
private int toHex(string hex) {
87+
hex = [0 .. 9].toString() and
88+
result = hex.toInt()
89+
or
90+
result = 10 and hex = ["a", "A"]
91+
or
92+
result = 11 and hex = ["b", "B"]
93+
or
94+
result = 12 and hex = ["c", "C"]
95+
or
96+
result = 13 and hex = ["d", "D"]
97+
or
98+
result = 14 and hex = ["e", "E"]
99+
or
100+
result = 15 and hex = ["f", "F"]
101+
}
102+
103+
/**
104+
* Gets the value of 16 to the power of `n`. Holds only for `n` in the range
105+
* 0..7 (inclusive).
106+
*/
107+
int sixteenToThe(int n) {
108+
// 16**7 is the largest power of 16 that fits in an int.
109+
n in [0 .. 7] and result = 1.bitShiftLeft(4 * n)
110+
}
111+
112+
/**
113+
* Gets the value of 8 to the power of `n`. Holds only for `n` in the range
114+
* 0..10 (inclusive).
115+
*/
116+
int eightToThe(int n) {
117+
// 8**10 is the largest power of 8 that fits in an int.
118+
n in [0 .. 10] and result = 1.bitShiftLeft(3 * n)
119+
}
120+
121+
/**
122+
* Gets the value of 2 to the power of `n`. Holds only for `n` in the range
123+
* 0..30 (inclusive).
124+
*/
125+
int twoToThe(int n) { n in [0 .. 30] and result = 1.bitShiftLeft(n) }
126+
127+
/** Gets `s` with any leading "0" characters removed. */
128+
bindingset[s]
129+
private string stripLeadingZeros(string s) { result = s.regexpCapture("0*(.*)", 1) }

0 commit comments

Comments
 (0)