/// <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); }
/// <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> /// <returns>Length of encoded bytes.</returns> public static int EncodeRaw(ReadOnlySpan <byte> src, Span <byte> dst, Span <int> recents, Span <int> windowBuf, LookupScheme lookupScheme = LookupScheme.LookAhead) { if (recents.Length < 0x100) { ThrowArgumentOutOfRangeExceptionForRecents("recents"); } if (windowBuf.Length > 0x1000) { ThrowArgumentOutOfRangeExceptionForDeltas("windowBuf"); } return(EncodeRaw_Internal(src, dst, recents, windowBuf, lookupScheme)); }