diff --git a/Bencodex/Types/Dictionary.cs b/Bencodex/Types/Dictionary.cs index 412c15f..02f8aa2 100644 --- a/Bencodex/Types/Dictionary.cs +++ b/Bencodex/Types/Dictionary.cs @@ -38,20 +38,20 @@ public Dictionary(IEnumerable> value) public static Dictionary Empty => default; - public int Count => Value?.Count ?? 0; + public int Count => Value.Count; public IEnumerable Keys => - Value?.Keys ?? Enumerable.Empty(); + Value.Keys; public IEnumerable Values => - Value?.Values ?? Enumerable.Empty(); + Value.Values; [Pure] public string Inspection { get { - if (Value is null || !this.Any()) + if (_value is null || !Value.Any()) { return "{}"; } @@ -297,12 +297,16 @@ IImmutableDictionary other [Pure] public IEnumerable EncodeIntoChunks() { + // FIXME: avoid duplication between this and EncodeToStream() yield return _dictionaryPrefix; - if (!(Value is null)) + if (!(_value is null)) { + var @enum = _value.Dict + ?? _value.Pairs + ?? Enumerable.Empty>(); IEnumerable> rawPairs = - from pair in this + from pair in @enum select ( pair.Key.KeyPrefix, pair.Key.EncodeAsByteArray(), @@ -312,8 +316,19 @@ from pair in this triple => (triple.Item1, triple.Item2), keyPairComparer ); + var byteArrayComparer = default(ByteArrayComparer); + (byte? keyPrefix, byte[] key)? prev = null; foreach ((byte? keyPrefix, byte[] key, IValue value) in orderedPairs) { + // Skip duplicates + if (_value.Dict is null && prev is { } p) + { + if (p.keyPrefix == keyPrefix && byteArrayComparer.Compare(p.key, key) == 0) + { + continue; + } + } + if (keyPrefix != null) { yield return _unicodeKeyPrefix; @@ -328,6 +343,8 @@ from pair in this { yield return chunk; } + + prev = (keyPrefix, key); } } @@ -336,12 +353,16 @@ from pair in this public void EncodeToStream(Stream stream) { + // FIXME: avoid duplication between this and EncodeIntoChunks() stream.WriteByte(_dictionaryPrefix[0]); if (!(_value is null)) { + var @enum = _value.Dict + ?? _value.Pairs + ?? Enumerable.Empty>(); IEnumerable> rawPairs = - from pair in this + from pair in @enum select ( pair.Key.KeyPrefix, pair.Key.EncodeAsByteArray(), @@ -351,8 +372,19 @@ from pair in this triple => (triple.Item1, triple.Item2), keyPairComparer ); + var byteArrayComparer = default(ByteArrayComparer); + (byte? keyPrefix, byte[] key)? prev = null; foreach ((byte? keyPrefix, byte[] key, IValue value) in orderedPairs) { + // Skip duplicates + if (_value.Dict is null && prev is { } p) + { + if (p.keyPrefix == keyPrefix && byteArrayComparer.Compare(p.key, key) == 0) + { + continue; + } + } + if (keyPrefix != null) { stream.WriteByte(_unicodeKeyPrefix[0]); @@ -365,6 +397,7 @@ from pair in this stream.WriteByte(CommonVariables.Separator[0]); stream.Write(key, 0, key.Length); value.EncodeToStream(stream); + prev = (keyPrefix, key); } } diff --git a/CHANGES.md b/CHANGES.md index 84afb85..c3c1a0a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -105,7 +105,7 @@ To be released. - `Bencodex.Types.Dictionary` became not to immediately realize the inner hash table, but do it when it needs (e.g., when to look up a key) instead. Note that this change does not cause any API changes, but just purposes - faster instantiation. [[#33]] + faster instantiation. [[#33], [#34]] [#7]: https://github.com/planetarium/bencodex.net/pull/7 [#11]: https://github.com/planetarium/bencodex.net/pull/11 @@ -120,6 +120,7 @@ To be released. [#28]: https://github.com/planetarium/bencodex.net/pull/28 [#32]: https://github.com/planetarium/bencodex.net/pull/32 [#33]: https://github.com/planetarium/bencodex.net/pull/33 +[#34]: https://github.com/planetarium/bencodex.net/pull/34 [nullable reference types]: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references [RTL]: https://en.wikipedia.org/wiki/Right-to-left [FNV]: https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function