blob: 6247e86f89176adb8c672e324927d86baa3899d9 [file] [log] [blame] [edit]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:fixnum/fixnum.dart' show Int64;
import 'internal.dart';
import 'json_parsing_context.dart';
/// Type of a function that checks items added to `PbList` and `PbMap`.
///
/// Throws [ArgumentError] or [RangeError] when the item is not valid.
typedef CheckFunc<E> = void Function(E? x);
// TODO(antonm): reconsider later if PbList should take care of equality.
bool deepEquals(Object? lhs, Object? rhs) {
// Some GeneratedMessages implement Map, so test this first.
if (lhs is GeneratedMessage) return lhs == rhs;
if (rhs is GeneratedMessage) return false;
if ((lhs is List) && (rhs is List)) return areListsEqual(lhs, rhs);
if ((lhs is Map) && (rhs is Map)) return areMapsEqual(lhs, rhs);
return lhs == rhs;
}
bool areListsEqual(List<Object?> lhs, List<Object?> rhs) {
if (lhs.length != rhs.length) return false;
for (var i = 0; i < lhs.length; i++) {
if (!deepEquals(lhs[i], rhs[i])) return false;
}
return true;
}
bool areMapsEqual(Map<Object?, Object?> lhs, Map<Object?, Object?> rhs) {
if (lhs.length != rhs.length) return false;
return lhs.keys.every((key) => deepEquals(lhs[key], rhs[key]));
}
List<T> sorted<T>(Iterable<T> list) => List.from(list)..sort();
/// Escapes slash, double quotes, and newlines in [s] with \ as needed
/// for a TextFormat string.
///
/// This is a copy of the official Java implementation: https://github.com/protocolbuffers/protobuf/blob/main/java/core/src/main/java/com/google/protobuf/TextFormat.java#L632
String escapeString(String s) {
return s
.replaceAll('\\', '\\\\')
.replaceAll('"', '\\"')
.replaceAll('\n', '\\n');
}
/// Appends the characters of [bytes] to [out] while escaping them as needed
/// for a TextFormat string.
///
/// See TextFormat spec in https://protobuf.dev/reference/protobuf/textformat-spec/
/// This is a copy of the official Java implementation: https://github.com/protocolbuffers/protobuf/blob/main/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java#L40
void escapeBytes(List<int> bytes, StringSink out) {
for (final byte in bytes) {
// Only ASCII characters between 0x20 (space) and 0x7e (tilde) are
// printable. Other byte values must be escaped.
switch (byte) {
case 0x07:
out.write(r'\a');
case 0x08:
out.write(r'\b');
case 0x0c:
out.write(r'\f');
case 0x0a:
out.write(r'\n');
case 0x0d:
out.write(r'\r');
case 0x09:
out.write(r'\t');
case 0x0b:
out.write(r'\v');
default:
if (byte >= 0x20 && byte < 0x7f) {
if (byte == 0x22 /* " */ || byte == 0x5c /* \ */ ) {
out.write(r'\');
}
out.writeCharCode(byte);
} else {
out.write(r'\');
out.write(((byte >> 6) & 3).toString());
out.write(((byte >> 3) & 7).toString());
out.write((byte & 7).toString());
}
}
}
}
class HashUtils {
// Jenkins hash functions copied from
// https://github.com/google/quiver-dart/blob/master/lib/src/core/hash.dart.
static int combine(int hash, int value) {
hash = 0x1fffffff & (hash + value);
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int _finish(int hash) {
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
/// Generates a hash code for multiple [objects].
static int hashObjects(Iterable objects) =>
_finish(objects.fold(0, (h, i) => combine(h, i.hashCode)));
/// Generates a hash code for two objects.
static int hash2(dynamic a, dynamic b) =>
_finish(combine(combine(0, a.hashCode), b.hashCode));
}
class Proto3ParseUtils {
static int tryParse32Bit(String s, JsonParsingContext context) {
return int.tryParse(s) ??
(throw context.parseException('expected integer', s));
}
static int check32BitSigned(int n, JsonParsingContext context) {
if (n < -2147483648 || n > 2147483647) {
throw context.parseException('expected 32 bit signed integer', n);
}
return n;
}
static int check32BitUnsigned(int n, JsonParsingContext context) {
if (n < 0 || n > 0xFFFFFFFF) {
throw context.parseException('expected 32 bit unsigned integer', n);
}
return n;
}
static Int64 tryParse64Bit(
Object? json,
String s,
JsonParsingContext context,
) {
try {
return Int64.parseInt(s);
} on FormatException {
throw context.parseException('expected integer', json);
}
}
}