Example #1
0
        static LookupResult LookupWithAhead(
            ReadOnlySpan <byte> src,
            int position,
            ref RevolvingBufferTracker <int> window,
            Span <int> recents)
        {
            var result1 = Lookup(src, position, ref window, recents);

            if (result1.Offset >= 0 && (position + 1) < src.Length)
            {
                // Store recent value for restoring afterwards.
                var prevRecent = recents[src[position]];
                Remember(src, position, ref window, recents);

                // Perform lookup for next byte.
                var result2 = Lookup(src, position + 1, ref window, recents);

                // Restore recent, pop previous delta value.
                recents[src[position]] = prevRecent;
                window.Pop();

                if (result2.Length >= result1.Length + 2)
                {
                    // If result2 is preferred, return with SkipByte set.
                    return(result2.WithSkipByte(true));
                }
            }
            return(result1);
        }
Example #2
0
        /// <summary>
        /// Update Deltas window and Recents for value of current byte.
        /// </summary>
        /// <param name="src">Source buffer.</param>
        /// <param name="position">Position in source buffer.</param>
        /// <param name="window">Deltas window.</param>
        /// <param name="recents">Recents buffer.</param>
        static void Remember(
            ReadOnlySpan <byte> src,
            int position,
            ref RevolvingBufferTracker <int> window,
            Span <int> recents)
        {
            // Updates both Recents and Window.
            var value  = src[position];
            var recent = recents[value] - 1;

            recents[value] = position + 1;
            window.Push(recent);
        }
Example #3
0
        /// <summary>
        /// Perform lookup for byte string at position.
        /// </summary>
        /// <param name="src">Source buffer.</param>
        /// <param name="position">Position in source buffer.</param>
        /// <param name="window">Deltas window.</param>
        /// <param name="recents">Recents buffer.</param>
        /// <returns></returns>
        static LookupResult Lookup(
            ReadOnlySpan <byte> src,
            int position,
            ref RevolvingBufferTracker <int> window,
            Span <int> recents)
        {
            // Get offset to previous index of current byte value.
            var c      = src[position];
            var recent = recents[c] - 1;

            // Check if: recent is 0 (no previous byte with this value), or offset is out-of-range of window.
            if (recent < 0 || window.Length < (position - recent))
            {
                return(LookupResult.NoneFound());
            }
            else
            {
                // Start searching at recent position.
                var searchPosition = recent;

                var currentSlice        = src.Slice(position, Math.Min(MaxCompare, src.Length - position));
                var windowStartPosition = Math.Max(position - window.Length, 0);
                var result = LookupResult.NoneFound();

                // Follow chain backwards and look for matches.
                while (windowStartPosition <= searchPosition)
                {
                    // Get slice for bytes to compare against currentSlice
                    var searchSlice = src.Slice(searchPosition);

                    // Find number of potential matches in search slice.
                    var potentialMatches = Math.Min(currentSlice.Length, searchSlice.Length);

                    // Set matches count to maximum, will update if loop exits early.
                    var matches = potentialMatches;
                    for (int i = 0; i < potentialMatches; i++)
                    {
                        if (currentSlice[i] != searchSlice[i])
                        {
                            matches = i;
                            break;
                        }
                    }

                    // Update lookup result if found new longest match.
                    if (matches >= 3 && matches >= result.Length)
                    {
                        result = new LookupResult(searchPosition, matches);

                        // If we have found a matching string with the max compare size, return early.
                        if (result.Length == MaxCompare)
                        {
                            break;
                        }
                    }

                    // Get search position relative to window start, and update search position.
                    var windowIndex = searchPosition - windowStartPosition;
                    searchPosition = window.Get(windowIndex);

                    // If next index is less-than 0, no previous instance of this value.
                    if (searchPosition < 0)
                    {
                        break;
                    }
                }

                return(result);
            }
        }
Example #4
0
        /// <summary>
        /// Perform raw encode using provided buffers for "Deltas" and "Recents" values with specified <see cref="LookupScheme"/>.
        /// </summary>
        /// <param name="src">Source buffer.</param>
        /// <param name="dst">Destination buffer.</param>
        /// <param name="recents">Provided buffer for storing Recents.</param>
        /// <param name="windowBuf">Provided buffer for storing Deltas.</param>
        /// <param name="lookupScheme">Scheme to use for lookups.</param>
        /// <returns>Length of encoded bytes.</returns>
        public static int EncodeRaw_Internal(
            ReadOnlySpan <byte> src,
            Span <byte> dst,
            Span <int> recents,
            Span <int> windowBuf,
            LookupScheme lookupScheme = LookupScheme.LookAhead)
        {
            // Variables to keep track of indexes for src/dst buffers, and code byte.
            int codeBytePlace = 0, commandBit = 0, dstPlace = 0, srcPlace = 0;

            // Helper for tracking state of "Deltas" revolving buffer.
            var window = new RevolvingBufferTracker <int>(windowBuf);

            // Recent lookup result.
            var result = LookupResult.NoneFound();

            // Whether or not to use LookAhead lookup scheme.
            bool useLookAheadScheme = lookupScheme == LookupScheme.LookAhead;

            while (srcPlace < src.Length)
            {
                if (commandBit == 0)
                {
                    // Reserve code byte location for modifying.
                    codeBytePlace      = dstPlace++;
                    dst[codeBytePlace] = 0;
                }

                // Perform lookup if result does not have a valid offset from previous loop.
                if (!result.Found)
                {
                    if (useLookAheadScheme)
                    {
                        // Perform lookup for both current byte and next byte.
                        result = LookupWithAhead(src, srcPlace, ref window, recents);
                    }
                    else
                    {
                        // Perform standard lookup for only the current byte.
                        result = Lookup(src, srcPlace, ref window, recents);
                    }
                }

                // Append to Deltas window and Recents.
                Remember(src, srcPlace, ref window, recents);

                // If found chunk in lookup, encode information about it.
                if (!result.SkipByte && result.Found)
                {
                    // Calculate distance between current src offset and chunk offset.
                    var distance = srcPlace - result.Offset - 1;

                    // Write bytes to indicate RLE copy.
                    // Encodes differently depending on chunk length.
                    if (result.Length < 0x12)
                    {
                        // Encode for chunk size smaller than 0x12. Uses 2 bytes.
                        dst[dstPlace]     = (byte)(((result.Length - 2) << 4) | (distance >> 8));
                        dst[dstPlace + 1] = (byte)(distance & 0xFF);
                        dstPlace         += 2;
                    }
                    else
                    {
                        // Encode for chunk size 0x12 or larger (up to 0x111). Uses 3 bytes.
                        dst[dstPlace]     = (byte)(distance >> 8);
                        dst[dstPlace + 1] = (byte)(distance & 0xFF);
                        dst[dstPlace + 2] = (byte)(result.Length - 0x12);
                        dstPlace         += 3;
                    }

                    // Would like to call method WriteRLEIndicator instead of above code, but it causes a performance hit.
                    // dstPlace += WriteRLEIndicator(dst, dstPlace, distance, result.Length);

                    // Go through each matched byte and append to delta window.
                    // Todo: Maybe don't do this if already finished encoding?
                    for (int j = 1; j < result.Length; j++)
                    {
                        Remember(src, srcPlace + j, ref window, recents);
                    }

                    srcPlace += result.Length;
                }
                else
                {
                    // If no result from lookup, set bit for direct copy.
                    dst[codeBytePlace] |= (byte)(1 << (7 - commandBit));
                    dst[dstPlace++]     = src[srcPlace++];
                }

                // Update result state.
                result = LookupResult.ClearIfNotSkipped(ref result);

                // Update command bit index.
                commandBit = (commandBit + 1) % 8;
            }

            return(dstPlace);
        }