Skip to content

Commit f66c7ea

Browse files
authored
Fix and test permissive compare in proto3json (flutter#329)
1 parent 95fec2f commit f66c7ea

File tree

5 files changed

+91
-40
lines changed

5 files changed

+91
-40
lines changed

protobuf/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
## 1.0.2
22

33
* Fix hashcode of bytes fields.
4+
* Fix issue with the `permissiveEnums` option to `mergeFromProto3Json`.
5+
The comparison did not work properly.
46

57
## 1.0.1
68

protobuf/lib/protobuf.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'dart:typed_data' show TypedData, Uint8List, ByteData, Endian;
1414
import 'package:fixnum/fixnum.dart' show Int64;
1515

1616
import 'src/protobuf/json_parsing_context.dart';
17+
import 'src/protobuf/permissive_compare.dart';
1718
import 'src/protobuf/type_registry.dart';
1819
export 'src/protobuf/type_registry.dart' show TypeRegistry;
1920

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// Returns true if [a] and [b] are the same ignoring case and all instances of
6+
/// `-` and `_`.
7+
///
8+
/// This is specialized code for comparing enum names.
9+
/// Works only for ascii strings containing letters and `_` and `-`.
10+
bool permissiveCompare(String a, String b) {
11+
const dash = 45;
12+
const underscore = 95;
13+
14+
int i = 0;
15+
int j = 0;
16+
17+
while (true) {
18+
int ca, cb;
19+
do {
20+
ca = i < a.length ? a.codeUnitAt(i++) : -1;
21+
} while (ca == dash || ca == underscore);
22+
do {
23+
cb = j < b.length ? b.codeUnitAt(j++) : -1;
24+
} while (cb == dash || cb == underscore);
25+
if (ca == cb) {
26+
if (ca == -1) return true; // Both at end
27+
continue;
28+
}
29+
if (ca ^ cb != 0x20 || !_isAsciiLetter(ca)) {
30+
return false;
31+
}
32+
}
33+
}
34+
35+
bool _isAsciiLetter(int char) {
36+
const lowerA = 97;
37+
const lowerZ = 122;
38+
const capitalA = 65;
39+
char |= lowerA ^ capitalA;
40+
return lowerA <= char && char <= lowerZ;
41+
}

protobuf/lib/src/protobuf/proto3_json.dart

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ void _mergeFromProto3Json(
198198
// TODO(sigurdm): Do we want to avoid linear search here? Measure...
199199
final result = permissiveEnums
200200
? fieldInfo.enumValues.firstWhere(
201-
(e) => _permissiveCompare(e.name, value),
201+
(e) => permissiveCompare(e.name, value),
202202
orElse: () => null)
203203
: fieldInfo.enumValues
204204
.firstWhere((e) => e.name == value, orElse: () => null);
@@ -405,42 +405,3 @@ void _mergeFromProto3Json(
405405

406406
recursionHelper(json, fieldSet);
407407
}
408-
409-
bool _isAsciiLetter(int char) {
410-
const lowerA = 97;
411-
const lowerZ = 122;
412-
const capitalA = 65;
413-
char |= lowerA ^ capitalA;
414-
return lowerA <= char && char <= lowerZ;
415-
}
416-
417-
/// Returns true if [a] and [b] are the same ignoring case and all instances of
418-
/// `-` and `_`.
419-
bool _permissiveCompare(String a, String b) {
420-
const dash = 45;
421-
const underscore = 95;
422-
423-
// Enum names are always ascii.
424-
int i = 0;
425-
int j = 0;
426-
427-
outer:
428-
while (i < a.length && j < b.length) {
429-
int ca = a.codeUnitAt(i);
430-
if (ca == dash || ca == underscore) {
431-
i++;
432-
continue;
433-
}
434-
int cb = b.codeUnitAt(j);
435-
while (cb == dash || cb == underscore) {
436-
j++;
437-
if (j == b.length) break outer;
438-
cb = b.codeUnitAt(j);
439-
}
440-
441-
if (ca != cb && (ca ^ cb != 0x20 || !_isAsciiLetter(ca))) return false;
442-
i++;
443-
j++;
444-
}
445-
return true;
446-
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:test/test.dart';
6+
import 'package:protobuf/src/protobuf/permissive_compare.dart';
7+
8+
void main() {
9+
void symmetric(String a, String b, bool expected) {
10+
expect(permissiveCompare(a, b), expected);
11+
expect(permissiveCompare(b, a), expected);
12+
}
13+
14+
List<String> variationsFromSeed(String seed) {
15+
final result = [
16+
seed,
17+
seed.toUpperCase(),
18+
'-$seed',
19+
'-${seed.toUpperCase()}',
20+
'_$seed',
21+
'_${seed.toUpperCase()}',
22+
'$seed-',
23+
'${seed}_',
24+
];
25+
if (2 <= seed.length) {
26+
result.add('${seed.substring(0, 1)}_${seed.substring(1)}');
27+
result.add('${seed.substring(0, 1)}-${seed.substring(1)}');
28+
result.add('${seed.substring(0, 1).toUpperCase()}${seed.substring(1)}');
29+
result.add('${seed.substring(0, 1)}${seed.substring(1).toUpperCase()}');
30+
}
31+
return result;
32+
}
33+
34+
test('permissive compare', () {
35+
final seeds = ['', 'a', 'b', 'aa', 'ab', 'bb', 'aaaa'];
36+
for (final a in seeds) {
37+
for (final aVariant in variationsFromSeed(a)) {
38+
for (final b in seeds) {
39+
for (final bVariant in variationsFromSeed(b)) {
40+
symmetric(aVariant, bVariant, a == b);
41+
}
42+
}
43+
}
44+
}
45+
});
46+
}

0 commit comments

Comments
 (0)