/// <exception cref="ArgumentNullException"><paramref name="prefix"/> is null.</exception> /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</exception> public void LinkPrefix(BlobBuilder prefix) { if (prefix == null) { Throw.ArgumentNull(nameof(prefix)); } // TODO: consider copying data from right to left while there is space if (!prefix.IsHead || !IsHead) { Throw.InvalidOperationBuilderAlreadyLinked(); } // avoid chaining empty chunks: if (prefix.Count == 0) { return; } PreviousLength += prefix.Count; // prefix is not a head anymore: prefix._length = prefix.FrozenLength; // First and last chunks: // // [PrefixFirst]->[]->[PrefixLast] <- [prefix] [First]->[]->[Last] <- [this] // ^_________________| ^___________| // // Degenerate cases: // this == First == Last and/or prefix == PrefixFirst == PrefixLast. var first = FirstChunk; var prefixFirst = prefix.FirstChunk; var last = _nextOrPrevious; var prefixLast = prefix._nextOrPrevious; // Relink like so: // [PrefixFirst]->[]->[PrefixLast] -> [prefix] -> [First]->[]->[Last] <- [this] // ^________________________________________________________| _nextOrPrevious = (last != this) ? last : prefix; prefix._nextOrPrevious = (first != this) ? first : (prefixFirst != prefix) ? prefixFirst : prefix; if (last != this) { last._nextOrPrevious = (prefixFirst != prefix) ? prefixFirst : prefix; } if (prefixLast != prefix) { prefixLast._nextOrPrevious = prefix; } prefix.CheckInvariants(); CheckInvariants(); }
/// <exception cref="ArgumentNullException"><paramref name="suffix"/> is null.</exception> /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</exception> public void LinkSuffix(BlobBuilder suffix) { if (suffix == null) { throw new ArgumentNullException(nameof(suffix)); } // TODO: consider copying data from right to left while there is space if (!IsHead || !suffix.IsHead) { Throw.InvalidOperationBuilderAlreadyLinked(); } // avoid chaining empty chunks: if (suffix.Count == 0) { return; } bool isEmpty = Count == 0; // swap buffers of the heads: var suffixBuffer = suffix._buffer; uint suffixLength = suffix._length; int suffixPreviousLength = suffix.PreviousLength; int oldSuffixLength = suffix.Length; suffix._buffer = _buffer; suffix._length = FrozenLength; // suffix is not a head anymore _buffer = suffixBuffer; _length = suffixLength; PreviousLength += suffix.Length + suffixPreviousLength; // Update the _previousLength of the suffix so that suffix.Count = suffix._previousLength + suffix.Length doesn't change. // Note that the resulting previous length might be negative. // The value is not used, other than for calculating the value of Count property. suffix._previousLengthOrFrozenSuffixLengthDelta = suffixPreviousLength + oldSuffixLength - suffix.Length; if (!isEmpty) { // First and last chunks: // // [First]->[]->[Last] <- [this] [SuffixFirst]->[]->[SuffixLast] <- [suffix] // ^___________| ^_________________| // // Degenerate cases: // this == First == Last and/or suffix == SuffixFirst == SuffixLast. var first = FirstChunk; var suffixFirst = suffix.FirstChunk; var last = _nextOrPrevious; var suffixLast = suffix._nextOrPrevious; // Relink like so: // [First]->[]->[Last] -> [suffix] -> [SuffixFirst]->[]->[SuffixLast] <- [this] // ^_______________________________________________________| _nextOrPrevious = suffixLast; suffix._nextOrPrevious = (suffixFirst != suffix) ? suffixFirst : (first != this) ? first : suffix; if (last != this) { last._nextOrPrevious = suffix; } if (suffixLast != suffix) { suffixLast._nextOrPrevious = (first != this) ? first : suffix; } } CheckInvariants(); suffix.CheckInvariants(); }
/// <exception cref="ArgumentNullException"><paramref name="suffix"/> is null.</exception> /// <exception cref="InvalidOperationException">Builder is not writable, it has been linked with another one.</exception> public void LinkSuffix(BlobBuilder suffix) { if (suffix == null) { throw new ArgumentNullException(nameof(suffix)); } // TODO: consider copying data from right to left while there is space if (!IsHead || !suffix.IsHead) { Throw.InvalidOperationBuilderAlreadyLinked(); } // avoid chaining empty chunks: if (suffix.Count == 0) { return; } // swap buffers of the heads: var suffixBuffer = suffix._buffer; uint suffixLength = suffix._length; suffix._buffer = _buffer; suffix._length = FrozenLength; // suffix is not a head anymore _buffer = suffixBuffer; _length = suffixLength; int suffixPreviousLength = suffix._previousLength; suffix._previousLength = _previousLength; _previousLength = _previousLength + suffix.Length + suffixPreviousLength; // First and last chunks: // // [First]->[]->[Last] <- [this] [SuffixFirst]->[]->[SuffixLast] <- [suffix] // ^___________| ^_________________| // // Degenerate cases: // this == First == Last and/or suffix == SuffixFirst == SuffixLast. var first = FirstChunk; var suffixFirst = suffix.FirstChunk; var last = _nextOrPrevious; var suffixLast = suffix._nextOrPrevious; // Relink like so: // [First]->[]->[Last] -> [suffix] -> [SuffixFirst]->[]->[SuffixLast] <- [this] // ^_______________________________________________________| _nextOrPrevious = suffixLast; suffix._nextOrPrevious = (suffixFirst != suffix) ? suffixFirst : (first != this) ? first : suffix; if (last != this) { last._nextOrPrevious = suffix; } if (suffixLast != suffix) { suffixLast._nextOrPrevious = (first != this) ? first : suffix; } CheckInvariants(); suffix.CheckInvariants(); }