blob: 5f5b192e7f3d35fc8783737a1890d5af5fefe6ac [file] [log] [blame]
// Copyright (c) 2022, 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 '../serialization/serialization.dart';
/// A canonicalized record shape comprising zero or more positional fields
/// followed by zero or more named fields in sorted order.
class RecordShape {
/// Tag used for identifying serialized [RecordShape] objects in a debugging
/// data stream.
static const String tag = 'record-shape';
/// Number of positional fields.
final int positionalFieldCount;
/// Names of named fields in canonical order.
final List<String> fieldNames;
int get namedFieldCount => fieldNames.length;
int get fieldCount => positionalFieldCount + namedFieldCount;
RecordShape._(this.positionalFieldCount, [this.fieldNames = const []]);
factory RecordShape(int positionalFieldCount, List<String> names) {
assert(positionalFieldCount >= 0);
if (names.isEmpty) {
if (0 <= positionalFieldCount && positionalFieldCount < _common.length) {
return _common[positionalFieldCount];
}
return RecordShape._(positionalFieldCount, const []);
}
assert(_isSorted(names));
return RecordShape._(positionalFieldCount, names);
}
static bool _isSorted(List<String> names) {
for (int i = 1; i < names.length; i++) {
if (names[i - 1].compareTo(names[i]) >= 0) return false;
}
return true;
}
static final List<RecordShape> _common = [
RecordShape._(0),
RecordShape._(1),
RecordShape._(2),
RecordShape._(3),
RecordShape._(4),
RecordShape._(5),
];
/// Deserializes a [RecordShape] object from [source].
factory RecordShape.readFromDataSource(DataSourceReader source) {
source.begin(tag);
int positionals = source.readInt();
List<String> names = source.readStrings()!;
source.end(tag);
return RecordShape(positionals, names);
}
/// Serializes this [RecordShape] to [sink].
void writeToDataSink(DataSinkWriter sink) {
sink.begin(tag);
sink.writeInt(positionalFieldCount);
sink.writeStrings(fieldNames);
sink.end(tag);
}
@override
bool operator ==(Object other) =>
identical(this, other) || other is RecordShape && _sameShape(this, other);
@override
late final int hashCode = _hashCode();
int _hashCode() {
return Object.hash(positionalFieldCount, Object.hashAll(fieldNames));
}
static bool _sameShape(RecordShape a, RecordShape b) {
if (a.positionalFieldCount != b.positionalFieldCount) return false;
if (a.fieldNames.length != b.fieldNames.length) return false;
for (int i = 0; i < a.fieldNames.length; i++) {
if (a.fieldNames[i] != b.fieldNames[i]) return false;
}
return true;
}
@override
String toString() {
if (fieldNames.isEmpty) {
return 'RecordShape($positionalFieldCount)';
}
return 'RecordShape($positionalFieldCount, {${fieldNames.join(", ")}})';
}
}