Skip to content

Commit 3bb5466

Browse files
committed
[Java,C++,C#] Improve DTO value validation.
Previously, in Java, the generated validation methods would not permit the null value, even for fields with optional presence. Now, we do allow these correctly. In this commit, I've also attempted to clean up, centralise, and test the logic that decides when validation for a particular side of a range needs to be included. We omit the validation when the native type cannot represent values outside of the range. There are still some gaps around validation, e.g., we do not validate array values.
1 parent 2821be8 commit 3bb5466

File tree

5 files changed

+428
-36
lines changed

5 files changed

+428
-36
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package uk.co.real_logic.sbe.generation.common;
2+
3+
import uk.co.real_logic.sbe.PrimitiveType;
4+
import uk.co.real_logic.sbe.PrimitiveValue;
5+
import uk.co.real_logic.sbe.ir.Encoding;
6+
import uk.co.real_logic.sbe.ir.Token;
7+
8+
public class DtoValidationUtil
9+
{
10+
private DtoValidationUtil()
11+
{
12+
}
13+
14+
public enum NativeIntegerSupport
15+
{
16+
/**
17+
* The target language supports both signed and unsigned integers natively and the generated code uses
18+
* these to represent the SBE types.
19+
*/
20+
SIGNED_AND_UNSIGNED,
21+
22+
/**
23+
* The target language only supports signed integers natively and the generated code uses the next biggest
24+
* signed integer type to represent the unsigned SBE types, except for UINT64 which is always represented
25+
* as a signed long.
26+
*/
27+
SIGNED_ONLY
28+
}
29+
30+
public static boolean nativeTypeRepresentsValuesLessThanValidRange(
31+
final Token fieldToken,
32+
final Encoding encoding,
33+
final NativeIntegerSupport integerSupport)
34+
{
35+
final PrimitiveType primitiveType = encoding.primitiveType();
36+
final PrimitiveValue minValue = encoding.applicableMinValue();
37+
38+
switch (minValue.representation())
39+
{
40+
case LONG:
41+
final long nativeMinValue = nativeTypeMinValue(primitiveType, integerSupport);
42+
final PrimitiveValue nullValue = encoding.applicableNullValue();
43+
return minValue.longValue() > nativeMinValue && !(
44+
fieldToken.isOptionalEncoding() &&
45+
nullValue.longValue() == nativeMinValue &&
46+
minValue.longValue() == nativeMinValue + 1L
47+
);
48+
49+
case DOUBLE:
50+
switch (primitiveType)
51+
{
52+
case FLOAT:
53+
return minValue.doubleValue() > -Float.MAX_VALUE;
54+
case DOUBLE:
55+
return minValue.doubleValue() > -Double.MAX_VALUE;
56+
default:
57+
throw new IllegalArgumentException(
58+
"Type did not have a double representation: " + primitiveType);
59+
}
60+
61+
default:
62+
throw new IllegalArgumentException(
63+
"Cannot understand the range of a type with representation: " + minValue.representation());
64+
}
65+
}
66+
67+
public static boolean nativeTypeRepresentsValuesGreaterThanValidRange(
68+
final Token fieldToken,
69+
final Encoding encoding,
70+
final NativeIntegerSupport integerSupport)
71+
{
72+
final PrimitiveType primitiveType = encoding.primitiveType();
73+
final PrimitiveValue maxValue = encoding.applicableMaxValue();
74+
75+
switch (maxValue.representation())
76+
{
77+
case LONG:
78+
final long nativeMaxValue = nativeTypeMaxValue(primitiveType, integerSupport);
79+
final PrimitiveValue nullValue = encoding.applicableNullValue();
80+
return maxValue.longValue() < nativeMaxValue && !(
81+
fieldToken.isOptionalEncoding() &&
82+
nullValue.longValue() == nativeMaxValue &&
83+
maxValue.longValue() + 1L == nativeMaxValue
84+
);
85+
86+
case DOUBLE:
87+
switch (primitiveType)
88+
{
89+
case FLOAT:
90+
return maxValue.doubleValue() < Float.MAX_VALUE;
91+
case DOUBLE:
92+
return maxValue.doubleValue() < Double.MAX_VALUE;
93+
default:
94+
throw new IllegalArgumentException(
95+
"Type did not have a double representation: " + primitiveType);
96+
}
97+
98+
default:
99+
throw new IllegalArgumentException(
100+
"Cannot understand the range of a type with representation: " + maxValue.representation());
101+
}
102+
}
103+
104+
private static long nativeTypeMinValue(
105+
final PrimitiveType primitiveType,
106+
final NativeIntegerSupport integerSupport)
107+
{
108+
switch (primitiveType)
109+
{
110+
case CHAR:
111+
return Character.MIN_VALUE;
112+
case INT8:
113+
return Byte.MIN_VALUE;
114+
case INT16:
115+
return Short.MIN_VALUE;
116+
case INT32:
117+
return Integer.MIN_VALUE;
118+
case INT64:
119+
return Long.MIN_VALUE;
120+
case UINT8:
121+
if (integerSupport == NativeIntegerSupport.SIGNED_ONLY)
122+
{
123+
return Short.MIN_VALUE;
124+
}
125+
return 0L;
126+
case UINT16:
127+
if (integerSupport == NativeIntegerSupport.SIGNED_ONLY)
128+
{
129+
return Integer.MIN_VALUE;
130+
}
131+
return 0L;
132+
case UINT32:
133+
if (integerSupport == NativeIntegerSupport.SIGNED_ONLY)
134+
{
135+
return Long.MIN_VALUE;
136+
}
137+
return 0L;
138+
case UINT64:
139+
return 0L;
140+
default:
141+
throw new IllegalArgumentException("Type did not have a long representation: " + primitiveType);
142+
}
143+
}
144+
145+
private static long nativeTypeMaxValue(
146+
final PrimitiveType primitiveType,
147+
final NativeIntegerSupport integerSupport)
148+
{
149+
switch (primitiveType)
150+
{
151+
case CHAR:
152+
return Character.MAX_VALUE;
153+
case INT8:
154+
return Byte.MAX_VALUE;
155+
case INT16:
156+
return Short.MAX_VALUE;
157+
case INT32:
158+
return Integer.MAX_VALUE;
159+
case INT64:
160+
return Long.MAX_VALUE;
161+
case UINT8:
162+
if (integerSupport == NativeIntegerSupport.SIGNED_ONLY)
163+
{
164+
return Short.MAX_VALUE;
165+
}
166+
return 0xFFL;
167+
case UINT16:
168+
if (integerSupport == NativeIntegerSupport.SIGNED_ONLY)
169+
{
170+
return Integer.MAX_VALUE;
171+
}
172+
return 0xFFFFL;
173+
case UINT32:
174+
if (integerSupport == NativeIntegerSupport.SIGNED_ONLY)
175+
{
176+
return Long.MAX_VALUE;
177+
}
178+
return 0xFFFFFFFFL;
179+
case UINT64:
180+
return 0xFFFFFFFFFFFFFFFFL;
181+
default:
182+
throw new IllegalArgumentException("Type did not have a long representation: " + primitiveType);
183+
}
184+
}
185+
}

sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppDtoGenerator.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535

3636
import static uk.co.real_logic.sbe.generation.Generators.toLowerFirstChar;
3737
import static uk.co.real_logic.sbe.generation.Generators.toUpperFirstChar;
38+
import static uk.co.real_logic.sbe.generation.common.DtoValidationUtil.NativeIntegerSupport.SIGNED_AND_UNSIGNED;
39+
import static uk.co.real_logic.sbe.generation.common.DtoValidationUtil.nativeTypeRepresentsValuesGreaterThanValidRange;
40+
import static uk.co.real_logic.sbe.generation.common.DtoValidationUtil.nativeTypeRepresentsValuesLessThanValidRange;
3841
import static uk.co.real_logic.sbe.generation.cpp.CppUtil.*;
3942
import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields;
4043
import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups;
@@ -1591,8 +1594,11 @@ private static void generateSingleValuePropertyValidateMethod(
15911594

15921595
String value = "value";
15931596

1594-
final boolean mustPreventLesser = !encoding.applicableMinValue().equals(encoding.primitiveType().minValue());
1595-
final boolean mustPreventGreater = !encoding.applicableMaxValue().equals(encoding.primitiveType().maxValue());
1597+
final boolean mustPreventLesser =
1598+
nativeTypeRepresentsValuesLessThanValidRange(fieldToken, encoding, SIGNED_AND_UNSIGNED);
1599+
1600+
final boolean mustPreventGreater =
1601+
nativeTypeRepresentsValuesGreaterThanValidRange(fieldToken, encoding, SIGNED_AND_UNSIGNED);
15961602

15971603
if (fieldToken.isOptionalEncoding())
15981604
{

sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpDtoGenerator.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
import java.util.Set;
3636
import java.util.function.Predicate;
3737

38+
import static uk.co.real_logic.sbe.generation.common.DtoValidationUtil.NativeIntegerSupport.SIGNED_AND_UNSIGNED;
39+
import static uk.co.real_logic.sbe.generation.common.DtoValidationUtil.nativeTypeRepresentsValuesGreaterThanValidRange;
40+
import static uk.co.real_logic.sbe.generation.common.DtoValidationUtil.nativeTypeRepresentsValuesLessThanValidRange;
3841
import static uk.co.real_logic.sbe.generation.csharp.CSharpUtil.*;
3942
import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields;
4043
import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups;
@@ -1285,7 +1288,9 @@ private void generateSingleValueProperty(
12851288
.append("}\n");
12861289
}
12871290

1288-
final boolean mustPreventLesser = !encoding.applicableMinValue().equals(encoding.primitiveType().minValue());
1291+
final boolean mustPreventLesser =
1292+
nativeTypeRepresentsValuesLessThanValidRange(fieldToken, typeToken.encoding(), SIGNED_AND_UNSIGNED);
1293+
12891294
if (mustPreventLesser)
12901295
{
12911296
sb.append(indent).append(INDENT)
@@ -1299,7 +1304,9 @@ private void generateSingleValueProperty(
12991304
.append("}\n");
13001305
}
13011306

1302-
final boolean mustPreventGreater = !encoding.applicableMaxValue().equals(encoding.primitiveType().maxValue());
1307+
final boolean mustPreventGreater =
1308+
nativeTypeRepresentsValuesGreaterThanValidRange(fieldToken, typeToken.encoding(), SIGNED_AND_UNSIGNED);
1309+
13031310
if (mustPreventGreater)
13041311
{
13051312
sb.append(indent).append(INDENT)

0 commit comments

Comments
 (0)