From c80dfe3a95f298065d334cc0086cd7b711fe6e3d Mon Sep 17 00:00:00 2001 From: ikpil Date: Sun, 1 Oct 2023 17:29:22 +0900 Subject: [PATCH] feat: test completion --- src/DotFastLZ.Compression/FastLZ.cs | 966 ++++++++---------- src/DotFastLZ.Compression/FastLZv2.cs | 620 ----------- src/DotFastLZ.SixPack/Program.cs | 14 +- .../Fixtures/RefImpl.cs | 20 +- .../Fixtures/TestHelper.cs | 12 +- 5 files changed, 469 insertions(+), 1163 deletions(-) delete mode 100644 src/DotFastLZ.Compression/FastLZv2.cs diff --git a/src/DotFastLZ.Compression/FastLZ.cs b/src/DotFastLZ.Compression/FastLZ.cs index 841c0ef..4704808 100644 --- a/src/DotFastLZ.Compression/FastLZ.cs +++ b/src/DotFastLZ.Compression/FastLZ.cs @@ -1,7 +1,6 @@ /* FastLZ - Byte-aligned LZ77 compression library - Copyright (C) 2005-2020 Ariya Hidayat https://github.com/ariya/FastLZ - jfastlz library written by William Kinney https://code.google.com/p/jfastlz + Copyright (C) 2005-2020 Ariya Hidayat Copyright (C) 2023 Choi Ikpil https://github.com/ikpil/DotFastLZ Permission is hereby granted, free of charge, to any person obtaining a copy @@ -29,664 +28,593 @@ namespace DotFastLZ.Compression { public static class FastLZ { - public const string VERSION_STRING = "0.5.0"; - private const int MAX_DISTANCE = 8191; - private const int MAX_FARDISTANCE = 65535 + MAX_DISTANCE - 1; + public const int FASTLZ_VERSION = 0x000500; + public const int FASTLZ_VERSION_MAJOR = 0; + public const int FASTLZ_VERSION_MINOR = 5; + public const int FASTLZ_VERSION_REVISION = 0; + public const string FASTLZ_VERSION_STRING = "0.5.0"; + + public const int MAX_COPY = 32; + public const int MAX_LEN = 264; /* 256 + 8 */ + public const int MAX_L1_DISTANCE = 8192; + public const int MAX_L2_DISTANCE = 8191; + public const int MAX_FARDISTANCE = (65535 + MAX_L2_DISTANCE - 1); + public const int HASH_LOG = 13; + public const int HASH_SIZE = (1 << HASH_LOG); + public const int HASH_MASK = (HASH_SIZE - 1); - private const int HASH_LOG = 13; - private const int HASH_SIZE = 1 << HASH_LOG; // 8192 - private const int HASH_MASK = HASH_SIZE - 1; - - private const int MAX_COPY = 32; - private const int MAX_LEN = 256 + 8; + /** + Compress a block of data in the input buffer and returns the size of + compressed block. The size of input buffer is specified by length. The + minimum input buffer size is 16. - private const int MIN_RECOMENDED_LENGTH_FOR_LEVEL_2 = 1024 * 64; + The output buffer must be at least 5% larger than the input buffer + and can not be smaller than 66 bytes. - private const int MAGIC_NUMBER = 'F' << 16 | 'L' << 8 | 'Z'; + If the input is not compressible, the return value might be larger than + length (input buffer size). - private const byte BLOCK_TYPE_NON_COMPRESSED = 0x00; - private const byte BLOCK_TYPE_COMPRESSED = 0x01; - private const byte BLOCK_WITHOUT_CHECKSUM = 0x00; - private const byte BLOCK_WITH_CHECKSUM = 0x10; + The input buffer and the output buffer can not overlap. - private const int OPTIONS_OFFSET = 3; - private const int CHECKSUM_OFFSET = 4; + Compression level can be specified in parameter level. At the moment, + only level 1 and level 2 are supported. + Level 1 is the fastest compression and generally useful for short data. + Level 2 is slightly slower but it gives better compression ratio. - private const int MAX_CHUNK_LENGTH = 0xFFFF; + Note that the compressed data, regardless of the level, can always be + decompressed using the function fastlz_decompress below. + */ + public static long fastlz_compress_level(int level, byte[] input, long length, byte[] output) + { + if (level == 1) + { + return fastlz1_compress(input, length, output); + } - /** - * Do not call {@link #Compress(byte[], int, int, byte[], int, int)} for input buffers - * which length less than this value. - */ - private const int MIN_LENGTH_TO_COMPRESSION = 32; + if (level == 2) + { + return fastlz2_compress(input, length, output); + } - /** - * In this case {@link #Compress(byte[], int, int, byte[], int, int)} will choose level - * automatically depending on the length of the input buffer. If length less than - * {@link #MIN_RECOMENDED_LENGTH_FOR_LEVEL_2} {@link #LEVEL_1} will be choosen, - * otherwise {@link #LEVEL_2}. - */ - private const int LEVEL_AUTO = 0; + return 0; + } - /** - * Level 1 is the fastest compression and generally useful for short data. - */ - private const int LEVEL_1 = 1; + public static long fastlz1_compress(byte[] input, long length, byte[] output) + { + long ip = 0; + long ip_start = ip; + long ip_bound = ip + length - 4; + long ip_limit = ip + length - 12 - 1; - /** - * Level 2 is slightly slower but it gives better compression ratio. - */ - private const int LEVEL_2 = 2; + long op = 0; - /** - * The output buffer must be at least 6% larger than the input buffer and can not be smaller than 66 bytes. - * @param inputLength length of input buffer - * @return Maximum output buffer length - */ - public static int CalculateOutputBufferLength(int inputLength) - { - int tempOutputLength = (int)(inputLength * 1.06); - return Math.Max(tempOutputLength, 66); - } + long[] htab = new long[HASH_SIZE]; + long seq, hash; - /** - * Compress a block of data in the input buffer and returns the size of compressed block. - * The size of input buffer is specified by length. The minimum input buffer size is 32. - * - * If the input is not compressible, the return value might be larger than length (input buffer size). - */ - public static int Compress(byte[] input, int inOffset, int inLength, byte[] output, int outOffset, int proposedLevel) - { - int level; - if (proposedLevel == LEVEL_AUTO) - { - level = inLength < MIN_RECOMENDED_LENGTH_FOR_LEVEL_2 ? LEVEL_1 : LEVEL_2; - } - else + // Initializes hash table + for (hash = 0; hash < HASH_SIZE; ++hash) { - level = proposedLevel; + htab[hash] = 0; } - int ip = 0; - int ipBound = ip + inLength - 2; - int ipLimit = ip + inLength - 12; - - int op = 0; - - // const flzuint8* htab[HASH_SIZE]; - int[] htab = new int[HASH_SIZE]; - // const flzuint8** hslot; - int hslot; - // flzuint32 hval; - // int OK b/c address starting from 0 - int hval; - // flzuint32 copy; - // int OK b/c address starting from 0 - int copy; - - /* sanity check */ - if (inLength < 4) + // We start with literal copy + long anchor = ip; + ip += 2; + + // Main loop + while (ip < ip_limit) { - if (inLength != 0) + long refIdx; + long distance, cmp; + + // Find potential match + do { - // *op++ = length-1; - output[outOffset + op++] = (byte)(inLength - 1); - ipBound++; - while (ip <= ipBound) + seq = flz_readu32(input, ip) & 0xffffff; + hash = flz_hash(seq); + refIdx = ip_start + htab[hash]; + htab[hash] = ip - ip_start; + distance = ip - refIdx; + cmp = distance < MAX_L1_DISTANCE + ? flz_readu32(input, refIdx) & 0xffffff + : 0x1000000; + + if (ip >= ip_limit) { - output[outOffset + op++] = input[inOffset + ip++]; + break; } - return inLength + 1; + ++ip; + } while (seq != cmp); + + if (ip >= ip_limit) + { + break; + } + + --ip; + + if (ip > anchor) + { + op = flz_literals(ip - anchor, input, anchor, output, op); } - // else - return 0; + long len = flz_cmp(input, refIdx + 3, input, ip + 3, ip_bound); + op = flz1_match(len, distance, output, op); + + // Update the hash at the match boundary + ip += len; + seq = flz_readu32(input, ip); + hash = flz_hash(seq & 0xffffff); + htab[hash] = ip++ - ip_start; + seq >>= 8; + hash = flz_hash(seq); + htab[hash] = ip++ - ip_start; + + anchor = ip; } + long copy = length - anchor; + op = flz_literals(copy, input, anchor, output, op); + return op; + } + + public static long fastlz2_compress(byte[] input, long length, byte[] output) + { + long ip = 0; + long ip_start = ip; + long ip_bound = ip + length - 4; /* because readU32 */ + long ip_limit = ip + length - 12 - 1; + + long op = 0; + + long[] htab = new long[HASH_SIZE]; + long seq, hash; + /* initializes hash table */ - // for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) - for (hslot = 0; hslot < HASH_SIZE; hslot++) + for (hash = 0; hash < HASH_SIZE; ++hash) { - //*hslot = ip; - htab[hslot] = ip; + htab[hash] = 0; } /* we start with literal copy */ - copy = 2; - output[outOffset + op++] = (byte)(MAX_COPY - 1); - output[outOffset + op++] = input[inOffset + ip++]; - output[outOffset + op++] = input[inOffset + ip++]; + long anchor = ip; + ip += 2; /* main loop */ - while (ip < ipLimit) + while (ip < ip_limit) { - int refs = 0; - - long distance = 0; - - /* minimum match length */ - // flzuint32 len = 3; - // int OK b/c len is 0 and octal based - int len = 3; - - /* comparison starting-point */ - int anchor = ip; - - bool matchLabel = false; - - /* check for a run */ - if (level == LEVEL_2) - { - //If(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1)) - if (input[inOffset + ip] == input[inOffset + ip - 1] && - ReadU16(input, inOffset + ip - 1) == ReadU16(input, inOffset + ip + 1)) - { - distance = 1; - ip += 3; - refs = anchor - 1 + 3; - - /* - * goto match; - */ - matchLabel = true; - } - } + long refIdx; + long distance, cmp; - if (!matchLabel) + /* find potential match */ + do { - /* find potential match */ - // HASH_FUNCTION(hval,ip); - hval = HashFunction(input, inOffset + ip); - // hslot = htab + hval; - hslot = hval; - // refs = htab[hval]; - refs = htab[hval]; - - /* calculate distance to the match */ - distance = anchor - refs; - - /* update hash table */ - //*hslot = anchor; - htab[hslot] = anchor; - - /* is this a match? check the first 3 bytes */ - if (distance == 0 - || (level == LEVEL_1 ? distance >= MAX_DISTANCE : distance >= MAX_FARDISTANCE) - || input[inOffset + refs++] != input[inOffset + ip++] - || input[inOffset + refs++] != input[inOffset + ip++] - || input[inOffset + refs++] != input[inOffset + ip++]) + seq = flz_readu32(input, ip) & 0xffffff; + hash = flz_hash(seq); + refIdx = ip_start + htab[hash]; + htab[hash] = ip - ip_start; + distance = ip - refIdx; + cmp = distance < MAX_FARDISTANCE + ? flz_readu32(input, refIdx) & 0xffffff + : 0x1000000; + + if (ip >= ip_limit) { - /* - * goto literal; - */ - output[outOffset + op++] = input[inOffset + anchor++]; - ip = anchor; - copy++; - if (copy == MAX_COPY) - { - copy = 0; - output[outOffset + op++] = (byte)(MAX_COPY - 1); - } - - continue; + break; } - if (level == LEVEL_2) - { - /* far, needs at least 5-byte match */ - if (distance >= MAX_DISTANCE) - { - if (input[inOffset + ip++] != input[inOffset + refs++] - || input[inOffset + ip++] != input[inOffset + refs++]) - { - /* - * goto literal; - */ - output[outOffset + op++] = input[inOffset + anchor++]; - ip = anchor; - copy++; - if (copy == MAX_COPY) - { - copy = 0; - output[outOffset + op++] = (byte)(MAX_COPY - 1); - } - - continue; - } - - len += 2; - } - } - } // end If(!matchLabel) + ++ip; + } while (seq != cmp); - /* - * match: - */ - /* last matched byte */ - ip = anchor + len; + if (ip >= ip_limit) + { + break; + } - /* distance is biased */ - distance--; + --ip; - if (distance == 0) + /* far, needs at least 5-byte match */ + if (distance >= MAX_L2_DISTANCE) { - /* zero distance means a run */ - //flzuint8 x = ip[-1]; - byte x = input[inOffset + ip - 1]; - while (ip < ipBound) + if (input[refIdx + 3] != input[ip + 3] || input[refIdx + 4] != input[ip + 4]) { - if (input[inOffset + refs++] != x) - { - break; - } - else - { - ip++; - } + ++ip; + continue; } } - else + + if (ip > anchor) { - for (;;) - { - /* safe because the outer check against ip limit */ - if (input[inOffset + refs++] != input[inOffset + ip++]) - { - break; - } + op = flz_literals(ip - anchor, input, anchor, output, op); + } - if (input[inOffset + refs++] != input[inOffset + ip++]) - { - break; - } + long len = flz_cmp(input, refIdx + 3, input, ip + 3, ip_bound); + op = flz2_match(len, distance, output, op); - if (input[inOffset + refs++] != input[inOffset + ip++]) - { - break; - } + /* update the hash at match boundary */ + ip += len; + seq = flz_readu32(input, ip); + hash = flz_hash(seq & 0xffffff); + htab[hash] = ip++ - ip_start; + seq >>= 8; + hash = flz_hash(seq); + htab[hash] = ip++ - ip_start; + + anchor = ip; + } - if (input[inOffset + refs++] != input[inOffset + ip++]) - { - break; - } + long copy = length - anchor; + op = flz_literals(copy, input, anchor, output, op); - if (input[inOffset + refs++] != input[inOffset + ip++]) - { - break; - } + /* marker for fastlz2 */ + output[0] |= (1 << 5); - if (input[inOffset + refs++] != input[inOffset + ip++]) - { - break; - } + return op; + } - if (input[inOffset + refs++] != input[inOffset + ip++]) - { - break; - } + /** + Decompress a block of compressed data and returns the size of the + decompressed block. If error occurs, e.g. the compressed data is + corrupted or the output buffer is not large enough, then 0 (zero) + will be returned instead. - if (input[inOffset + refs++] != input[inOffset + ip++]) - { - break; - } + The input buffer and the output buffer can not overlap. - while (ip < ipBound) - { - if (input[inOffset + refs++] != input[inOffset + ip++]) - { - break; - } - } + Decompression is memory safe and guaranteed not to write the output buffer + more than what is specified in maxout. - break; - } - } + Note that the decompression will always work, regardless of the + compression level specified in fastlz_compress_level above (when + producing the compressed block). + */ + public static long fastlz_decompress(byte[] input, long length, byte[] output, long maxout) + { + /* magic identifier for compression level */ + int level = (input[0] >> 5) + 1; - /* if we have copied something, adjust the copy count */ - if (copy != 0) - { - /* copy is biased, '0' means 1 byte copy */ - // *(op-copy-1) = copy-1; - output[outOffset + op - copy - 1] = (byte)(copy - 1); - } - else - { - /* back, to overwrite the copy count */ - op--; - } + if (level == 1) return fastlz1_decompress(input, length, output, maxout); + if (level == 2) return fastlz2_decompress(input, length, output, maxout); + + return 0; + } - /* reset literal counter */ - copy = 0; + public static long fastlz1_decompress(byte[] input, long length, byte[] output, long maxout) + { + long ip = 0; + long ip_limit = ip + length; + long ip_bound = ip_limit - 2; - /* length is biased, '1' means a match of 3 bytes */ - ip -= 3; - len = ip - anchor; + long opOffset = 0; + long op = 0; + long op_limit = op + maxout; + long ctrl = input[ip++] & 31; - /* encode the match */ - if (level == LEVEL_2) + while (true) + { + if (ctrl >= 32) { - if (distance < MAX_DISTANCE) + long len = (ctrl >> 5) - 1; + long ofs = (ctrl & 31) << 8; + long refIdx = op - ofs - 1; + if (len == 7 - 1) { - if (len < 7) + if (!(ip <= ip_bound)) { - output[outOffset + op++] = (byte)((len << 5) + (int)((ulong)distance >> 8)); - output[outOffset + op++] = (byte)(distance & 255); + return 0; } - else - { - output[outOffset + op++] = (byte)((7 << 5) + ((ulong)distance >> 8)); - for (len -= 7; len >= 255; len -= 255) - { - output[outOffset + op++] = (byte)255; - } - output[outOffset + op++] = (byte)len; - output[outOffset + op++] = (byte)(distance & 255); - } + len += input[ip++]; } - else + + refIdx -= input[ip++]; + len += 3; + if (!(op + len <= op_limit)) { - /* far away, but not yet in the another galaxy... */ - if (len < 7) - { - distance -= MAX_DISTANCE; - output[outOffset + op++] = (byte)((len << 5) + 31); - output[outOffset + op++] = (byte)255; - output[outOffset + op++] = (byte)((ulong)distance >> 8); - output[outOffset + op++] = (byte)(distance & 255); - } - else - { - distance -= MAX_DISTANCE; - output[outOffset + op++] = (byte)((7 << 5) + 31); - for (len -= 7; len >= 255; len -= 255) - { - output[outOffset + op++] = (byte)255; - } + return 0; + } - output[outOffset + op++] = (byte)len; - output[outOffset + op++] = (byte)255; - output[outOffset + op++] = (byte)((ulong)distance >> 8); - output[outOffset + op++] = (byte)(distance & 255); - } + if (!(refIdx >= opOffset)) + { + return 0; } + + fastlz_memmove(output, op, output, refIdx, len); + op += len; } else { - if (len > MAX_LEN - 2) + ctrl++; + if (!(op + ctrl <= op_limit)) { - while (len > MAX_LEN - 2) - { - output[outOffset + op++] = (byte)((7 << 5) + ((ulong)distance >> 8)); - output[outOffset + op++] = (byte)(MAX_LEN - 2 - 7 - 2); - output[outOffset + op++] = (byte)(distance & 255); - len -= MAX_LEN - 2; - } + return 0; } - if (len < 7) - { - output[outOffset + op++] = (byte)((len << 5) + (int)((ulong)distance >> 8)); - output[outOffset + op++] = (byte)(distance & 255); - } - else + if (!(ip + ctrl <= ip_limit)) { - output[outOffset + op++] = (byte)((7 << 5) + (int)((ulong)distance >> 8)); - output[outOffset + op++] = (byte)(len - 7); - output[outOffset + op++] = (byte)(distance & 255); + return 0; } - } - /* update the hash at match boundary */ - //HASH_FUNCTION(hval,ip); - hval = HashFunction(input, inOffset + ip); - htab[hval] = ip++; - - //HASH_FUNCTION(hval,ip); - hval = HashFunction(input, inOffset + ip); - htab[hval] = ip++; - - /* assuming literal copy */ - output[outOffset + op++] = (byte)(MAX_COPY - 1); - - continue; - - // Moved to be inline, with a 'continue' - /* - * literal: - * - output[outOffset + op++] = input[inOffset + anchor++]; - ip = anchor; - copy++; - If(copy == MAX_COPY){ - copy = 0; - output[outOffset + op++] = MAX_COPY-1; - } - */ - } + Array.Copy(input, ip, output, op, ctrl); + ip += ctrl; + op += ctrl; + } - /* left-over as literal copy */ - ipBound++; - while (ip <= ipBound) - { - output[outOffset + op++] = input[inOffset + ip++]; - copy++; - if (copy == MAX_COPY) + if (ip > ip_bound) { - copy = 0; - output[outOffset + op++] = (byte)(MAX_COPY - 1); + break; } - } - /* if we have copied something, adjust the copy length */ - if (copy != 0) - { - //*(op-copy-1) = copy-1; - output[outOffset + op - copy - 1] = (byte)(copy - 1); - } - else - { - op--; - } - - if (level == LEVEL_2) - { - /* marker for fastlz2 */ - output[outOffset] |= 1 << 5; + ctrl = input[ip++]; } return op; } - /** - * Decompress a block of compressed data and returns the size of the decompressed block. - * If error occurs, e.g. the compressed data is corrupted or the output buffer is not large - * enough, then 0 (zero) will be returned instead. - * - * Decompression is memory safe and guaranteed not to write the output buffer - * more than what is specified in outLength. - */ - public static int Decompress(byte[] input, int inOffset, int inLength, byte[] output, int outOffset, int outLength) + public static long fastlz2_decompress(byte[] input, long length, byte[] output, long maxout) { - //int level = ((*(const flzuint8*)input) >> 5) + 1; - int level = (input[inOffset] >> 5) + 1; - if (level != LEVEL_1 && level != LEVEL_2) - { - throw new Exception($"invalid level: {level} (expected: {LEVEL_1} or {LEVEL_2})"); - } + long ip = 0; + long ip_limit = ip + length; + long ip_bound = ip_limit - 2; - // const flzuint8* ip = (const flzuint8*) input; - int ip = 0; - // flzuint8* op = (flzuint8*) output; - int op = 0; - // flzuint32 ctrl = (*ip++) & 31; - long ctrl = input[inOffset + ip++] & 31; + long opOffset = 0; + long op = 0; + long op_limit = op + maxout; + long ctrl = input[ip++] & 31; - int loop = 1; - do + while (true) { - // const flzuint8* refs = op; - int refs = op; - // flzuint32 len = ctrl >> 5; - long len = ctrl >> 5; - // flzuint32 ofs = (ctrl & 31) << 8; - long ofs = (ctrl & 31) << 8; - if (ctrl >= 32) { - len--; - // refs -= ofs; - refs -= (int)ofs; + long len = (ctrl >> 5) - 1; + long ofs = (ctrl & 31) << 8; + long refIdx = op - ofs - 1; - int code; - if (len == 6) + long code; + if (len == 7 - 1) { - if (level == LEVEL_1) - { - // len += *ip++; - len += input[inOffset + ip++] & 0xFF; - } - else + do { - do + if (!(ip <= ip_bound)) { - code = input[inOffset + ip++] & 0xFF; - len += code; - } while (code == 255); - } - } + return 0; + } - if (level == LEVEL_1) - { - // refs -= *ip++; - refs -= input[inOffset + ip++] & 0xFF; + code = input[ip++]; + len += code; + } while (code == 255); } - else - { - code = input[inOffset + ip++] & 0xFF; - refs -= code; - /* match from 16-bit distance */ - // If(FASTLZ_UNEXPECT_CONDITIONAL(code==255)) - // If(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8))) - if (code == 255 && ofs == 31 << 8) + code = input[ip++]; + refIdx -= code; + len += 3; + + /* match from 16-bit distance */ + if (code == 255) + { + if (ofs == (31 << 8)) { - ofs = (input[inOffset + ip++] & 0xFF) << 8; - ofs += input[inOffset + ip++] & 0xFF; + if (!(ip < ip_bound)) + { + return 0; + } - refs = (int)(op - ofs - MAX_DISTANCE); + ofs = input[ip++] << 8; + ofs += input[ip++]; + refIdx = op - ofs - MAX_L2_DISTANCE - 1; } } - // if the output index + length of Block(?) + 3(?) is over the output limit? - if (op + len + 3 > outLength) + if (!(op + len <= op_limit)) { return 0; } - // if (FASTLZ_UNEXPECT_CONDITIONAL(refs-1 < (flzuint8 *)output)) - // if the address space of refs-1 is < the address of output? - // if we are still at the beginning of the output address? - if (refs - 1 < 0) + if (!(refIdx >= opOffset)) { return 0; } - if (ip < inLength) - { - ctrl = input[inOffset + ip++] & 0xFF; - } - else - { - loop = 0; - } - - if (refs == op) - { - /* optimize copy for a run */ - // flzuint8 b = refs[-1]; - byte b = output[outOffset + refs - 1]; - output[outOffset + op++] = b; - output[outOffset + op++] = b; - output[outOffset + op++] = b; - while (len != 0) - { - output[outOffset + op++] = b; - --len; - } - } - else - { - /* copy from reference */ - refs--; - - // *op++ = *refs++; - output[outOffset + op++] = output[outOffset + refs++]; - output[outOffset + op++] = output[outOffset + refs++]; - output[outOffset + op++] = output[outOffset + refs++]; - - while (len != 0) - { - output[outOffset + op++] = output[outOffset + refs++]; - --len; - } - } + fastlz_memmove(output, op, output, refIdx, len); + op += len; } else { ctrl++; - - if (op + ctrl > outLength) + if (!(op + ctrl <= op_limit)) { return 0; } - if (ip + ctrl > inLength) + if (!(ip + ctrl <= ip_limit)) { return 0; } - //*op++ = *ip++; - output[outOffset + op++] = input[inOffset + ip++]; + Array.Copy(input, ip, output, op, ctrl); + ip += ctrl; + op += ctrl; + } + + if (ip >= ip_limit) + { + break; + } + + ctrl = input[ip++]; + } + + return op; + } + + public static uint flz_readu32(byte[] data, long offset) + { + return ((uint)data[offset + 3] & 0xff) << 24 | + ((uint)data[offset + 2] & 0xff) << 16 | + ((uint)data[offset + 1] & 0xff) << 8 | + ((uint)data[offset + 0] & 0xff); + } + + public static ushort flz_hash(long v) + { + ulong h = ((ulong)v * 2654435769UL) >> (32 - HASH_LOG); + return (ushort)(h & HASH_MASK); + } + + /* special case of memcpy: at most MAX_COPY bytes */ + public static void flz_smallcopy(byte[] dest, long destOffset, byte[] src, long srcOffset, long count) + { + // if (count >= 4) + // { + // count -= count % 4; + // Array.Copy(src, srcOffset, dest, destOffset, count); + // } + Array.Copy(src, srcOffset, dest, destOffset, count); + } + + /* special case of memcpy: exactly MAX_COPY bytes */ + static void flz_maxcopy(byte[] dest, long destOffset, byte[] src, long secOffset) + { + Array.Copy(src, secOffset, dest, destOffset, MAX_COPY); + } + + public static long flz_literals(long runs, byte[] src, long srcOffset, byte[] dest, long destOffset) + { + while (runs >= MAX_COPY) + { + dest[destOffset++] = MAX_COPY - 1; + flz_maxcopy(dest, destOffset, src, srcOffset); + srcOffset += MAX_COPY; + destOffset += MAX_COPY; + runs -= MAX_COPY; + } + + if (runs > 0) + { + dest[destOffset++] = (byte)(runs - 1); + flz_smallcopy(dest, destOffset, src, srcOffset, runs); + destOffset += runs; + } + + return destOffset; + } + + public static long flz1_match(long len, long distance, byte[] output, long op) + { + --distance; + if (len > MAX_LEN - 2) + { + while (len > MAX_LEN - 2) + { + output[op++] = (byte)((7 << 5) + (distance >> 8)); + output[op++] = (byte)(MAX_LEN - 2 - 7 - 2); + output[op++] = (byte)(distance & 255); + len -= MAX_LEN - 2; + } + } + + if (len < 7) + { + output[op++] = (byte)((len << 5) + (distance >> 8)); + output[op++] = (byte)(distance & 255); + } + else + { + output[op++] = (byte)((7 << 5) + (distance >> 8)); + output[op++] = (byte)(len - 7); + output[op++] = (byte)((distance & 255)); + } - for (--ctrl; ctrl != 0; ctrl--) + return op; + } + + public static long flz2_match(long len, long distance, byte[] output, long op) + { + --distance; + if (distance < MAX_L2_DISTANCE) + { + if (len < 7) + { + output[op++] = (byte)((len << 5) + (distance >> 8)); + output[op++] = (byte)((distance & 255)); + } + else + { + output[op++] = (byte)((7 << 5) + (distance >> 8)); + for (len -= 7; len >= 255; len -= 255) { - // *op++ = *ip++; - output[outOffset + op++] = input[inOffset + ip++]; + output[op++] = 255; } - loop = ip < inLength ? 1 : 0; - if (loop != 0) + output[op++] = (byte)(len); + output[op++] = (byte)((distance & 255)); + } + } + else + { + /* far away, but not yet in the another galaxy... */ + if (len < 7) + { + distance -= MAX_L2_DISTANCE; + output[op++] = (byte)((len << 5) + 31); + output[op++] = (byte)(255); + output[op++] = (byte)(distance >> 8); + output[op++] = (byte)(distance & 255); + } + else + { + distance -= MAX_L2_DISTANCE; + output[op++] = (7 << 5) + 31; + for (len -= 7; len >= 255; len -= 255) { - // ctrl = *ip++; - ctrl = input[inOffset + ip++] & 0xFF; + output[op++] = 255; } - } - // While(FASTLZ_EXPECT_CONDITIONAL(loop)); - } while (loop != 0); + output[op++] = (byte)(len); + output[op++] = (byte)(255); + output[op++] = (byte)(distance >> 8); + output[op++] = (byte)(distance & 255); + } + } - // return op - (flzuint8*)output; return op; } - private static int HashFunction(byte[] p, int offset) + public static long flz_cmp(byte[] p, long pOffset, byte[] q, long qOffset, long r) { - int v = ReadU16(p, offset); - v ^= ReadU16(p, offset + 1) ^ v >> 16 - HASH_LOG; - v &= HASH_MASK; - return v; + long start = pOffset; + + if (flz_readu32(p, pOffset) == flz_readu32(q, qOffset)) + { + pOffset += 4; + qOffset += 4; + } + + while (qOffset < r) + { + if (p[pOffset++] != q[qOffset++]) + break; + } + + return pOffset - start; } - private static int ReadU16(byte[] data, int offset) + public static void fastlz_memmove(byte[] dest, long destOffset, byte[] src, long srcOffset, long count) { - if (offset + 1 >= data.Length) + if (dest.Length < destOffset + count) + { + throw new IndexOutOfRangeException($"{dest.Length} < {destOffset} + {count}"); + } + + if (src.Length < srcOffset + count) { - return data[offset] & 0xff; + throw new IndexOutOfRangeException($"{src.Length} < {srcOffset} + {count}"); } - return (data[offset + 1] & 0xff) << 8 | data[offset] & 0xff; + for (long i = 0; i < count; ++i) + { + dest[destOffset + i] = src[srcOffset + i]; + } } } } \ No newline at end of file diff --git a/src/DotFastLZ.Compression/FastLZv2.cs b/src/DotFastLZ.Compression/FastLZv2.cs deleted file mode 100644 index 4337ac5..0000000 --- a/src/DotFastLZ.Compression/FastLZv2.cs +++ /dev/null @@ -1,620 +0,0 @@ -/* - FastLZ - Byte-aligned LZ77 compression library - Copyright (C) 2005-2020 Ariya Hidayat - Copyright (C) 2023 Choi Ikpil https://github.com/ikpil/DotFastLZ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -using System; - -namespace DotFastLZ.Compression -{ - public class FastLZv2 - { - public const int FASTLZ_VERSION = 0x000500; - public const int FASTLZ_VERSION_MAJOR = 0; - public const int FASTLZ_VERSION_MINOR = 5; - public const int FASTLZ_VERSION_REVISION = 0; - public const string FASTLZ_VERSION_STRING = "0.5.0"; - - public const int MAX_COPY = 32; - public const int MAX_LEN = 264; /* 256 + 8 */ - public const int MAX_L1_DISTANCE = 8192; - public const int MAX_L2_DISTANCE = 8191; - public const int MAX_FARDISTANCE = (65535 + MAX_L2_DISTANCE - 1); - public const int HASH_LOG = 13; - public const int HASH_SIZE = (1 << HASH_LOG); - public const int HASH_MASK = (HASH_SIZE - 1); - - /** - Compress a block of data in the input buffer and returns the size of - compressed block. The size of input buffer is specified by length. The - minimum input buffer size is 16. - - The output buffer must be at least 5% larger than the input buffer - and can not be smaller than 66 bytes. - - If the input is not compressible, the return value might be larger than - length (input buffer size). - - The input buffer and the output buffer can not overlap. - - Compression level can be specified in parameter level. At the moment, - only level 1 and level 2 are supported. - Level 1 is the fastest compression and generally useful for short data. - Level 2 is slightly slower but it gives better compression ratio. - - Note that the compressed data, regardless of the level, can always be - decompressed using the function fastlz_decompress below. - */ - public static long fastlz_compress_level(int level, byte[] input, long length, byte[] output) - { - if (level == 1) - { - return fastlz1_compress(input, length, output); - } - - if (level == 2) - { - return fastlz2_compress(input, length, output); - } - - return 0; - } - - public static long fastlz1_compress(byte[] input, long length, byte[] output) - { - long ip = 0; - long ip_start = ip; - long ip_bound = ip + length - 4; - long ip_limit = ip + length - 12 - 1; - - long op = 0; - - long[] htab = new long[HASH_SIZE]; - long seq, hash; - - // Initializes hash table - for (hash = 0; hash < HASH_SIZE; ++hash) - { - htab[hash] = 0; - } - - // We start with literal copy - long anchor = ip; - ip += 2; - - // Main loop - while (ip < ip_limit) - { - long refIdx; - long distance, cmp; - - // Find potential match - do - { - seq = flz_readu32(input, ip) & 0xffffff; - hash = flz_hash(seq); - refIdx = ip_start + htab[hash]; - htab[hash] = ip - ip_start; - distance = ip - refIdx; - cmp = distance < MAX_L1_DISTANCE - ? flz_readu32(input, refIdx) & 0xffffff - : 0x1000000; - - if (ip >= ip_limit) - { - break; - } - - ++ip; - } while (seq != cmp); - - if (ip >= ip_limit) - { - break; - } - - --ip; - - if (ip > anchor) - { - op = flz_literals(ip - anchor, input, anchor, output, op); - } - - long len = flz_cmp(input, refIdx + 3, input, ip + 3, ip_bound); - op = flz1_match(len, distance, output, op); - - // Update the hash at the match boundary - ip += len; - seq = flz_readu32(input, ip); - hash = flz_hash(seq & 0xffffff); - htab[hash] = ip++ - ip_start; - seq >>= 8; - hash = flz_hash(seq); - htab[hash] = ip++ - ip_start; - - anchor = ip; - } - - long copy = length - anchor; - op = flz_literals(copy, input, anchor, output, op); - return op; - } - - public static long fastlz2_compress(byte[] input, long length, byte[] output) - { - long ip = 0; - long ip_start = ip; - long ip_bound = ip + length - 4; /* because readU32 */ - long ip_limit = ip + length - 12 - 1; - - long op = 0; - - long[] htab = new long[HASH_SIZE]; - long seq, hash; - - /* initializes hash table */ - for (hash = 0; hash < HASH_SIZE; ++hash) - { - htab[hash] = 0; - } - - /* we start with literal copy */ - long anchor = ip; - ip += 2; - - /* main loop */ - while (ip < ip_limit) - { - long refIdx; - long distance, cmp; - - /* find potential match */ - do - { - seq = flz_readu32(input, ip) & 0xffffff; - hash = flz_hash(seq); - refIdx = ip_start + htab[hash]; - htab[hash] = ip - ip_start; - distance = ip - refIdx; - cmp = distance < MAX_FARDISTANCE - ? flz_readu32(input, refIdx) & 0xffffff - : 0x1000000; - - if (ip >= ip_limit) - { - break; - } - - ++ip; - } while (seq != cmp); - - if (ip >= ip_limit) - { - break; - } - - --ip; - - /* far, needs at least 5-byte match */ - if (distance >= MAX_L2_DISTANCE) - { - if (input[refIdx + 3] != input[ip + 3] || input[refIdx + 4] != input[ip + 4]) - { - ++ip; - continue; - } - } - - if (ip > anchor) - { - op = flz_literals(ip - anchor, input, anchor, output, op); - } - - long len = flz_cmp(input, refIdx + 3, input, ip + 3, ip_bound); - op = flz2_match(len, distance, output, op); - - /* update the hash at match boundary */ - ip += len; - seq = flz_readu32(input, ip); - hash = flz_hash(seq & 0xffffff); - htab[hash] = ip++ - ip_start; - seq >>= 8; - hash = flz_hash(seq); - htab[hash] = ip++ - ip_start; - - anchor = ip; - } - - long copy = length - anchor; - op = flz_literals(copy, input, anchor, output, op); - - /* marker for fastlz2 */ - output[0] |= (1 << 5); - - return op; - } - - /** - Decompress a block of compressed data and returns the size of the - decompressed block. If error occurs, e.g. the compressed data is - corrupted or the output buffer is not large enough, then 0 (zero) - will be returned instead. - - The input buffer and the output buffer can not overlap. - - Decompression is memory safe and guaranteed not to write the output buffer - more than what is specified in maxout. - - Note that the decompression will always work, regardless of the - compression level specified in fastlz_compress_level above (when - producing the compressed block). - */ - public static long fastlz_decompress(byte[] input, long length, byte[] output, long maxout) - { - /* magic identifier for compression level */ - int level = (input[0] >> 5) + 1; - - if (level == 1) return fastlz1_decompress(input, length, output, maxout); - if (level == 2) return fastlz2_decompress(input, length, output, maxout); - - return 0; - } - - public static long fastlz1_decompress(byte[] input, long length, byte[] output, long maxout) - { - long ip = 0; - long ip_limit = ip + length; - long ip_bound = ip_limit - 2; - - long opOffset = 0; - long op = 0; - long op_limit = op + maxout; - long ctrl = input[ip++] & 31; - - while (true) - { - if (ctrl >= 32) - { - long len = (ctrl >> 5) - 1; - long ofs = (ctrl & 31) << 8; - long refIdx = op - ofs - 1; - if (len == 7 - 1) - { - if (!(ip <= ip_bound)) - { - return 0; - } - - len += input[ip++]; - } - - refIdx -= input[ip++]; - len += 3; - if (!(op + len <= op_limit)) - { - return 0; - } - - if (!(refIdx >= opOffset)) - { - return 0; - } - - fastlz_memmove(output, op, output, refIdx, len); - op += len; - } - else - { - ctrl++; - if (!(op + ctrl <= op_limit)) - { - return 0; - } - - if (!(ip + ctrl <= ip_limit)) - { - return 0; - } - - Array.Copy(input, ip, output, op, ctrl); - ip += ctrl; - op += ctrl; - } - - if (ip > ip_bound) - { - break; - } - - ctrl = input[ip++]; - } - - return op; - } - - public static long fastlz2_decompress(byte[] input, long length, byte[] output, long maxout) - { - long ip = 0; - long ip_limit = ip + length; - long ip_bound = ip_limit - 2; - - long opOffset = 0; - long op = 0; - long op_limit = op + maxout; - long ctrl = input[ip++] & 31; - - while (true) - { - if (ctrl >= 32) - { - long len = (ctrl >> 5) - 1; - long ofs = (ctrl & 31) << 8; - long refIdx = op - ofs - 1; - - long code; - if (len == 7 - 1) - { - do - { - if (!(ip <= ip_bound)) - { - return 0; - } - - code = input[ip++]; - len += code; - } while (code == 255); - } - - code = input[ip++]; - refIdx -= code; - len += 3; - - /* match from 16-bit distance */ - if (code == 255) - { - if (ofs == (31 << 8)) - { - if (!(ip < ip_bound)) - { - return 0; - } - - ofs = input[ip++] << 8; - ofs += input[ip++]; - refIdx = op - ofs - MAX_L2_DISTANCE - 1; - } - } - - if (!(op + len <= op_limit)) - { - return 0; - } - - if (!(refIdx >= opOffset)) - { - return 0; - } - - fastlz_memmove(output, op, output, refIdx, len); - op += len; - } - else - { - ctrl++; - if (!(op + ctrl <= op_limit)) - { - return 0; - } - - if (!(ip + ctrl <= ip_limit)) - { - return 0; - } - - Array.Copy(input, ip, output, op, ctrl); - ip += ctrl; - op += ctrl; - } - - if (ip >= ip_limit) - { - break; - } - - ctrl = input[ip++]; - } - - return op; - } - - public static uint flz_readu32(byte[] data, long offset) - { - return ((uint)data[offset + 3] & 0xff) << 24 | - ((uint)data[offset + 2] & 0xff) << 16 | - ((uint)data[offset + 1] & 0xff) << 8 | - ((uint)data[offset + 0] & 0xff); - } - - public static ushort flz_hash(long v) - { - ulong h = ((ulong)v * 2654435769UL) >> (32 - HASH_LOG); - return (ushort)(h & HASH_MASK); - } - - /* special case of memcpy: at most MAX_COPY bytes */ - public static void flz_smallcopy(byte[] dest, long destOffset, byte[] src, long srcOffset, long count) - { - // if (count >= 4) - // { - // count -= count % 4; - // Array.Copy(src, srcOffset, dest, destOffset, count); - // } - Array.Copy(src, srcOffset, dest, destOffset, count); - } - - /* special case of memcpy: exactly MAX_COPY bytes */ - static void flz_maxcopy(byte[] dest, long destOffset, byte[] src, long secOffset) - { - Array.Copy(src, secOffset, dest, destOffset, MAX_COPY); - } - - public static long flz_literals(long runs, byte[] src, long srcOffset, byte[] dest, long destOffset) - { - while (runs >= MAX_COPY) - { - dest[destOffset++] = MAX_COPY - 1; - flz_maxcopy(dest, destOffset, src, srcOffset); - srcOffset += MAX_COPY; - destOffset += MAX_COPY; - runs -= MAX_COPY; - } - - if (runs > 0) - { - dest[destOffset++] = (byte)(runs - 1); - flz_smallcopy(dest, destOffset, src, srcOffset, runs); - destOffset += runs; - } - - return destOffset; - } - - public static long flz1_match(long len, long distance, byte[] output, long op) - { - --distance; - if (len > MAX_LEN - 2) - { - while (len > MAX_LEN - 2) - { - output[op++] = (byte)((7 << 5) + (distance >> 8)); - output[op++] = (byte)(MAX_LEN - 2 - 7 - 2); - output[op++] = (byte)(distance & 255); - len -= MAX_LEN - 2; - } - } - - if (len < 7) - { - output[op++] = (byte)((len << 5) + (distance >> 8)); - output[op++] = (byte)(distance & 255); - } - else - { - output[op++] = (byte)((7 << 5) + (distance >> 8)); - output[op++] = (byte)(len - 7); - output[op++] = (byte)((distance & 255)); - } - - return op; - } - - public static long flz2_match(long len, long distance, byte[] output, long op) - { - --distance; - if (distance < MAX_L2_DISTANCE) - { - if (len < 7) - { - output[op++] = (byte)((len << 5) + (distance >> 8)); - output[op++] = (byte)((distance & 255)); - } - else - { - output[op++] = (byte)((7 << 5) + (distance >> 8)); - for (len -= 7; len >= 255; len -= 255) - { - output[op++] = 255; - } - - output[op++] = (byte)(len); - output[op++] = (byte)((distance & 255)); - } - } - else - { - /* far away, but not yet in the another galaxy... */ - if (len < 7) - { - distance -= MAX_L2_DISTANCE; - output[op++] = (byte)((len << 5) + 31); - output[op++] = (byte)(255); - output[op++] = (byte)(distance >> 8); - output[op++] = (byte)(distance & 255); - } - else - { - distance -= MAX_L2_DISTANCE; - output[op++] = (7 << 5) + 31; - for (len -= 7; len >= 255; len -= 255) - { - output[op++] = 255; - } - - output[op++] = (byte)(len); - output[op++] = (byte)(255); - output[op++] = (byte)(distance >> 8); - output[op++] = (byte)(distance & 255); - } - } - - return op; - } - - public static long flz_cmp(byte[] p, long pOffset, byte[] q, long qOffset, long r) - { - long start = pOffset; - - if (flz_readu32(p, pOffset) == flz_readu32(q, qOffset)) - { - pOffset += 4; - qOffset += 4; - } - - while (qOffset < r) - { - if (p[pOffset++] != q[qOffset++]) - break; - } - - return pOffset - start; - } - - public static void fastlz_memmove(byte[] dest, long destOffset, byte[] src, long srcOffset, long count) - { - if (dest.Length < destOffset + count) - { - throw new IndexOutOfRangeException($"{dest.Length} < {destOffset} + {count}"); - } - - if (src.Length < srcOffset + count) - { - throw new IndexOutOfRangeException($"{src.Length} < {srcOffset} + {count}"); - } - - for (long i = 0; i < count; ++i) - { - dest[destOffset + i] = src[srcOffset + i]; - } - } - } -} \ No newline at end of file diff --git a/src/DotFastLZ.SixPack/Program.cs b/src/DotFastLZ.SixPack/Program.cs index 5766df3..15d45ae 100644 --- a/src/DotFastLZ.SixPack/Program.cs +++ b/src/DotFastLZ.SixPack/Program.cs @@ -292,7 +292,7 @@ public static int pack_file_compressed(string input_file, int method, int level, /* FastLZ */ case 1: { - long chunkSize = FastLZv2.fastlz_compress_level(level, buffer, bytes_read, result); + long chunkSize = FastLZ.fastlz_compress_level(level, buffer, bytes_read, result); checksum = update_adler32(1L, result, chunkSize); write_chunk_header(f, 17, 1, chunkSize, checksum, bytes_read); f.Write(result, 0, (int)chunkSize); @@ -444,7 +444,7 @@ public static int benchmark_speed(int compress_level, string input_file) Console.WriteLine($"Benchmarking FastLZ Level {compress_level}, please wait..."); - int u = 0; + long u = 0; int i = bytes_read; fastest = 0; for (int j = 0; j < 3; j++) @@ -458,8 +458,7 @@ public static int benchmark_speed(int compress_level, string input_file) mbs = DateTime.UtcNow.Ticks; while (DateTime.UtcNow.Ticks - mbs < 3000) /* 1% accuracy with 18.2 timer */ { - //u = fastlz_compress_level(compress_level, buffer, bytes_read, result); - u = FastLZ.Compress(buffer, 0, bytes_read, result, 0, compress_level); + u = FastLZ.fastlz_compress_level(compress_level, buffer, bytes_read, result); y++; } @@ -472,7 +471,7 @@ public static int benchmark_speed(int compress_level, string input_file) Console.WriteLine($"Compressed {i} bytes into {u} bytes ({(u * 100.0 / i):F1}%) at {fastest:F1} Mbyte/s."); fastest = 0; - int compressed_size = u; + long compressed_size = u; for (int j = 0; j < 3; j++) { int y = 0; @@ -484,8 +483,7 @@ public static int benchmark_speed(int compress_level, string input_file) mbs = DateTime.UtcNow.Ticks; while (DateTime.UtcNow.Ticks - mbs < 3000) /* 1% accuracy with 18.2 timer */ { - //u = fastlz_decompress(result, compressed_size, buffer, bytes_read); - u = FastLZ.Decompress(result, 0, compressed_size, buffer, 0, bytes_read); + u = FastLZ.fastlz_decompress(result, compressed_size, buffer, bytes_read); y++; } @@ -538,7 +536,7 @@ public static int Main(string[] args) if (argument == "-v" || argument == "--version") { Console.WriteLine("6pack: high-speed file compression tool"); - Console.WriteLine($"Version {SIXPACK_VERSION_STRING} (using FastLZ {FastLZ.VERSION_STRING})"); + Console.WriteLine($"Version {SIXPACK_VERSION_STRING} (using FastLZ {FastLZ.FASTLZ_VERSION_STRING})"); Console.WriteLine("Copyright (C) Ariya Hidayat, Choi Ikpil(ikpil@naver.com"); Console.WriteLine(""); return 0; diff --git a/test/DotFastLZ.Compression.Tests/Fixtures/RefImpl.cs b/test/DotFastLZ.Compression.Tests/Fixtures/RefImpl.cs index 5e23294..a3fc30a 100644 --- a/test/DotFastLZ.Compression.Tests/Fixtures/RefImpl.cs +++ b/test/DotFastLZ.Compression.Tests/Fixtures/RefImpl.cs @@ -2,10 +2,10 @@ public static class RefImpl { - public static void REF_Level1_decompress(byte[] input, int length, byte[] output) + public static void REF_Level1_decompress(byte[] input, long length, byte[] output) { - int src = 0; - int dest = 0; + long src = 0; + long dest = 0; while (src < length) { int type = input[src] >> 5; @@ -28,11 +28,11 @@ public static void REF_Level1_decompress(byte[] input, int length, byte[] output int ofs = 256 * (input[src] & 31) + input[src + 1]; int len = 2 + (input[src] >> 5); src = src + 2; - int @ref = dest - ofs - 1; + long @ref = dest - ofs - 1; while (len > 0) { output[dest] = output[@ref]; - @ref = @ref +1; + @ref = @ref + 1; dest = dest + 1; len = len - 1; } @@ -43,7 +43,7 @@ public static void REF_Level1_decompress(byte[] input, int length, byte[] output int ofs = 256 * (input[src] & 31) + input[src + 2]; int len = 9 + input[src + 1]; src = src + 3; - int @ref = dest - ofs - 1; + long @ref = dest - ofs - 1; while (len > 0) { output[dest] = output[@ref]; @@ -55,10 +55,10 @@ public static void REF_Level1_decompress(byte[] input, int length, byte[] output } } - public static void REF_Level2_decompress(byte[] input, int length, byte[] output) + public static void REF_Level2_decompress(byte[] input, long length, byte[] output) { - int src = 0; - int dest = 0; + long src = 0; + long dest = 0; while (src < length) { int type = input[src] >> 5; @@ -107,7 +107,7 @@ public static void REF_Level2_decompress(byte[] input, int length, byte[] output src = src + next; - int @ref = dest - ofs - 1; + long @ref = dest - ofs - 1; while (len > 0) { output[dest] = output[@ref]; diff --git a/test/DotFastLZ.Compression.Tests/Fixtures/TestHelper.cs b/test/DotFastLZ.Compression.Tests/Fixtures/TestHelper.cs index c23432c..b12e94a 100644 --- a/test/DotFastLZ.Compression.Tests/Fixtures/TestHelper.cs +++ b/test/DotFastLZ.Compression.Tests/Fixtures/TestHelper.cs @@ -70,7 +70,7 @@ public static bool test_ref_decompressor_level1(string name, string file_name) Console.WriteLine("Compressing. Please wait..."); byte[] compressed_buffer = new byte[(int)(1.05 * file_size)]; - int compressed_size = FastLZ.Compress(file_buffer, 0, (int)file_size, compressed_buffer, 0, 1); + long compressed_size = FastLZ.fastlz_compress_level(1, file_buffer, file_size, compressed_buffer); double ratio = (100.0 * compressed_size) / file_size; Console.WriteLine($"Compressing was completed: {file_size} -> {compressed_size} ({ratio})"); @@ -133,7 +133,7 @@ public static bool test_ref_decompressor_level2(string name, string file_name) Console.WriteLine("Compressing. Please wait..."); byte[] compressed_buffer = new byte[(int)(1.05 * file_size)]; - int compressed_size = FastLZ.Compress(file_buffer, 0, (int)file_size, compressed_buffer, 0, 2); + long compressed_size = FastLZ.fastlz_compress_level(2, file_buffer, file_size, compressed_buffer); double ratio = (100.0 * compressed_size) / file_size; Console.WriteLine($"Compressing was completed: {file_size} -> {compressed_size} ({ratio})"); @@ -203,7 +203,7 @@ public static bool test_roundtrip_level1(string name, string file_name) byte[] compressed_buffer = new byte[(int)(1.05 * file_size)]; //int compressed_size = FastLZ.Compress(file_buffer, 0, (int)file_size, compressed_buffer, 0, 1); - long compressed_size = FastLZv2.fastlz1_compress(file_buffer, file_size, compressed_buffer); + long compressed_size = FastLZ.fastlz1_compress(file_buffer, file_size, compressed_buffer); double ratio = (100.0 * compressed_size) / file_size; Console.WriteLine($"Compressing was completed: {file_size} -> {compressed_size} ({ratio})"); @@ -217,7 +217,7 @@ public static bool test_roundtrip_level1(string name, string file_name) Array.Fill(uncompressed_buffer, (byte)'-'); - FastLZv2.fastlz1_decompress(compressed_buffer, compressed_size, uncompressed_buffer, uncompressed_buffer.Length); + FastLZ.fastlz1_decompress(compressed_buffer, compressed_size, uncompressed_buffer, uncompressed_buffer.Length); Console.WriteLine("Comparing. Please wait..."); long result = ResourceHelper.Compare(file_name, file_buffer, uncompressed_buffer, file_size); @@ -270,7 +270,7 @@ public static bool test_roundtrip_level2(string name, string file_name) byte[] compressed_buffer = new byte[(int)(1.05 * file_size)]; //int compressed_size = FastLZ.Compress(file_buffer, 0, (int)file_size, compressed_buffer, 0, 2); - long compressed_size = FastLZv2.fastlz2_compress(file_buffer, file_size, compressed_buffer); + long compressed_size = FastLZ.fastlz2_compress(file_buffer, file_size, compressed_buffer); double ratio = (100.0 * compressed_size) / file_size; Console.WriteLine($"Compressing was completed: {file_size} -> {compressed_size} ({ratio})"); @@ -285,7 +285,7 @@ public static bool test_roundtrip_level2(string name, string file_name) Array.Fill(uncompressed_buffer, (byte)'-'); //FastLZ.Decompress(compressed_buffer, 0, compressed_buffer.Length, uncompressed_buffer, 0, uncompressed_buffer.Length); - FastLZv2.fastlz2_decompress(compressed_buffer, compressed_size, uncompressed_buffer, uncompressed_buffer.Length); + FastLZ.fastlz2_decompress(compressed_buffer, compressed_size, uncompressed_buffer, uncompressed_buffer.Length); Console.WriteLine("Comparing. Please wait..."); long result = ResourceHelper.Compare(file_name, file_buffer, uncompressed_buffer, file_size);