Skip to content

Commit 85e5845

Browse files
committed
Add benchmarks for version parsing
1 parent 49ecb5d commit 85e5845

13 files changed

+3389
-0
lines changed

src/com/komanov/ver/BUILD

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package(default_visibility = ["//src/com/komanov/ver:__subpackages__"])
2+
3+
scala_library(
4+
name = "ver",
5+
srcs = [
6+
"ParseVersionNoAllocUtil.java",
7+
"ParseVersionUtil.java",
8+
"Version.scala",
9+
"VersionNoAlloc.scala",
10+
],
11+
)
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
package com.komanov.ver;
2+
3+
public abstract class ParseVersionNoAllocUtil {
4+
private ParseVersionNoAllocUtil() {
5+
}
6+
7+
public static final int MaxVersionSize = Version$.MODULE$.MaxVersionSize();
8+
private static final long Invalid = VersionNoAlloc$.MODULE$.Invalid();
9+
10+
public static long parse(String v) {
11+
final int len = v.length();
12+
int major = -1;
13+
int minor = -1;
14+
int lastDotIndex = -1;
15+
16+
for (int i = 0; i < len; ++i) {
17+
char ch = v.charAt(i);
18+
switch (ch) {
19+
case '0':
20+
case '1':
21+
case '2':
22+
case '3':
23+
case '4':
24+
case '5':
25+
case '6':
26+
case '7':
27+
case '8':
28+
case '9':
29+
if (i - (lastDotIndex + 1) > 5) {
30+
return Invalid; // longer than MaxVersionSize
31+
}
32+
break;
33+
34+
case '.':
35+
if (major == -1) {
36+
int parsed = parseIntSafeEmpty(v, 0, i);
37+
if (parsed == -1 || parsed > MaxVersionSize) {
38+
return Invalid;
39+
}
40+
major = parsed;
41+
} else if (minor == -1) {
42+
int parsed = parseIntSafeEmpty(v, lastDotIndex + 1, i);
43+
if (parsed == -1 || parsed > MaxVersionSize) {
44+
return Invalid;
45+
}
46+
minor = parsed;
47+
} else {
48+
return Invalid; // 3rd dot
49+
}
50+
lastDotIndex = i;
51+
break;
52+
53+
default:
54+
return Invalid; // invalid character
55+
}
56+
}
57+
58+
if (major != -1 && minor != -1) {
59+
int parsed = parseIntSafeEmpty(v, lastDotIndex + 1, len);
60+
if (parsed == -1 || parsed > MaxVersionSize) {
61+
return Invalid;
62+
}
63+
return VersionNoAlloc.version(major, minor, parsed);
64+
} else {
65+
return Invalid;
66+
}
67+
}
68+
69+
public static long parseNoSwitch(String v) {
70+
final int len = v.length();
71+
int major = -1;
72+
int minor = -1;
73+
int lastDotIndex = -1;
74+
75+
for (int i = 0; i < len; ++i) {
76+
char ch = v.charAt(i);
77+
if (ch >= '0' && ch <= '9') {
78+
if (i - (lastDotIndex + 1) > 5) {
79+
return Invalid; // longer than MaxVersionSize
80+
}
81+
} else if (ch == '.') {
82+
if (major == -1) {
83+
int parsed = parseIntSafeEmpty(v, 0, i);
84+
if (parsed == -1 || parsed > MaxVersionSize) {
85+
return Invalid;
86+
}
87+
major = parsed;
88+
} else if (minor == -1) {
89+
int parsed = parseIntSafeEmpty(v, lastDotIndex + 1, i);
90+
if (parsed == -1 || parsed > MaxVersionSize) {
91+
return Invalid;
92+
}
93+
minor = parsed;
94+
} else {
95+
return Invalid; // 3rd dot
96+
}
97+
lastDotIndex = i;
98+
} else {
99+
return Invalid; // invalid character
100+
}
101+
}
102+
103+
if (major != -1 && minor != -1) {
104+
int parsed = parseIntSafeEmpty(v, lastDotIndex + 1, len);
105+
if (parsed == -1 || parsed > MaxVersionSize) {
106+
return Invalid;
107+
}
108+
return VersionNoAlloc.version(major, minor, parsed);
109+
} else {
110+
return Invalid;
111+
}
112+
}
113+
114+
public static long parseHardCore(String v) {
115+
final int len = v.length();
116+
int major = 0;
117+
int minor = 0;
118+
int fix = 0;
119+
int lastDotIndex = -1;
120+
int currentPart = 0;
121+
122+
for (int i = 0; i < len; ++i) {
123+
char ch = v.charAt(i);
124+
switch (ch) {
125+
case '0':
126+
case '1':
127+
case '2':
128+
case '3':
129+
case '4':
130+
case '5':
131+
case '6':
132+
case '7':
133+
case '8':
134+
case '9':
135+
int currentDigit = (int) ch - '0';
136+
switch (currentPart) {
137+
case 0:
138+
major = major * 10 + currentDigit;
139+
if (major > MaxVersionSize) {
140+
return Invalid;
141+
}
142+
break;
143+
case 1:
144+
minor = minor * 10 + currentDigit;
145+
if (minor > MaxVersionSize) {
146+
return Invalid;
147+
}
148+
break;
149+
case 2:
150+
fix = fix * 10 + currentDigit;
151+
if (fix > MaxVersionSize) {
152+
return Invalid;
153+
}
154+
break;
155+
156+
default:
157+
throw new IllegalStateException("should never happen");
158+
}
159+
160+
if (i - (lastDotIndex + 1) > 5) {
161+
return Invalid; // longer than MaxVersionSize
162+
}
163+
break;
164+
165+
case '.':
166+
if (lastDotIndex + 1 == i) {
167+
return Invalid; // ..
168+
}
169+
170+
++currentPart;
171+
if (currentPart > 2) {
172+
return Invalid; // extra dot
173+
}
174+
175+
lastDotIndex = i;
176+
break;
177+
178+
default:
179+
return Invalid; // invalid character
180+
}
181+
}
182+
183+
return currentPart == 2 && lastDotIndex != len - 1 ? VersionNoAlloc.version(major, minor, fix) : Invalid;
184+
}
185+
186+
private static int parseNumberUntilDotOrLength(final String s, final int fromIndex, final int len) {
187+
int value = 0;
188+
int i = fromIndex;
189+
final int endIndex = Math.min(fromIndex + 1 + 5 + 1, len);
190+
while (i < endIndex) {
191+
char ch = s.charAt(i);
192+
int digit = ch - 48; // 48 == (int) '0'
193+
if (digit == -2) { // dot
194+
if (i == fromIndex) {
195+
return -1;
196+
}
197+
198+
if (value > MaxVersionSize) {
199+
return -1;
200+
}
201+
202+
return i + (value << 8);
203+
} else if (digit < 0 || digit > 9) {
204+
return -1;
205+
} else {
206+
value = value * 10 + digit;
207+
++i;
208+
}
209+
}
210+
211+
return i == fromIndex || value > MaxVersionSize ? -1 : i + (value << 8);
212+
}
213+
214+
public static long parseHardCore2(String v) {
215+
final int len = v.length();
216+
217+
int majorValue = parseNumberUntilDotOrLength(v, 0, len);
218+
if (majorValue == -1) {
219+
return Invalid;
220+
}
221+
222+
int minorValue = parseNumberUntilDotOrLength(v, (majorValue & 0xff) + 1, len);
223+
if (minorValue == -1) {
224+
return Invalid;
225+
}
226+
227+
int fixValue = parseNumberUntilDotOrLength(v, (minorValue & 0xff) + 1, len);
228+
if (fixValue == -1) {
229+
return Invalid;
230+
}
231+
232+
if ((fixValue & 0xff) != len) {
233+
return Invalid;
234+
}
235+
236+
return VersionNoAlloc.version(majorValue >> 8, minorValue >> 8, fixValue >> 8);
237+
}
238+
239+
public static long parseHardCore3SingleLoop(String v) {
240+
final int len = v.length();
241+
242+
int major = 0;
243+
int minor = 0;
244+
245+
int current = 0;
246+
int dots = 0;
247+
int lastDotIndex = -1;
248+
249+
for (int i = 0; i < len; ++i) {
250+
final char ch = v.charAt(i);
251+
int digit = ch - 48;
252+
if (digit == -2) {
253+
if (lastDotIndex + 1 == i) {
254+
return Invalid;
255+
}
256+
257+
if (current < 0 || current > MaxVersionSize) {
258+
return Invalid;
259+
}
260+
261+
switch (dots) {
262+
case 0:
263+
major = current;
264+
dots = 1;
265+
break;
266+
267+
case 1:
268+
minor = current;
269+
dots = 2;
270+
break;
271+
272+
default:
273+
return Invalid;
274+
}
275+
276+
lastDotIndex = i;
277+
current = 0;
278+
} else if (digit < 0 || digit > 9) {
279+
return Invalid;
280+
} else {
281+
// overflow is possible!
282+
current = current * 10 + digit;
283+
}
284+
}
285+
286+
if (dots != 2 || lastDotIndex == len - 1) {
287+
return Invalid;
288+
}
289+
if (current < 0 || current > MaxVersionSize) {
290+
return Invalid;
291+
}
292+
293+
return VersionNoAlloc.version(major, minor, current);
294+
}
295+
296+
public static int parseIntSafeEmpty(String s, int from, int to) {
297+
return from >= to ? -1 : Integer.parseUnsignedInt(s, from, to, 10);
298+
}
299+
}

0 commit comments

Comments
 (0)