| // 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); |
| } |
| } |
| } |