From ec75db22941d833ef542b43c52c75d143aeba48a Mon Sep 17 00:00:00 2001
From: Qi Wang <wangqi@linux.alibaba.com>
Date: Mon, 6 Jun 2022 10:19:46 +0800
Subject: tests: add tests for `LZ4_decompress_safe_partial_usingDict`

Signed-off-by: Qi Wang <wangqi@linux.alibaba.com>
---
 tests/.gitignore                     |  2 +-
 tests/Makefile                       |  7 ++-
 tests/decompress-partial-usingDict.c | 88 ++++++++++++++++++++++++++++++++++++
 tests/fullbench.c                    | 39 +++++++++++++---
 tests/fuzzer.c                       | 40 ++++++++++++++++
 5 files changed, 168 insertions(+), 8 deletions(-)
 create mode 100644 tests/decompress-partial-usingDict.c

diff --git a/tests/.gitignore b/tests/.gitignore
index 99351af..346c989 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -12,7 +12,7 @@ roundTripTest
 checkTag
 checkFrame
 decompress-partial
-
+decompress-partial-usingDict
 # test artefacts
 tmp*
 versionsTest
diff --git a/tests/Makefile b/tests/Makefile
index b4d40ca..9f83f06 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -112,6 +112,9 @@ checkFrame : lz4frame.o lz4.o lz4hc.o xxhash.o checkFrame.c
 decompress-partial: lz4.o decompress-partial.c
 	$(CC) $(FLAGS) $^ -o $@$(EXT)
 
+decompress-partial-usingDict: lz4.o decompress-partial-usingDict.c
+	$(CC) $(FLAGS) $^ -o $@$(EXT)
+
 .PHONY: clean
 clean:
 	@$(MAKE) -C $(LZ4DIR) $@ > $(VOID)
@@ -547,8 +550,10 @@ test-mem: lz4 datagen fuzzer frametest fullbench
 test-mem32: lz4c32 datagen
 # unfortunately, valgrind doesn't seem to work with non-native binary...
 
-test-decompress-partial : decompress-partial
+test-decompress-partial : decompress-partial decompress-partial-usingDict
 	@echo "\n ---- test decompress-partial ----"
 	./decompress-partial$(EXT)
+	@echo "\n ---- test decompress-partial-usingDict ----"
+	./decompress-partial-usingDict$(EXT)
 
 endif
diff --git a/tests/decompress-partial-usingDict.c b/tests/decompress-partial-usingDict.c
new file mode 100644
index 0000000..cfcb971
--- /dev/null
+++ b/tests/decompress-partial-usingDict.c
@@ -0,0 +1,88 @@
+#include "stdio.h"
+#include "string.h"
+#include "stdlib.h"
+#include "lz4.h"
+
+const char source[] =
+  "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\n"
+  "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim\n"
+  "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea\n"
+  "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate\n"
+  "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat\n"
+  "cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id\n"
+  "est laborum.\n"
+  "\n"
+  "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium\n"
+  "doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore\n"
+  "veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim\n"
+  "ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia\n"
+  "consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque\n"
+  "porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur,\n"
+  "adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore\n"
+  "et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis\n"
+  "nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid\n"
+  "ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea\n"
+  "voluptate velit esse quam nihil molestiae consequatur, vel illum qui\n"
+  "dolorem eum fugiat quo voluptas nulla pariatur?\n";
+
+#define BUFFER_SIZE 2048
+
+int main(void)
+{
+  int srcLen = (int)strlen(source);
+  size_t const smallSize = 1024;
+  size_t const largeSize = 64 * 1024 - 1;
+  char cmpBuffer[BUFFER_SIZE];
+  char buffer[BUFFER_SIZE + largeSize];
+  char* outBuffer = buffer + largeSize;
+  char* const dict = (char*)malloc(largeSize);
+  char* const largeDict = dict;
+  char* const smallDict = dict + largeSize - smallSize;
+  int cmpSize;
+  int i;
+
+  cmpSize = LZ4_compress_default(source, cmpBuffer, srcLen, BUFFER_SIZE);
+
+  for (i = cmpSize; i < cmpSize + 10; ++i) {
+    int result = LZ4_decompress_safe_partial_usingDict(cmpBuffer, outBuffer, i, srcLen, BUFFER_SIZE, NULL, 0);
+    if ((result < 0) || (result != srcLen) || memcmp(source, outBuffer, srcLen)) {
+      printf("test decompress-partial-usingDict with no dict error \n");
+      return -1;
+    }
+  }
+  
+  for (i = cmpSize; i < cmpSize + 10; ++i) {
+    int result = LZ4_decompress_safe_partial_usingDict(cmpBuffer, outBuffer, i, srcLen, BUFFER_SIZE, outBuffer - smallSize, smallSize);
+    if ((result < 0) || (result != srcLen) || memcmp(source, outBuffer, srcLen)) {
+      printf("test decompress-partial-usingDict with small prefix error \n");
+      return -1;
+    }
+  }
+
+  for (i = cmpSize; i < cmpSize + 10; ++i) {
+    int result = LZ4_decompress_safe_partial_usingDict(cmpBuffer, outBuffer, i, srcLen, BUFFER_SIZE, buffer, largeSize);
+    if ((result < 0) || (result != srcLen) || memcmp(source, outBuffer, srcLen)) {
+      printf("test decompress-partial-usingDict with large prefix error \n");
+      return -1;
+    }
+  }
+
+  for (i = cmpSize; i < cmpSize + 10; ++i) {
+    int result = LZ4_decompress_safe_partial_usingDict(cmpBuffer, outBuffer, i, srcLen, BUFFER_SIZE, smallDict, smallSize);
+    if ((result < 0) || (result != srcLen) || memcmp(source, outBuffer, srcLen)) {
+      printf("test decompress-partial-usingDict with small external dict error \n");
+      return -1;
+    }
+  }
+
+  for (i = cmpSize; i < cmpSize + 10; ++i) {
+    int result = LZ4_decompress_safe_partial_usingDict(cmpBuffer, outBuffer, i, srcLen, BUFFER_SIZE, largeDict, largeSize);
+    if ((result < 0) || (result != srcLen) || memcmp(source, outBuffer, srcLen)) {
+      printf("test decompress-partial-usingDict with large external dict error \n");
+      return -1;
+    }
+  }
+
+  printf("test decompress-partial-usingDict OK \n");
+  return 0;
+}
diff --git a/tests/fullbench.c b/tests/fullbench.c
index 55bf0b7..ec20dcb 100644
--- a/tests/fullbench.c
+++ b/tests/fullbench.c
@@ -312,6 +312,13 @@ static int local_LZ4_decompress_safe_usingDict(const char* in, char* out, int in
     return outSize;
 }
 
+static int local_LZ4_decompress_safe_partial_usingDict(const char* in, char* out, int inSize, int outSize)
+{
+    int result = LZ4_decompress_safe_partial_usingDict(in, out, inSize, outSize - 5, outSize, out - 65536, 65536);
+    if (result < 0) return result;
+    return outSize;
+}
+
 #ifndef LZ4_DLL_IMPORT
 #if defined (__cplusplus)
 extern "C" {
@@ -325,12 +332,30 @@ extern int LZ4_decompress_safe_forceExtDict(const char* in, char* out, int inSiz
 
 static int local_LZ4_decompress_safe_forceExtDict(const char* in, char* out, int inSize, int outSize)
 {
-    (void)inSize;
     LZ4_decompress_safe_forceExtDict(in, out, inSize, outSize, out - 65536, 65536);
     return outSize;
 }
 #endif
 
+#ifndef LZ4_DLL_IMPORT
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+extern int LZ4_decompress_safe_partial_forceExtDict(const char* in, char* out, int inSize, int targetOutputSize, int dstCapacity, const void* dict, size_t dictSize);
+
+#if defined (__cplusplus)
+}
+#endif
+
+static int local_LZ4_decompress_safe_partial_forceExtDict(const char* in, char* out, int inSize, int outSize)
+{
+    int result = LZ4_decompress_safe_partial_forceExtDict(in, out, inSize, outSize - 5, outSize, out - 65536, 65536);
+    if (result < 0) return result;
+    return outSize;
+}
+#endif
+
 static int local_LZ4_decompress_safe_partial(const char* in, char* out, int inSize, int outSize)
 {
     int result = LZ4_decompress_safe_partial(in, out, inSize, outSize - 5, outSize);
@@ -657,15 +682,17 @@ int fullSpeedBench(const char** fileNamesTable, int nbFiles)
             case 5: decompressionFunction = local_LZ4_decompress_safe_withPrefix64k; dName = "LZ4_decompress_safe_withPrefix64k"; break;
             case 6: decompressionFunction = local_LZ4_decompress_safe_usingDict; dName = "LZ4_decompress_safe_usingDict"; break;
             case 7: decompressionFunction = local_LZ4_decompress_safe_partial; dName = "LZ4_decompress_safe_partial"; checkResult = 0; break;
+            case 8: decompressionFunction = local_LZ4_decompress_safe_partial_usingDict; dName = "LZ4_decompress_safe_partial_usingDict"; checkResult = 0; break;
 #ifndef LZ4_DLL_IMPORT
-            case 8: decompressionFunction = local_LZ4_decompress_safe_forceExtDict; dName = "LZ4_decompress_safe_forceExtDict"; break;
+            case 9: decompressionFunction = local_LZ4_decompress_safe_partial_forceExtDict; dName = "LZ4_decompress_safe_partial_forceExtDict"; checkResult = 0; break;
+            case 10: decompressionFunction = local_LZ4_decompress_safe_forceExtDict; dName = "LZ4_decompress_safe_forceExtDict"; break;
 #endif
-            case 10:
             case 11:
             case 12:
-                if (dAlgNb == 10) { decompressionFunction = local_LZ4F_decompress; dName = "LZ4F_decompress"; }  /* can be skipped */
-                if (dAlgNb == 11) { decompressionFunction = local_LZ4F_decompress_followHint; dName = "LZ4F_decompress_followHint"; }  /* can be skipped */
-                if (dAlgNb == 12) { decompressionFunction = local_LZ4F_decompress_noHint; dName = "LZ4F_decompress_noHint"; }  /* can be skipped */
+            case 13:
+                if (dAlgNb == 11) { decompressionFunction = local_LZ4F_decompress; dName = "LZ4F_decompress"; }  /* can be skipped */
+                if (dAlgNb == 12) { decompressionFunction = local_LZ4F_decompress_followHint; dName = "LZ4F_decompress_followHint"; }  /* can be skipped */
+                if (dAlgNb == 13) { decompressionFunction = local_LZ4F_decompress_noHint; dName = "LZ4F_decompress_noHint"; }  /* can be skipped */
                 /* prepare compressed data using frame format */
                 {   size_t const fcsize = LZ4F_compressFrame(compressed_buff, (size_t)compressedBuffSize, orig_buff, benchedSize, NULL);
                     assert(!LZ4F_isError(fcsize));
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index e6fa13c..07d63a2 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -630,6 +630,46 @@ static int FUZ_test(U32 seed, U32 nbCycles, const U32 startCycle, const double c
             FUZ_CHECKTEST(memcmp(block, decodedBuffer, (size_t)targetSize), "LZ4_decompress_safe_partial: corruption detected in regenerated data");
         }
 
+        /* Partial decompression using dictionary. */
+        FUZ_DISPLAYTEST("test LZ4_decompress_safe_partial_usingDict using no dict");
+        {   size_t const missingOutBytes = FUZ_rand(&randState) % (unsigned)blockSize;
+            int const targetSize = (int)((size_t)blockSize - missingOutBytes);
+            size_t const extraneousInBytes = FUZ_rand(&randState) % 2;
+            int const inCSize = (int)((size_t)compressedSize + extraneousInBytes);
+            char const sentinel = decodedBuffer[targetSize] = block[targetSize] ^ 0x5A;
+            int const decResult = LZ4_decompress_safe_partial_usingDict(compressedBuffer, decodedBuffer, inCSize, targetSize, blockSize, NULL, 0);
+            FUZ_CHECKTEST(decResult<0, "LZ4_decompress_safe_partial_usingDict failed despite valid input data (error:%i)", decResult);
+            FUZ_CHECKTEST(decResult != targetSize, "LZ4_decompress_safe_partial_usingDict did not regenerated required amount of data (%i < %i <= %i)", decResult, targetSize, blockSize);
+            FUZ_CHECKTEST(decodedBuffer[targetSize] != sentinel, "LZ4_decompress_safe_partial_usingDict overwrite beyond requested size (though %i <= %i <= %i)", decResult, targetSize, blockSize);
+            FUZ_CHECKTEST(memcmp(block, decodedBuffer, (size_t)targetSize), "LZ4_decompress_safe_partial_usingDict: corruption detected in regenerated data");
+        }
+
+        FUZ_DISPLAYTEST("test LZ4_decompress_safe_partial_usingDict() using prefix as dict");
+        {   size_t const missingOutBytes = FUZ_rand(&randState) % (unsigned)blockSize;
+            int const targetSize = (int)((size_t)blockSize - missingOutBytes);
+            size_t const extraneousInBytes = FUZ_rand(&randState) % 2;
+            int const inCSize = (int)((size_t)compressedSize + extraneousInBytes);
+            char const sentinel = decodedBuffer[targetSize] = block[targetSize] ^ 0x5A;
+            int const decResult = LZ4_decompress_safe_partial_usingDict(compressedBuffer, decodedBuffer, inCSize, targetSize, blockSize, decodedBuffer, dictSize);
+            FUZ_CHECKTEST(decResult<0, "LZ4_decompress_safe_partial_usingDict failed despite valid input data (error:%i)", decResult);
+            FUZ_CHECKTEST(decResult != targetSize, "LZ4_decompress_safe_partial_usingDict did not regenerated required amount of data (%i < %i <= %i)", decResult, targetSize, blockSize);
+            FUZ_CHECKTEST(decodedBuffer[targetSize] != sentinel, "LZ4_decompress_safe_partial_usingDict overwrite beyond requested size (though %i <= %i <= %i)", decResult, targetSize, blockSize);
+            FUZ_CHECKTEST(memcmp(block, decodedBuffer, (size_t)targetSize), "LZ4_decompress_safe_partial_usingDict: corruption detected in regenerated data");
+        }
+
+        FUZ_DISPLAYTEST("test LZ4_decompress_safe_partial_usingDict() using external dict");
+        {   size_t const missingOutBytes = FUZ_rand(&randState) % (unsigned)blockSize;
+            int const targetSize = (int)((size_t)blockSize - missingOutBytes);
+            size_t const extraneousInBytes = FUZ_rand(&randState) % 2;
+            int const inCSize = (int)((size_t)compressedSize + extraneousInBytes);
+            char const sentinel = decodedBuffer[targetSize] = block[targetSize] ^ 0x5A;
+            int const decResult = LZ4_decompress_safe_partial_usingDict(compressedBuffer, decodedBuffer, inCSize, targetSize, blockSize, dict, dictSize);
+            FUZ_CHECKTEST(decResult<0, "LZ4_decompress_safe_partial_usingDict failed despite valid input data (error:%i)", decResult);
+            FUZ_CHECKTEST(decResult != targetSize, "LZ4_decompress_safe_partial_usingDict did not regenerated required amount of data (%i < %i <= %i)", decResult, targetSize, blockSize);
+            FUZ_CHECKTEST(decodedBuffer[targetSize] != sentinel, "LZ4_decompress_safe_partial_usingDict overwrite beyond requested size (though %i <= %i <= %i)", decResult, targetSize, blockSize);
+            FUZ_CHECKTEST(memcmp(block, decodedBuffer, (size_t)targetSize), "LZ4_decompress_safe_partial_usingDict: corruption detected in regenerated data");
+        }
+
         /* Test Compression with limited output size */
 
         /* Test compression with output size being exactly what's necessary (should work) */
-- 
cgit v0.12