Skip to content

Commit 4aae24e

Browse files
authored
Read/write/compare uint32 and uint64 proto fields as unsigned values. (#54)
The proto field is read as Int and Long, we need to manually convert them to UInt and ULong.
1 parent 3a75214 commit 4aae24e

File tree

13 files changed

+173
-154
lines changed

13 files changed

+173
-154
lines changed

src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter/AnyInFilter.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ import org.wfanet.virtualpeople.common.fieldfilter.utils.*
2525
/**
2626
* The implementation of [FieldFilter] when op is ANY_IN in config.
2727
*
28-
* The supported ValueTypes are: [Int], [Long], [Boolean], [EnumValueDescriptor], [String]
28+
* The supported ValueTypes are: [Int], [UInt], [Long], [ULong], [Boolean], [EnumValueDescriptor],
29+
* [String]
2930
*
3031
* Always use [FieldFilter.create]. Users should never construct a [AnyInFilter] directly.
3132
*/
@@ -60,9 +61,9 @@ internal abstract class AnyInFilter(val fieldDescriptors: List<FieldDescriptor>)
6061
}
6162

6263
return when (fieldDescriptors.last().type) {
63-
Type.INT32,
64-
Type.UINT32 -> AnyInFilterImpl<Int>(fieldDescriptors, parseValue(config.value))
65-
Type.UINT64,
64+
Type.INT32 -> AnyInFilterImpl<Int>(fieldDescriptors, parseValue(config.value))
65+
Type.UINT32 -> AnyInFilterImpl<UInt>(fieldDescriptors, parseValue(config.value))
66+
Type.UINT64 -> AnyInFilterImpl<ULong>(fieldDescriptors, parseValue(config.value))
6667
Type.INT64 -> AnyInFilterImpl<Long>(fieldDescriptors, parseValue(config.value))
6768
Type.BOOL -> AnyInFilterImpl<Boolean>(fieldDescriptors, parseValue(config.value))
6869
Type.STRING -> AnyInFilterImpl<String>(fieldDescriptors, parseValue(config.value))
@@ -90,9 +91,9 @@ internal class AnyInFilterImpl<V>(
9091
/** Guaranteed safe since all types come from the same fieldDescriptor.Type */
9192
val values =
9293
when (fieldDescriptors.last().type) {
93-
Type.INT32,
94-
Type.UINT32 -> getAllValuesFromRepeatedProto<Int>(messageOrBuilder, fieldDescriptors)
95-
Type.UINT64,
94+
Type.INT32 -> getAllValuesFromRepeatedProto<Int>(messageOrBuilder, fieldDescriptors)
95+
Type.UINT32 -> getAllValuesFromRepeatedProto<UInt>(messageOrBuilder, fieldDescriptors)
96+
Type.UINT64 -> getAllValuesFromRepeatedProto<ULong>(messageOrBuilder, fieldDescriptors)
9697
Type.INT64 -> getAllValuesFromRepeatedProto<Long>(messageOrBuilder, fieldDescriptors)
9798
Type.BOOL -> getAllValuesFromRepeatedProto<Boolean>(messageOrBuilder, fieldDescriptors)
9899
Type.STRING -> getAllValuesFromRepeatedProto<String>(messageOrBuilder, fieldDescriptors)

src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter/EqualFilter.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ import org.wfanet.virtualpeople.common.fieldfilter.utils.*
2525
/**
2626
* The implementation of [FieldFilter] when op is EQUAL in config.
2727
*
28-
* The supported ValueTypes are: [Int], [Long], [Boolean], [EnumValueDescriptor], [String]
28+
* The supported ValueTypes are: [Int], [UInt], [Long], [ULong], [Boolean], [EnumValueDescriptor],
29+
* [String]
2930
*
3031
* Always use [FieldFilter.create] Users should never construct a [EqualFilter] directly.
3132
*/
@@ -61,10 +62,10 @@ internal class EqualFilter(descriptor: Descriptors.Descriptor, config: FieldFilt
6162
*/
6263
override fun matches(messageOrBuilder: MessageOrBuilder): Boolean {
6364
return when (fieldDescriptors.last().type) {
64-
Type.INT32,
65-
Type.UINT32 -> matchesInternal<Int>(messageOrBuilder)
66-
Type.UINT64,
67-
Type.INT64 -> matchesInternal<Long>(messageOrBuilder)
65+
Type.INT32 -> matchesInternal<Int>(messageOrBuilder)
66+
Type.UINT32 -> matchesInternal<UInt>(messageOrBuilder)
67+
Type.UINT64 -> matchesInternal<Long>(messageOrBuilder)
68+
Type.INT64 -> matchesInternal<ULong>(messageOrBuilder)
6869
Type.BOOL -> matchesInternal<Boolean>(messageOrBuilder)
6970
Type.STRING -> matchesInternal<String>(messageOrBuilder)
7071
Type.ENUM -> matchesInternal<EnumValueDescriptor>(messageOrBuilder)

src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter/GtFilter.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ internal class GtFilter(descriptor: Descriptors.Descriptor, config: FieldFilterP
3333

3434
/**
3535
* Returns error status if any of the following happens:
36-
* 1. [config.op] is not GT.
37-
* 2. [config.name] is not set.
38-
* 3. [config.name] refers to a non-integer field.
39-
* 4. Any field of the path represented by [config.name] is repeated field.
40-
* 5. [config.value] is not set.
36+
* 1. config.op is not GT.
37+
* 2. config.name is not set.
38+
* 3. config.name refers to a non-integer field.
39+
* 4. Any field of the path represented by config.name is repeated field.
40+
* 5. config.value is not set.
4141
*
42-
* [config.value] will be cast to the type of the field represented by [config.name].
42+
* config.value will be cast to the type of the field represented by config.name.
4343
*/
4444
init {
4545
if (config.op != FieldFilterProto.Op.GT) {
@@ -56,8 +56,8 @@ internal class GtFilter(descriptor: Descriptors.Descriptor, config: FieldFilterP
5656
}
5757

5858
/**
59-
* Returns true when the field represented by [config.name] in [messageOrBuilder] is larger than
60-
* [config.value]. Otherwise, returns false. Returns false if the field is not set.
59+
* Returns true when the field represented by config.name in [messageOrBuilder] is larger than
60+
* config.value. Otherwise, returns false. Returns false if the field is not set.
6161
*/
6262
override fun matches(messageOrBuilder: MessageOrBuilder): Boolean {
6363
return comparator.compare(messageOrBuilder) == IntegerCompareResult.GREATER_THAN

src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter/InFilter.kt

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ package org.wfanet.virtualpeople.common.fieldfilter
1616

1717
import com.google.protobuf.Descriptors
1818
import com.google.protobuf.Descriptors.EnumValueDescriptor
19+
import com.google.protobuf.Descriptors.FieldDescriptor.Type
1920
import com.google.protobuf.MessageOrBuilder
2021
import org.wfanet.virtualpeople.common.FieldFilterProto
2122
import org.wfanet.virtualpeople.common.fieldfilter.utils.*
2223

2324
/**
2425
* The implementation of [FieldFilter] when op is IN in config.
2526
*
26-
* The supported ValueTypes are: [Int], [Long], [Boolean], [EnumValueDescriptor], [String]
27+
* The supported ValueTypes are: [Int], [UInt], [Long], [ULong], [Boolean], [EnumValueDescriptor],
28+
* [String]
2729
*
2830
* Always use [FieldFilter.create]. Users should never construct a [InFilter] directly.
2931
*/
@@ -32,12 +34,12 @@ internal abstract class InFilter(val fieldDescriptors: List<Descriptors.FieldDes
3234
companion object {
3335
/**
3436
* Throws an error if any of the following happens:
35-
* 1. [config.op] is not IN.
36-
* 2. [config.name] is not set.
37-
* 3. Any field of the path represented by [config.name] is repeated field.
38-
* 4. [config.value] is not set.
39-
* 5. Any entry in [config.value] (split by comma) cannot be cast to the type of the field
40-
* represented by [config.name].
37+
* 1. [config].op is not IN.
38+
* 2. [config].name is not set.
39+
* 3. Any field of the path represented by [config].name is repeated field.
40+
* 4. [config].value is not set.
41+
* 5. Any entry in [config].value (split by comma) cannot be cast to the type of the field
42+
* represented by [config].name.
4143
*/
4244
internal fun create(descriptor: Descriptors.Descriptor, config: FieldFilterProto): InFilter {
4345

@@ -53,17 +55,13 @@ internal abstract class InFilter(val fieldDescriptors: List<Descriptors.FieldDes
5355
val fieldDescriptors = getFieldFromProto(descriptor, config.name, allowRepeated = false)
5456

5557
return when (fieldDescriptors.last().type) {
56-
Descriptors.FieldDescriptor.Type.INT32,
57-
Descriptors.FieldDescriptor.Type.UINT32 ->
58-
InFilterImpl<Int>(fieldDescriptors, parseValue(config.value))
59-
Descriptors.FieldDescriptor.Type.UINT64,
60-
Descriptors.FieldDescriptor.Type.INT64 ->
61-
InFilterImpl<Long>(fieldDescriptors, parseValue(config.value))
62-
Descriptors.FieldDescriptor.Type.BOOL ->
63-
InFilterImpl<Boolean>(fieldDescriptors, parseValue(config.value))
64-
Descriptors.FieldDescriptor.Type.STRING ->
65-
InFilterImpl<String>(fieldDescriptors, parseValue(config.value))
66-
Descriptors.FieldDescriptor.Type.ENUM ->
58+
Type.INT32 -> InFilterImpl<Int>(fieldDescriptors, parseValue(config.value))
59+
Type.UINT32 -> InFilterImpl<UInt>(fieldDescriptors, parseValue(config.value))
60+
Type.UINT64 -> InFilterImpl<ULong>(fieldDescriptors, parseValue(config.value))
61+
Type.INT64 -> InFilterImpl<Long>(fieldDescriptors, parseValue(config.value))
62+
Type.BOOL -> InFilterImpl<Boolean>(fieldDescriptors, parseValue(config.value))
63+
Type.STRING -> InFilterImpl<String>(fieldDescriptors, parseValue(config.value))
64+
Type.ENUM ->
6765
InFilterImpl(
6866
fieldDescriptors,
6967
parseEnumValues(fieldDescriptors.last().enumType, config.value)
@@ -90,18 +88,13 @@ internal class InFilterImpl<T>(
9088
override fun matches(messageOrBuilder: MessageOrBuilder): Boolean {
9189
val protoFieldValue =
9290
when (fieldDescriptors.last().type) {
93-
Descriptors.FieldDescriptor.Type.INT32,
94-
Descriptors.FieldDescriptor.Type.UINT32 ->
95-
getValueFromProto<Int>(messageOrBuilder, fieldDescriptors)
96-
Descriptors.FieldDescriptor.Type.UINT64,
97-
Descriptors.FieldDescriptor.Type.INT64 ->
98-
getValueFromProto<Long>(messageOrBuilder, fieldDescriptors)
99-
Descriptors.FieldDescriptor.Type.BOOL ->
100-
getValueFromProto<Boolean>(messageOrBuilder, fieldDescriptors)
101-
Descriptors.FieldDescriptor.Type.STRING ->
102-
getValueFromProto<String>(messageOrBuilder, fieldDescriptors)
103-
Descriptors.FieldDescriptor.Type.ENUM ->
104-
getValueFromProto<EnumValueDescriptor>(messageOrBuilder, fieldDescriptors)
91+
Type.INT32 -> getValueFromProto<Int>(messageOrBuilder, fieldDescriptors)
92+
Type.UINT32 -> getValueFromProto<UInt>(messageOrBuilder, fieldDescriptors)
93+
Type.UINT64 -> getValueFromProto<ULong>(messageOrBuilder, fieldDescriptors)
94+
Type.INT64 -> getValueFromProto<Long>(messageOrBuilder, fieldDescriptors)
95+
Type.BOOL -> getValueFromProto<Boolean>(messageOrBuilder, fieldDescriptors)
96+
Type.STRING -> getValueFromProto<String>(messageOrBuilder, fieldDescriptors)
97+
Type.ENUM -> getValueFromProto<EnumValueDescriptor>(messageOrBuilder, fieldDescriptors)
10598
else -> error("Unsupported field type for IN filter. ${fieldDescriptors.last().type}")
10699
}
107100
@Suppress("UNCHECKED_CAST")

src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter/LtFilter.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ internal class LtFilter(descriptor: Descriptors.Descriptor, config: FieldFilterP
3232
private val comparator: IntegerComparator
3333

3434
/**
35-
* Returns error status if any of the following happens:
36-
* 1. [config.op] is not LT.
37-
* 2. [config.name] is not set.
38-
* 3. [config.name] refers to a non-integer field.
39-
* 4. Any field of the path represented by [config.name] is repeated field.
40-
* 5. [config.value] is not set.
35+
* Throws an error if any of the following happens:
36+
* 1. config.op is not LT.
37+
* 2. config.name is not set.
38+
* 3. config.name refers to a non-integer field.
39+
* 4. Any field of the path represented by config.name is repeated field.
40+
* 5. config.value is not set.
4141
*
42-
* [config.value] will be cast to the type of the field represented by [config.name].
42+
* config.value will be cast to the type of the field represented by config.name.
4343
*/
4444
init {
4545
if (config.op != FieldFilterProto.Op.LT) {
@@ -56,8 +56,8 @@ internal class LtFilter(descriptor: Descriptors.Descriptor, config: FieldFilterP
5656
}
5757

5858
/**
59-
* Returns true when the field represented by [config.name] in [messageOrBuilder] is less than
60-
* [config.value]. Otherwise, returns false. Returns false if the field is not set.
59+
* Returns true when the field represented by config.name in [messageOrBuilder] is less than
60+
* config.value. Otherwise, returns false. Returns false if the field is not set.
6161
*/
6262
override fun matches(messageOrBuilder: MessageOrBuilder): Boolean {
6363
return comparator.compare(messageOrBuilder) == IntegerCompareResult.LESS_THAN

src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter/utils/FieldUtil.kt

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,12 @@ inline fun <reified V> getImmediateValueFromProtoOrDefault(
142142
fieldDescriptor: FieldDescriptor
143143
): V {
144144
val result = message.getField(fieldDescriptor)
145-
if (result is V) {
146-
return result
145+
return if (result is V) {
146+
result
147+
} else if (result is Int && V::class == UInt::class) {
148+
result.toUInt() as V
149+
} else if (result is Long && V::class == ULong::class) {
150+
result.toULong() as V
147151
} else {
148152
error("The type of the field is not compatible to the target")
149153
}
@@ -195,10 +199,17 @@ inline fun <reified V> setImmediateValueToProtoBuilder(
195199
fieldDescriptor: FieldDescriptor,
196200
value: V
197201
) {
198-
if (builder.getField(fieldDescriptor) is V) {
202+
val fieldObject = builder.getField(fieldDescriptor)
203+
if (fieldObject is V) {
199204
builder.setField(fieldDescriptor, value)
205+
} else if (fieldObject is Int && V::class == UInt::class) {
206+
builder.setField(fieldDescriptor, (value as UInt).toInt())
207+
} else if (fieldObject is Long && V::class == ULong::class) {
208+
builder.setField(fieldDescriptor, (value as ULong).toLong())
200209
} else {
201-
error("The type of the field is not compatible to the target")
210+
error(
211+
"The type of the field is not compatible to the target. ${fieldObject.javaClass} vs ${V::class}"
212+
)
202213
}
203214
}
204215

@@ -267,8 +278,12 @@ inline fun <reified V> getImmediateValueFromRepeatedProto(
267278
index: Int
268279
): V {
269280
val result = message.getRepeatedField(fieldDescriptor, index)
270-
if (result is V) {
271-
return result
281+
return if (result is V) {
282+
result
283+
} else if (result is Int && V::class == UInt::class) {
284+
result.toUInt() as V
285+
} else if (result is Long && V::class == ULong::class) {
286+
result.toULong() as V
272287
} else {
273288
error("The type of the field is not compatible to the target: $result")
274289
}
@@ -312,6 +327,10 @@ inline fun <reified V> getAllImmediateValuesFromRepeatedProto(
312327
val value = message.getRepeatedField(fieldDescriptor, it)
313328
if (value is V) {
314329
value
330+
} else if (value is Int && V::class == UInt::class) {
331+
value.toUInt() as V
332+
} else if (value is Long && V::class == ULong::class) {
333+
value.toULong() as V
315334
} else {
316335
error("The type of the field is not compatible to the target: $value")
317336
}

src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter/utils/IntegerComparatorUtil.kt

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ enum class IntegerCompareResult {
2929
* A helper class to do integer comparison between a field in a protobuf message and a given integer
3030
* represented by a string.
3131
*
32-
* The supported [ValueType]s integer types are: [Int] [Long]
32+
* The supported integer types are: [Int], [UInt], [Long], [ULong]
3333
*
3434
* The path represented by [fieldDescriptors] must be a valid path to an integer field in
3535
* [messageOrBuilder].
@@ -93,10 +93,10 @@ abstract class IntegerComparator(val fieldDescriptors: List<FieldDescriptor>) {
9393
*/
9494
fun new(fieldDescriptors: List<FieldDescriptor>, value: String): IntegerComparator {
9595
return when (fieldDescriptors.last().type) {
96-
Type.INT32,
97-
Type.UINT32 -> IntegerComparatorImpl<Int>(fieldDescriptors, convertToNumeric(value))
98-
Type.INT64,
99-
Type.UINT64 -> IntegerComparatorImpl<Long>(fieldDescriptors, convertToNumeric(value))
96+
Type.INT32 -> IntegerComparatorImpl<Int>(fieldDescriptors, convertToNumeric(value))
97+
Type.UINT32 -> IntegerComparatorImpl<UInt>(fieldDescriptors, convertToNumeric(value))
98+
Type.INT64 -> IntegerComparatorImpl<Long>(fieldDescriptors, convertToNumeric(value))
99+
Type.UINT64 -> IntegerComparatorImpl<ULong>(fieldDescriptors, convertToNumeric(value))
100100
else -> {
101101
error("The given field is not integer when building IntegerComparator.")
102102
}
@@ -112,21 +112,23 @@ private class IntegerComparatorImpl<T>(fieldDescriptors: List<FieldDescriptor>,
112112
override fun compare(messageOrBuilder: MessageOrBuilder): IntegerCompareResult {
113113
val fieldValue =
114114
when (fieldDescriptors.last().type) {
115-
Type.INT32,
116-
Type.UINT32 -> getValueFromProto<Int>(messageOrBuilder, fieldDescriptors)
117-
Type.INT64,
118-
Type.UINT64 -> getValueFromProto<Long>(messageOrBuilder, fieldDescriptors)
115+
Type.INT32 -> getValueFromProto<Int>(messageOrBuilder, fieldDescriptors)
116+
Type.UINT32 -> getValueFromProto<UInt>(messageOrBuilder, fieldDescriptors)
117+
Type.INT64 -> getValueFromProto<Long>(messageOrBuilder, fieldDescriptors)
118+
Type.UINT64 -> getValueFromProto<ULong>(messageOrBuilder, fieldDescriptors)
119119
else -> error("The given field is not integer.")
120120
}
121121
if (!fieldValue.isSet) {
122122
return IntegerCompareResult.INVALID
123123
}
124-
if (fieldValue.value as Comparable<T> > value) {
125-
return IntegerCompareResult.GREATER_THAN
126-
}
127-
if (fieldValue.value as Comparable<T> < value) {
128-
return IntegerCompareResult.LESS_THAN
124+
125+
val inputValue = fieldValue.value as Comparable<T>
126+
return if (inputValue > value) {
127+
IntegerCompareResult.GREATER_THAN
128+
} else if (inputValue < value) {
129+
IntegerCompareResult.LESS_THAN
130+
} else {
131+
IntegerCompareResult.EQUAL
129132
}
130-
return IntegerCompareResult.EQUAL
131133
}
132134
}

src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter/utils/TypeConvertUtil.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor
2020
/**
2121
* Convert the string [input] to a numeric type. The allowed output types are
2222
*
23-
* [Int], [Long], [Float], [Double], [Boolean]
23+
* [Int], [UInt], [Long], [ULong], [Float], [Double], [Boolean]
2424
*
2525
* For boolean, only "true' and "false" (capitalization doesn't matter) string are supported
2626
*/
2727
inline fun <reified V> convertToNumeric(input: String): V {
2828
return when (V::class) {
2929
Int::class -> input.toInt() as V
30+
UInt::class -> input.toUInt() as V
3031
Long::class -> input.toLong() as V
32+
ULong::class -> input.toULong() as V
3133
Float::class -> input.toFloat() as V
3234
Double::class -> input.toDouble() as V
3335
Boolean::class -> input.lowercase().toBooleanStrict() as V

src/main/kotlin/org/wfanet/virtualpeople/common/fieldfilter/utils/ValuesParser.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import com.google.protobuf.Descriptors.EnumDescriptor
2020
* A helper function to parse and store [valuesString] as a set of V. [valuesString] is a string
2121
* represents a list of [V] entities separated by comma.
2222
*
23-
* The supported [V]s are: [Int] [Long] [Boolean] [String]
23+
* The supported [V]s are: [Int], [UInt], [Long], [ULong], [Boolean], [String].
2424
*/
2525
inline fun <reified V> parseValue(valuesString: String): Set<V> {
2626
return valuesString
@@ -29,9 +29,12 @@ inline fun <reified V> parseValue(valuesString: String): Set<V> {
2929
when (V::class) {
3030
String::class -> value as V
3131
Int::class,
32+
UInt::class,
3233
Long::class,
33-
Boolean::class -> convertToNumeric<V>(value)
34-
else -> error("Only Int, Long, Boolean and String are supported, get ${V::class}")
34+
ULong::class,
35+
Boolean::class -> convertToNumeric(value)
36+
else ->
37+
error("Only Int, UInt, Long, ULong, Boolean and String are supported, get ${V::class}")
3538
}
3639
}
3740
.toSet()

0 commit comments

Comments
 (0)