-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comparing #17
Comments
There should probably be a way to easily check if two compounds have the same contents. I will add overrides of /// <summary> Compares NbtTag for equality by comparing their types, names, and values. Considers compound tags to be equal
/// if they contain equal sets of tags. Considered list tags to be equal if their tags are equal and in the same order. </summary>
public class NbtComparer : IEqualityComparer<NbtTag> {
public static readonly NbtComparer Instance = new NbtComparer();
public bool Equals(NbtTag x, NbtTag y) {
if (x == y) return true;
if (x == null || y == null) return false;
return (x.TagType == y.TagType)
&& string.Equals(x.Name, y.Name) // null names are permitted
&& DeepEquals(x, y);
}
public int GetHashCode(NbtTag tag) {
var hash = tag.TagType.GetHashCode();
hash = hash*23 ^ (tag.Name ?? "").GetHashCode();
var rawValue = GetRawValue(tag);
if (rawValue != null) {
hash = hash*23 ^ rawValue.GetHashCode();
} else if (tag.TagType == NbtTagType.List) {
var list = (NbtList)tag;
hash = hash*23 ^ list.ListType.GetHashCode();
hash = hash*23 ^ list.Count.GetHashCode();
} else if (tag.TagType == NbtTagType.Compound) {
var comp = (NbtCompound)tag;
hash = hash*23 ^ comp.Count.GetHashCode();
} else {
// END and unknown
throw new ArgumentException("Cannot compare tags of type " + tag.TagType);
}
return hash;
}
// Compare detailed attributes of two given tags
private bool DeepEquals(NbtTag x, NbtTag y) {
// Assume that tags have same type and are non-null
var rawValue1 = GetRawValue(x);
if (rawValue1 != null) {
var rawValue2 = GetRawValue(y);
// Regular tags are equal if their values are equal
return Equals(rawValue1, rawValue2);
} else if (x.TagType == NbtTagType.Compound) {
var xComp = (NbtCompound)x;
var yComp = (NbtCompound)y;
// Compounds are equal if their child-count and contents are equal
return (xComp.Count == yComp.Count) &&
new HashSet<NbtTag>(xComp, this).SetEquals(yComp);
} else if (x.TagType == NbtTagType.List) {
var xList = (NbtList)x;
var yList = (NbtList)y;
// Lists are considered equal if their type, count, and contents are equal
return (xList.ListType == yList.ListType) &&
(xList.Count == yList.Count) &&
xList.SequenceEqual(yList, this);
} else {
// END and unknown
throw new ArgumentException("Cannot compare tags of type " + x.TagType);
}
}
private object GetRawValue(NbtTag tag) {
switch (tag.TagType) {
case NbtTagType.Byte:
return tag.ByteValue;
case NbtTagType.ByteArray:
return tag.ByteArrayValue;
case NbtTagType.Double:
return tag.DoubleValue;
case NbtTagType.Float:
return tag.FloatValue;
case NbtTagType.Int:
return tag.IntValue;
case NbtTagType.IntArray:
return tag.IntArrayValue;
case NbtTagType.Long:
return tag.LongValue;
case NbtTagType.Short:
return tag.ShortValue;
case NbtTagType.String:
return tag.StringValue;
default:
// returns null for List, Compound, and unknown tag types
return null;
}
}
} Here is how you'd use it, for example: private static void Main(string[] args) {
var comp1 = new NbtCompound("Foo") {
new NbtInt("IntField", 1),
new NbtString("StringVal", "blah"),
new NbtIntArray("ByteArrayVal", new[] { 1, 2, 3 }),
new NbtList("ListVal")
};
var comp2 = new NbtCompound("Bar") {
new NbtInt("IntField", 2),
new NbtString("StringVal", "blah"),
new NbtIntArray("ByteArrayVal", new[] { 1, 2, 3 }),
new NbtDouble("DoubleVal", 1.23)
};
PrintDifference(comp1, comp2, NbtComparer.Instance);
Console.ReadLine();
}
private static void PrintDifference(NbtCompound x, NbtCompound y, IEqualityComparer<NbtTag> comparer) {
if (comparer.Equals(x, y)) {
Console.WriteLine("They're equal!");
} else {
Console.WriteLine("They're NOT equal!");
if (!string.Equals(x.Name, y.Name)) {
Console.WriteLine("Names are different: {0} vs {1}", x.Name, y.Name);
}
// Compare named tags that are present in both compounds
foreach (var sharedName in x.Names.Intersect(y.Names)) {
if (comparer.Equals(x[sharedName], y[sharedName])) {
Console.WriteLine(" \"{0}\" is same ({1})", sharedName, x[sharedName]);
} else {
Console.WriteLine(" \"{0}\" is different ({1} vs {2})", sharedName, x[sharedName],
y[sharedName]);
}
}
// Print named tags that are unique to one of the compounds
foreach (var newName in x.Names.Except(y.Names)) {
Console.WriteLine(" \"{0}\" is only in {1} ({2})", newName, x.Name, x[newName]);
}
foreach (var newName in y.Names.Except(x.Names)) {
Console.WriteLine(" \"{0}\" is only in {1} ({2})", newName, y.Name, y[newName]);
}
}
} |
Now that I think about it, tags with numeric values (byte, int, long, float, double) can also implement IComparable. Perhaps string tags too. |
@Fragmer, when is next release? |
Well, I can do a minor release (0.6.4) with just these additions in a couple days. Timeline of the next major release (either 0.7 or 1.0) is uncertain. The big new feature (automated serialization/deserialization of objects) has been stalled for a while. It needs well over 100 hours of additional work, and I don't have much spare time these days. |
Can you add method to compare two compounds?
The text was updated successfully, but these errors were encountered: