From c843142ad006f288421f0d97cd11be279e4253c3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 1 Sep 2016 15:05:57 -0700 Subject: [PATCH 01/32] zstd -d writes to stdout when input is stdin --- programs/zstdcli.c | 18 +++++++++--------- tests/playTests.sh | 3 +++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 2d6d45214..57a271ab9 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -83,7 +83,7 @@ static const char* g_defaultDictName = "dictionary"; static const unsigned g_defaultMaxDictSize = 110 KB; -static const int g_defaultDictCLevel = 5; +static const int g_defaultDictCLevel = 3; static const unsigned g_defaultSelectivityLevel = 9; @@ -182,7 +182,7 @@ static void waitEnter(void) /*! readU32FromChar() : @return : unsigned integer value reach from input in `char` format Will also modify `*stringPtr`, advancing it to position where it stopped reading. - Note : this function can overflow if result > MAX_UINT */ + Note : this function can overflow if digit string > MAX_UINT */ static unsigned readU32FromChar(const char** stringPtr) { unsigned result = 0; @@ -289,13 +289,13 @@ int main(int argCount, char** argv) argument++; while (argument[0]!=0) { - #ifndef ZSTD_NOCOMPRESS +#ifndef ZSTD_NOCOMPRESS /* compression Level */ if ((*argument>='0') && (*argument<='9')) { dictCLevel = cLevel = readU32FromChar(&argument); continue; } - #endif +#endif switch(argument[0]) { @@ -337,7 +337,7 @@ int main(int argCount, char** argv) /* recursive */ case 'r': recursive=1; argument++; break; - #ifndef ZSTD_NOBENCH +#ifndef ZSTD_NOBENCH /* Benchmark */ case 'b': bench=1; argument++; break; @@ -368,7 +368,7 @@ int main(int argCount, char** argv) BMK_SetBlockSize(bSize); } break; - #endif /* ZSTD_NOBENCH */ +#endif /* ZSTD_NOBENCH */ /* Dictionary Selection level */ case 's': @@ -378,11 +378,11 @@ int main(int argCount, char** argv) /* Pause at the end (-p) or set an additional param (-p#) (hidden option) */ case 'p': argument++; - #ifndef ZSTD_NOBENCH +#ifndef ZSTD_NOBENCH if ((*argument>='0') && (*argument<='9')) { BMK_setAdditionalParam(readU32FromChar(&argument)); } else - #endif +#endif main_pause=1; break; /* unknown command */ @@ -470,7 +470,7 @@ int main(int argCount, char** argv) /* Check if input/output defined as console; trigger an error in this case */ if (!strcmp(filenameTable[0], stdinmark) && IS_CONSOLE(stdin) ) CLEAN_RETURN(badusage(programName)); - if (outFileName && !strcmp(outFileName, stdoutmark) && IS_CONSOLE(stdout) && !(forceStdout && decode)) + if (outFileName && !strcmp(outFileName, stdoutmark) && IS_CONSOLE(stdout) && strcmp(filenameTable[0], stdinmark) && !(forceStdout && decode)) CLEAN_RETURN(badusage(programName)); /* user-selected output filename, only possible with a single file */ diff --git a/tests/playTests.sh b/tests/playTests.sh index b887613f7..64b3fd956 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -41,6 +41,7 @@ $ECHO "\nStarting playTests.sh isWindows=$isWindows" [ -n "$ZSTD" ] || die "ZSTD variable must be defined!" file $ZSTD + $ECHO "\n**** simple tests **** " ./datagen > tmp @@ -51,6 +52,8 @@ $ZSTD -99 -f tmp # too large compression level, automatic sized down $ECHO "test : compress to stdout" $ZSTD tmp -c > tmpCompressed $ZSTD tmp --stdout > tmpCompressed # long command format +$ECHO "test : implied stdout when input is stdin" +$ECHO bob | $ZSTD | $ZSTD -d $ECHO "test : null-length file roundtrip" $ECHO -n '' | $ZSTD - --stdout | $ZSTD -d --stdout $ECHO "test : decompress file with wrong suffix (must fail)" From c9325209601e217ca327f91b322360bcc5ca3102 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 15:22:19 -0700 Subject: [PATCH 02/32] Add PZstandard to contrib/ --- contrib/pzstd/ErrorHolder.h | 55 +++ contrib/pzstd/Makefile | 71 +++ contrib/pzstd/Options.cpp | 182 ++++++++ contrib/pzstd/Options.h | 60 +++ contrib/pzstd/Pzstd.cpp | 462 ++++++++++++++++++++ contrib/pzstd/Pzstd.h | 93 ++++ contrib/pzstd/README.md | 47 ++ contrib/pzstd/SkippableFrame.cpp | 30 ++ contrib/pzstd/SkippableFrame.h | 64 +++ contrib/pzstd/bench.cpp | 146 +++++++ contrib/pzstd/images/Cspeed.png | Bin 0 -> 58612 bytes contrib/pzstd/images/Dspeed.png | Bin 0 -> 26335 bytes contrib/pzstd/main.cpp | 34 ++ contrib/pzstd/test/Makefile | 46 ++ contrib/pzstd/test/OptionsTest.cpp | 179 ++++++++ contrib/pzstd/test/PzstdTest.cpp | 112 +++++ contrib/pzstd/test/RoundTrip.h | 89 ++++ contrib/pzstd/test/RoundTripTest.cpp | 88 ++++ contrib/pzstd/utils/Buffer.h | 99 +++++ contrib/pzstd/utils/FileSystem.h | 61 +++ contrib/pzstd/utils/Likely.h | 28 ++ contrib/pzstd/utils/Range.h | 130 ++++++ contrib/pzstd/utils/ScopeGuard.h | 50 +++ contrib/pzstd/utils/ThreadPool.h | 58 +++ contrib/pzstd/utils/WorkQueue.h | 144 ++++++ contrib/pzstd/utils/test/BufferTest.cpp | 89 ++++ contrib/pzstd/utils/test/Makefile | 41 ++ contrib/pzstd/utils/test/RangeTest.cpp | 82 ++++ contrib/pzstd/utils/test/ScopeGuardTest.cpp | 28 ++ contrib/pzstd/utils/test/ThreadPoolTest.cpp | 67 +++ contrib/pzstd/utils/test/WorkQueueTest.cpp | 176 ++++++++ 31 files changed, 2811 insertions(+) create mode 100644 contrib/pzstd/ErrorHolder.h create mode 100644 contrib/pzstd/Makefile create mode 100644 contrib/pzstd/Options.cpp create mode 100644 contrib/pzstd/Options.h create mode 100644 contrib/pzstd/Pzstd.cpp create mode 100644 contrib/pzstd/Pzstd.h create mode 100644 contrib/pzstd/README.md create mode 100644 contrib/pzstd/SkippableFrame.cpp create mode 100644 contrib/pzstd/SkippableFrame.h create mode 100644 contrib/pzstd/bench.cpp create mode 100644 contrib/pzstd/images/Cspeed.png create mode 100644 contrib/pzstd/images/Dspeed.png create mode 100644 contrib/pzstd/main.cpp create mode 100644 contrib/pzstd/test/Makefile create mode 100644 contrib/pzstd/test/OptionsTest.cpp create mode 100644 contrib/pzstd/test/PzstdTest.cpp create mode 100644 contrib/pzstd/test/RoundTrip.h create mode 100644 contrib/pzstd/test/RoundTripTest.cpp create mode 100644 contrib/pzstd/utils/Buffer.h create mode 100644 contrib/pzstd/utils/FileSystem.h create mode 100644 contrib/pzstd/utils/Likely.h create mode 100644 contrib/pzstd/utils/Range.h create mode 100644 contrib/pzstd/utils/ScopeGuard.h create mode 100644 contrib/pzstd/utils/ThreadPool.h create mode 100644 contrib/pzstd/utils/WorkQueue.h create mode 100644 contrib/pzstd/utils/test/BufferTest.cpp create mode 100644 contrib/pzstd/utils/test/Makefile create mode 100644 contrib/pzstd/utils/test/RangeTest.cpp create mode 100644 contrib/pzstd/utils/test/ScopeGuardTest.cpp create mode 100644 contrib/pzstd/utils/test/ThreadPoolTest.cpp create mode 100644 contrib/pzstd/utils/test/WorkQueueTest.cpp diff --git a/contrib/pzstd/ErrorHolder.h b/contrib/pzstd/ErrorHolder.h new file mode 100644 index 000000000..4a81a068c --- /dev/null +++ b/contrib/pzstd/ErrorHolder.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#include +#include +#include + +namespace pzstd { + +// Coordinates graceful shutdown of the pzstd pipeline +class ErrorHolder { + std::atomic error_; + std::string message_; + + public: + ErrorHolder() : error_(false) {} + + bool hasError() noexcept { + return error_.load(); + } + + void setError(std::string message) noexcept { + // Given multiple possibly concurrent calls, exactly one will ever succeed. + bool expected = false; + if (error_.compare_exchange_strong(expected, true)) { + message_ = std::move(message); + } + } + + bool check(bool predicate, std::string message) noexcept { + if (!predicate) { + setError(std::move(message)); + } + return !hasError(); + } + + std::string getError() noexcept { + error_.store(false); + return std::move(message_); + } + + ~ErrorHolder() { + if (hasError()) { + throw std::logic_error(message_); + } + } +}; +} diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile new file mode 100644 index 000000000..512a76292 --- /dev/null +++ b/contrib/pzstd/Makefile @@ -0,0 +1,71 @@ +# ########################################################################## +# Copyright (c) 2016-present, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. +# ########################################################################## + +ZSTDDIR = ../../lib +PROGDIR = ../../programs + +CPPFLAGS = -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/dictBuilder -I$(PROGDIR) -I. +CFLAGS ?= -O3 +CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wstrict-aliasing=1 \ + -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef \ + -std=c++11 +CFLAGS += $(MOREFLAGS) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) + + +ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c +ZSTDCOMP_FILES := $(ZSTDDIR)/compress/zstd_compress.c $(ZSTDDIR)/compress/fse_compress.c $(ZSTDDIR)/compress/huf_compress.c +ZSTDDECOMP_FILES := $(ZSTDDIR)/decompress/huf_decompress.c +ZSTD_FILES := $(ZSTDDECOMP_FILES) $(ZSTDCOMMON_FILES) $(ZSTDCOMP_FILES) + + +# Define *.exe as extension for Windows systems +ifneq (,$(filter Windows%,$(OS))) +EXT =.exe +else +EXT = +endif + +.PHONY: default all test clean + +default: pzstd + +all: pzstd + + +libzstd.a: $(ZSTD_FILES) + $(MAKE) -C $(ZSTDDIR) libzstd + @cp $(ZSTDDIR)/libzstd.a . + + +Pzstd.o: Pzstd.h Pzstd.cpp ErrorHolder.h utils/*.h + $(CXX) $(FLAGS) -c Pzstd.cpp -o $@ + +SkippableFrame.o: SkippableFrame.h SkippableFrame.cpp utils/*.h + $(CXX) $(FLAGS) -c SkippableFrame.cpp -o $@ + +Options.o: Options.h Options.cpp + $(CXX) $(FLAGS) -c Options.cpp -o $@ + +main.o: main.cpp *.h utils/*.h + $(CXX) $(FLAGS) -c main.cpp -o $@ + +pzstd: libzstd.a Pzstd.o SkippableFrame.o Options.o main.o + $(CXX) $(FLAGS) $^ -o $@$(EXT) + +test: libzstd.a Pzstd.o Options.o SkippableFrame.o + $(MAKE) -C utils/test test + $(MAKE) -C test test + +clean: + $(MAKE) -C $(ZSTDDIR) clean + $(MAKE) -C utils/test clean + $(MAKE) -C test clean + @$(RM) libzstd.a *.o pzstd$(EXT) + @echo Cleaning completed diff --git a/contrib/pzstd/Options.cpp b/contrib/pzstd/Options.cpp new file mode 100644 index 000000000..dc6aeef14 --- /dev/null +++ b/contrib/pzstd/Options.cpp @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "Options.h" + +#include + +namespace pzstd { + +namespace { +unsigned parseUnsigned(const char* arg) { + unsigned result = 0; + while (*arg >= '0' && *arg <= '9') { + result *= 10; + result += *arg - '0'; + ++arg; + } + return result; +} + +const std::string zstdExtension = ".zst"; +constexpr unsigned defaultCompressionLevel = 3; +constexpr unsigned maxNonUltraCompressionLevel = 19; + +void usage() { + std::fprintf(stderr, "Usage:\n"); + std::fprintf(stderr, "\tpzstd [args] FILE\n"); + std::fprintf(stderr, "Parallel ZSTD options:\n"); + std::fprintf(stderr, "\t-n/--num-threads #: Number of threads to spawn\n"); + std::fprintf(stderr, "\t-p/--pzstd-headers: Write pzstd headers to enable parallel decompression\n"); + + std::fprintf(stderr, "ZSTD options:\n"); + std::fprintf(stderr, "\t-u/--ultra : enable levels beyond %i, up to %i (requires more memory)\n", maxNonUltraCompressionLevel, ZSTD_maxCLevel()); + std::fprintf(stderr, "\t-h/--help : display help and exit\n"); + std::fprintf(stderr, "\t-V/--version : display version number and exit\n"); + std::fprintf(stderr, "\t-d/--decompress : decompression\n"); + std::fprintf(stderr, "\t-f/--force : overwrite output\n"); + std::fprintf(stderr, "\t-o/--output file : result stored into `file`\n"); + std::fprintf(stderr, "\t-c/--stdout : write output to standard output\n"); + std::fprintf(stderr, "\t-# : # compression level (1-%d, default:%d)\n", maxNonUltraCompressionLevel, defaultCompressionLevel); +} +} // anonymous namespace + +Options::Options() + : numThreads(0), + maxWindowLog(23), + compressionLevel(defaultCompressionLevel), + decompress(false), + overwrite(false), + pzstdHeaders(false) {} + +bool Options::parse(int argc, const char** argv) { + bool ultra = false; + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + // Arguments with a short option + char option = 0; + if (!std::strcmp(arg, "--num-threads")) { + option = 'n'; + } else if (!std::strcmp(arg, "--pzstd-headers")) { + option = 'p'; + } else if (!std::strcmp(arg, "--ultra")) { + option = 'u'; + } else if (!std::strcmp(arg, "--version")) { + option = 'V'; + } else if (!std::strcmp(arg, "--help")) { + option = 'h'; + } else if (!std::strcmp(arg, "--decompress")) { + option = 'd'; + } else if (!std::strcmp(arg, "--force")) { + option = 'f'; + } else if (!std::strcmp(arg, "--output")) { + option = 'o'; + } else if (!std::strcmp(arg, "--stdout")) { + option = 'c'; + }else if (arg[0] == '-' && arg[1] != 0) { + // Parse the compression level or short option + if (arg[1] >= '0' && arg[1] <= '9') { + compressionLevel = parseUnsigned(arg + 1); + continue; + } + option = arg[1]; + } else if (inputFile.empty()) { + inputFile = arg; + continue; + } else { + std::fprintf(stderr, "Invalid argument: %s.\n", arg); + return false; + } + + switch (option) { + case 'n': + if (++i == argc) { + std::fprintf(stderr, "Invalid argument: -n requires an argument.\n"); + return false; + } + numThreads = parseUnsigned(argv[i]); + if (numThreads == 0) { + std::fprintf(stderr, "Invalid argument: # of threads must be > 0.\n"); + } + break; + case 'p': + pzstdHeaders = true; + break; + case 'u': + ultra = true; + maxWindowLog = 0; + break; + case 'V': + std::fprintf(stderr, "ZSTD version: %s.\n", ZSTD_VERSION_STRING); + return false; + case 'h': + usage(); + return false; + case 'd': + decompress = true; + break; + case 'f': + overwrite = true; + break; + case 'o': + if (++i == argc) { + std::fprintf(stderr, "Invalid argument: -o requires an argument.\n"); + return false; + } + outputFile = argv[i]; + break; + case 'c': + outputFile = '-'; + break; + default: + std::fprintf(stderr, "Invalid argument: %s.\n", arg); + return false; + } + } + // Determine input file if not specified + if (inputFile.empty()) { + inputFile = "-"; + } + // Determine output file if not specified + if (outputFile.empty()) { + if (inputFile == "-") { + std::fprintf( + stderr, + "Invalid arguments: Reading from stdin, but -o not provided.\n"); + return false; + } + // Attempt to add/remove zstd extension from the input file + if (decompress) { + int stemSize = inputFile.size() - zstdExtension.size(); + if (stemSize > 0 && inputFile.substr(stemSize) == zstdExtension) { + outputFile = inputFile.substr(0, stemSize); + } else { + std::fprintf( + stderr, "Invalid argument: Unable to determine output file.\n"); + return false; + } + } else { + outputFile = inputFile + zstdExtension; + } + } + // Check compression level + { + unsigned maxCLevel = ultra ? ZSTD_maxCLevel() : maxNonUltraCompressionLevel; + if (compressionLevel > maxCLevel) { + std::fprintf( + stderr, "Invalid compression level %u.\n", compressionLevel); + } + } + // Check that numThreads is set + if (numThreads == 0) { + std::fprintf(stderr, "Invalid arguments: # of threads not specified.\n"); + return false; + } + return true; +} +} diff --git a/contrib/pzstd/Options.h b/contrib/pzstd/Options.h new file mode 100644 index 000000000..47c5f78a6 --- /dev/null +++ b/contrib/pzstd/Options.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#define ZSTD_STATIC_LINKING_ONLY +#include "zstd.h" +#undef ZSTD_STATIC_LINKING_ONLY + +#include +#include + +namespace pzstd { + +struct Options { + unsigned numThreads; + unsigned maxWindowLog; + unsigned compressionLevel; + bool decompress; + std::string inputFile; + std::string outputFile; + bool overwrite; + bool pzstdHeaders; + + Options(); + Options( + unsigned numThreads, + unsigned maxWindowLog, + unsigned compressionLevel, + bool decompress, + const std::string& inputFile, + const std::string& outputFile, + bool overwrite, + bool pzstdHeaders) + : numThreads(numThreads), + maxWindowLog(maxWindowLog), + compressionLevel(compressionLevel), + decompress(decompress), + inputFile(inputFile), + outputFile(outputFile), + overwrite(overwrite), + pzstdHeaders(pzstdHeaders) {} + + bool parse(int argc, const char** argv); + + ZSTD_parameters determineParameters() const { + ZSTD_parameters params = ZSTD_getParams(compressionLevel, 0, 0); + if (maxWindowLog != 0 && params.cParams.windowLog > maxWindowLog) { + params.cParams.windowLog = maxWindowLog; + params.cParams = ZSTD_adjustCParams(params.cParams, 0, 0); + } + return params; + } +}; +} diff --git a/contrib/pzstd/Pzstd.cpp b/contrib/pzstd/Pzstd.cpp new file mode 100644 index 000000000..84f6a2e4c --- /dev/null +++ b/contrib/pzstd/Pzstd.cpp @@ -0,0 +1,462 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "Pzstd.h" +#include "SkippableFrame.h" +#include "utils/FileSystem.h" +#include "utils/Range.h" +#include "utils/ScopeGuard.h" +#include "utils/ThreadPool.h" +#include "utils/WorkQueue.h" + +#include +#include +#include +#include + +namespace pzstd { + +namespace { +#ifdef _WIN32 +const std::string nullOutput = "nul"; +#else +const std::string nullOutput = "/dev/null"; +#endif +} + +using std::size_t; + +size_t pzstdMain(const Options& options, ErrorHolder& errorHolder) { + // Open the input file and attempt to determine its size + FILE* inputFd = stdin; + size_t inputSize = 0; + if (options.inputFile != "-") { + inputFd = std::fopen(options.inputFile.c_str(), "rb"); + if (!errorHolder.check(inputFd != nullptr, "Failed to open input file")) { + return 0; + } + std::error_code ec; + inputSize = file_size(options.inputFile, ec); + if (ec) { + inputSize = 0; + } + } + auto closeInputGuard = makeScopeGuard([&] { std::fclose(inputFd); }); + + // Check if the output file exists and then open it + FILE* outputFd = stdout; + if (options.outputFile != "-") { + if (!options.overwrite && options.outputFile != nullOutput) { + outputFd = std::fopen(options.outputFile.c_str(), "rb"); + if (!errorHolder.check(outputFd == nullptr, "Output file exists")) { + return 0; + } + } + outputFd = std::fopen(options.outputFile.c_str(), "wb"); + if (!errorHolder.check( + outputFd != nullptr, "Failed to open output file")) { + return 0; + } + } + auto closeOutputGuard = makeScopeGuard([&] { std::fclose(outputFd); }); + + // WorkQueue outlives ThreadPool so in the case of error we are certain + // we don't accidently try to call push() on it after it is destroyed. + WorkQueue> outs; + size_t bytesWritten; + { + // Initialize the thread pool with numThreads + ThreadPool executor(options.numThreads); + if (!options.decompress) { + // Add a job that reads the input and starts all the compression jobs + executor.add( + [&errorHolder, &outs, &executor, inputFd, inputSize, &options] { + asyncCompressChunks( + errorHolder, + outs, + executor, + inputFd, + inputSize, + options.numThreads, + options.determineParameters()); + }); + // Start writing + bytesWritten = + writeFile(errorHolder, outs, outputFd, options.pzstdHeaders); + } else { + // Add a job that reads the input and starts all the decompression jobs + executor.add([&errorHolder, &outs, &executor, inputFd] { + asyncDecompressFrames(errorHolder, outs, executor, inputFd); + }); + // Start writing + bytesWritten = writeFile( + errorHolder, outs, outputFd, /* writeSkippableFrames */ false); + } + } + return bytesWritten; +} + +/// Construct a `ZSTD_inBuffer` that points to the data in `buffer`. +static ZSTD_inBuffer makeZstdInBuffer(const Buffer& buffer) { + return ZSTD_inBuffer{buffer.data(), buffer.size(), 0}; +} + +/** + * Advance `buffer` and `inBuffer` by the amount of data read, as indicated by + * `inBuffer.pos`. + */ +void advance(Buffer& buffer, ZSTD_inBuffer& inBuffer) { + auto pos = inBuffer.pos; + inBuffer.src = static_cast(inBuffer.src) + pos; + inBuffer.size -= pos; + inBuffer.pos = 0; + return buffer.advance(pos); +} + +/// Construct a `ZSTD_outBuffer` that points to the data in `buffer`. +static ZSTD_outBuffer makeZstdOutBuffer(Buffer& buffer) { + return ZSTD_outBuffer{buffer.data(), buffer.size(), 0}; +} + +/** + * Split `buffer` and advance `outBuffer` by the amount of data written, as + * indicated by `outBuffer.pos`. + */ +Buffer split(Buffer& buffer, ZSTD_outBuffer& outBuffer) { + auto pos = outBuffer.pos; + outBuffer.dst = static_cast(outBuffer.dst) + pos; + outBuffer.size -= pos; + outBuffer.pos = 0; + return buffer.splitAt(pos); +} + +/** + * Stream chunks of input from `in`, compress it, and stream it out to `out`. + * + * @param errorHolder Used to report errors and check if an error occured + * @param in Queue that we `pop()` input buffers from + * @param out Queue that we `push()` compressed output buffers to + * @param maxInputSize An upper bound on the size of the input + * @param parameters The zstd parameters to use for compression + */ +static void compress( + ErrorHolder& errorHolder, + std::shared_ptr in, + std::shared_ptr out, + size_t maxInputSize, + ZSTD_parameters parameters) { + auto guard = makeScopeGuard([&] { out->finish(); }); + // Initialize the CCtx + std::unique_ptr ctx( + ZSTD_createCStream(), ZSTD_freeCStream); + if (!errorHolder.check(ctx != nullptr, "Failed to allocate ZSTD_CStream")) { + return; + } + { + auto err = ZSTD_initCStream_advanced(ctx.get(), nullptr, 0, parameters, 0); + if (!errorHolder.check(!ZSTD_isError(err), ZSTD_getErrorName(err))) { + return; + } + } + + // Allocate space for the result + auto outBuffer = Buffer(ZSTD_compressBound(maxInputSize)); + auto zstdOutBuffer = makeZstdOutBuffer(outBuffer); + { + Buffer inBuffer; + // Read a buffer in from the input queue + while (in->pop(inBuffer) && !errorHolder.hasError()) { + auto zstdInBuffer = makeZstdInBuffer(inBuffer); + // Compress the whole buffer and send it to the output queue + while (!inBuffer.empty() && !errorHolder.hasError()) { + if (!errorHolder.check( + !outBuffer.empty(), "ZSTD_compressBound() was too small")) { + return; + } + // Compress + auto err = + ZSTD_compressStream(ctx.get(), &zstdOutBuffer, &zstdInBuffer); + if (!errorHolder.check(!ZSTD_isError(err), ZSTD_getErrorName(err))) { + return; + } + // Split the compressed data off outBuffer and pass to the output queue + out->push(split(outBuffer, zstdOutBuffer)); + // Forget about the data we already compressed + advance(inBuffer, zstdInBuffer); + } + } + } + // Write the epilog + size_t bytesLeft; + do { + if (!errorHolder.check( + !outBuffer.empty(), "ZSTD_compressBound() was too small")) { + return; + } + bytesLeft = ZSTD_endStream(ctx.get(), &zstdOutBuffer); + if (!errorHolder.check( + !ZSTD_isError(bytesLeft), ZSTD_getErrorName(bytesLeft))) { + return; + } + out->push(split(outBuffer, zstdOutBuffer)); + } while (bytesLeft != 0 && !errorHolder.hasError()); +} + +/** + * Calculates how large each independently compressed frame should be. + * + * @param size The size of the source if known, 0 otherwise + * @param numThreads The number of threads available to run compression jobs on + * @param params The zstd parameters to be used for compression + */ +static size_t +calculateStep(size_t size, size_t numThreads, const ZSTD_parameters& params) { + size_t step = 1ul << (params.cParams.windowLog + 2); + // If file size is known, see if a smaller step will spread work more evenly + if (size != 0) { + size_t newStep = size / numThreads; + if (newStep != 0) { + step = std::min(step, newStep); + } + } + return step; +} + +namespace { +enum class FileStatus { Continue, Done, Error }; +} // anonymous namespace + +/** + * Reads `size` data in chunks of `chunkSize` and puts it into `queue`. + * Will read less if an error or EOF occurs. + * Returns the status of the file after all of the reads have occurred. + */ +static FileStatus +readData(BufferWorkQueue& queue, size_t chunkSize, size_t size, FILE* fd) { + Buffer buffer(size); + while (!buffer.empty()) { + auto bytesRead = + std::fread(buffer.data(), 1, std::min(chunkSize, buffer.size()), fd); + queue.push(buffer.splitAt(bytesRead)); + if (std::feof(fd)) { + return FileStatus::Done; + } else if (std::ferror(fd) || bytesRead == 0) { + return FileStatus::Error; + } + } + return FileStatus::Continue; +} + +void asyncCompressChunks( + ErrorHolder& errorHolder, + WorkQueue>& chunks, + ThreadPool& executor, + FILE* fd, + size_t size, + size_t numThreads, + ZSTD_parameters params) { + auto chunksGuard = makeScopeGuard([&] { chunks.finish(); }); + + // Break the input up into chunks of size `step` and compress each chunk + // independently. + size_t step = calculateStep(size, numThreads, params); + auto status = FileStatus::Continue; + while (status == FileStatus::Continue && !errorHolder.hasError()) { + // Make a new input queue that we will put the chunk's input data into. + auto in = std::make_shared(); + auto inGuard = makeScopeGuard([&] { in->finish(); }); + // Make a new output queue that compress will put the compressed data into. + auto out = std::make_shared(); + // Start compression in the thread pool + executor.add([&errorHolder, in, out, step, params] { + return compress( + errorHolder, std::move(in), std::move(out), step, params); + }); + // Pass the output queue to the writer thread. + chunks.push(std::move(out)); + // Fill the input queue for the compression job we just started + status = readData(*in, ZSTD_CStreamInSize(), step, fd); + } + errorHolder.check(status != FileStatus::Error, "Error reading input"); +} + +/** + * Decompress a frame, whose data is streamed into `in`, and stream the output + * to `out`. + * + * @param errorHolder Used to report errors and check if an error occured + * @param in Queue that we `pop()` input buffers from. It contains + * exactly one compressed frame. + * @param out Queue that we `push()` decompressed output buffers to + */ +static void decompress( + ErrorHolder& errorHolder, + std::shared_ptr in, + std::shared_ptr out) { + auto guard = makeScopeGuard([&] { out->finish(); }); + // Initialize the DCtx + std::unique_ptr ctx( + ZSTD_createDStream(), ZSTD_freeDStream); + if (!errorHolder.check(ctx != nullptr, "Failed to allocate ZSTD_DStream")) { + return; + } + { + auto err = ZSTD_initDStream(ctx.get()); + if (!errorHolder.check(!ZSTD_isError(err), ZSTD_getErrorName(err))) { + return; + } + } + + const size_t outSize = ZSTD_DStreamOutSize(); + Buffer inBuffer; + size_t returnCode = 0; + // Read a buffer in from the input queue + while (in->pop(inBuffer) && !errorHolder.hasError()) { + auto zstdInBuffer = makeZstdInBuffer(inBuffer); + // Decompress the whole buffer and send it to the output queue + while (!inBuffer.empty() && !errorHolder.hasError()) { + // Allocate a buffer with at least outSize bytes. + Buffer outBuffer(outSize); + auto zstdOutBuffer = makeZstdOutBuffer(outBuffer); + // Decompress + returnCode = + ZSTD_decompressStream(ctx.get(), &zstdOutBuffer, &zstdInBuffer); + if (!errorHolder.check( + !ZSTD_isError(returnCode), ZSTD_getErrorName(returnCode))) { + return; + } + // Pass the buffer with the decompressed data to the output queue + out->push(split(outBuffer, zstdOutBuffer)); + // Advance past the input we already read + advance(inBuffer, zstdInBuffer); + if (returnCode == 0) { + // The frame is over, prepare to (maybe) start a new frame + ZSTD_initDStream(ctx.get()); + } + } + } + if (!errorHolder.check(returnCode <= 1, "Incomplete block")) { + return; + } + // We've given ZSTD_decompressStream all of our data, but there may still + // be data to read. + while (returnCode == 1) { + // Allocate a buffer with at least outSize bytes. + Buffer outBuffer(outSize); + auto zstdOutBuffer = makeZstdOutBuffer(outBuffer); + // Pass in no input. + ZSTD_inBuffer zstdInBuffer{nullptr, 0, 0}; + // Decompress + returnCode = + ZSTD_decompressStream(ctx.get(), &zstdOutBuffer, &zstdInBuffer); + if (!errorHolder.check( + !ZSTD_isError(returnCode), ZSTD_getErrorName(returnCode))) { + return; + } + // Pass the buffer with the decompressed data to the output queue + out->push(split(outBuffer, zstdOutBuffer)); + } +} + +void asyncDecompressFrames( + ErrorHolder& errorHolder, + WorkQueue>& frames, + ThreadPool& executor, + FILE* fd) { + auto framesGuard = makeScopeGuard([&] { frames.finish(); }); + // Split the source up into its component frames. + // If we find our recognized skippable frame we know the next frames size + // which means that we can decompress each standard frame in independently. + // Otherwise, we will decompress using only one decompression task. + const size_t chunkSize = ZSTD_DStreamInSize(); + auto status = FileStatus::Continue; + while (status == FileStatus::Continue && !errorHolder.hasError()) { + // Make a new input queue that we will put the frames's bytes into. + auto in = std::make_shared(); + auto inGuard = makeScopeGuard([&] { in->finish(); }); + // Make a output queue that decompress will put the decompressed data into + auto out = std::make_shared(); + + size_t frameSize; + { + // Calculate the size of the next frame. + // frameSize is 0 if the frame info can't be decoded. + Buffer buffer(SkippableFrame::kSize); + auto bytesRead = std::fread(buffer.data(), 1, buffer.size(), fd); + if (bytesRead == 0 && status != FileStatus::Continue) { + break; + } + buffer.subtract(buffer.size() - bytesRead); + frameSize = SkippableFrame::tryRead(buffer.range()); + in->push(std::move(buffer)); + } + // Start decompression in the thread pool + executor.add([&errorHolder, in, out] { + return decompress(errorHolder, std::move(in), std::move(out)); + }); + // Pass the output queue to the writer thread + frames.push(std::move(out)); + if (frameSize == 0) { + // We hit a non SkippableFrame ==> not compressed by pzstd or corrupted + // Pass the rest of the source to this decompression task + while (status == FileStatus::Continue && !errorHolder.hasError()) { + status = readData(*in, chunkSize, chunkSize, fd); + } + break; + } + // Fill the input queue for the decompression job we just started + status = readData(*in, chunkSize, frameSize, fd); + } + errorHolder.check(status != FileStatus::Error, "Error reading input"); +} + +/// Write `data` to `fd`, returns true iff success. +static bool writeData(ByteRange data, FILE* fd) { + while (!data.empty()) { + data.advance(std::fwrite(data.begin(), 1, data.size(), fd)); + if (std::ferror(fd)) { + return false; + } + } + return true; +} + +size_t writeFile( + ErrorHolder& errorHolder, + WorkQueue>& outs, + FILE* outputFd, + bool writeSkippableFrames) { + size_t bytesWritten = 0; + std::shared_ptr out; + // Grab the output queue for each decompression job (in order). + while (outs.pop(out) && !errorHolder.hasError()) { + if (writeSkippableFrames) { + // If we are compressing and want to write skippable frames we can't + // start writing before compression is done because we need to know the + // compressed size. + // Wait for the compressed size to be available and write skippable frame + SkippableFrame frame(out->size()); + if (!writeData(frame.data(), outputFd)) { + errorHolder.setError("Failed to write output"); + return bytesWritten; + } + bytesWritten += frame.kSize; + } + // For each chunk of the frame: Pop it from the queue and write it + Buffer buffer; + while (out->pop(buffer) && !errorHolder.hasError()) { + if (!writeData(buffer.range(), outputFd)) { + errorHolder.setError("Failed to write output"); + return bytesWritten; + } + bytesWritten += buffer.size(); + } + } + return bytesWritten; +} +} diff --git a/contrib/pzstd/Pzstd.h b/contrib/pzstd/Pzstd.h new file mode 100644 index 000000000..617aecb3f --- /dev/null +++ b/contrib/pzstd/Pzstd.h @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#include "ErrorHolder.h" +#include "Options.h" +#include "utils/Buffer.h" +#include "utils/Range.h" +#include "utils/ThreadPool.h" +#include "utils/WorkQueue.h" +#define ZSTD_STATIC_LINKING_ONLY +#include "zstd.h" +#undef ZSTD_STATIC_LINKING_ONLY + +#include +#include + +namespace pzstd { +/** + * Runs pzstd with `options` and returns the number of bytes written. + * An error occurred if `errorHandler.hasError()`. + * + * @param options The pzstd options to use for (de)compression + * @param errorHolder Used to report errors and coordinate early shutdown + * if an error occured + * @returns The number of bytes written. + */ +std::size_t pzstdMain(const Options& options, ErrorHolder& errorHolder); + +/** + * Streams input from `fd`, breaks input up into chunks, and compresses each + * chunk independently. Output of each chunk gets streamed to a queue, and + * the output queues get put into `chunks` in order. + * + * @param errorHolder Used to report errors and coordinate early shutdown + * @param chunks Each compression jobs output queue gets `pushed()` here + * as soon as it is available + * @param executor The thread pool to run compression jobs in + * @param fd The input file descriptor + * @param size The size of the input file if known, 0 otherwise + * @param numThreads The number of threads in the thread pool + * @param parameters The zstd parameters to use for compression + */ +void asyncCompressChunks( + ErrorHolder& errorHolder, + WorkQueue>& chunks, + ThreadPool& executor, + FILE* fd, + std::size_t size, + std::size_t numThreads, + ZSTD_parameters parameters); + +/** + * Streams input from `fd`. If pzstd headers are available it breaks the input + * up into independent frames. It sends each frame to an independent + * decompression job. Output of each frame gets streamed to a queue, and + * the output queues get put into `frames` in order. + * + * @param errorHolder Used to report errors and coordinate early shutdown + * @param frames Each decompression jobs output queue gets `pushed()` here + * as soon as it is available + * @param executor The thread pool to run compression jobs in + * @param fd The input file descriptor + */ +void asyncDecompressFrames( + ErrorHolder& errorHolder, + WorkQueue>& frames, + ThreadPool& executor, + FILE* fd); + +/** + * Streams input in from each queue in `outs` in order, and writes the data to + * `outputFd`. + * + * @param errorHolder Used to report errors and coordinate early exit + * @param outs A queue of output queues, one for each + * (de)compression job. + * @param outputFd The file descriptor to write to + * @param writeSkippableFrames Should we write pzstd headers? + * @returns The number of bytes written + */ +std::size_t writeFile( + ErrorHolder& errorHolder, + WorkQueue>& outs, + FILE* outputFd, + bool writeSkippableFrames); +} diff --git a/contrib/pzstd/README.md b/contrib/pzstd/README.md new file mode 100644 index 000000000..1a5a0105d --- /dev/null +++ b/contrib/pzstd/README.md @@ -0,0 +1,47 @@ +# Parallel Zstandard (PZstandard) + +Parallel Zstandard provides Zstandard format compatible compression and decompression that is able to utilize multiple cores. +It breaks the input up into equal sized chunks and compresses each chunk independently into a Zstandard frame. +It then concatenates the frames together to produce the final compressed output. +Optionally, with the `-p` option, PZstandard will write a 12 byte header for each frame that is a skippable frame in the Zstandard format, which tells PZstandard the size of the next compressed frame. +When `-p` is specified for compression, PZstandard can decompress the output in parallel. + +## Usage + +Basic usage + + pzstd input-file -o output-file -n num-threads [ -p ] -# # Compression + pzstd -d input-file -o output-file -n num-threads # Decompression + +PZstandard also supports piping and fifo pipes + + cat input-file | pzstd -n num-threads [ -p ] -# -c > /dev/null + +For more options + + pzstd --help + +## Benchmarks + +As a reference, PZstandard and Pigz were compared on an Intel Core i7 @ 3.1 GHz, each using 4 threads, with the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia). + +Compression Speed vs Ratio with 4 Threads | Decompression Speed with 4 Threads +------------------------------------------|----------------------------------- +![Compression Speed vs Ratio](images/Cspeed.png "Compression Speed vs Ratio") | ![Decompression Speed](images/Dspeed.png "Decompression Speed") + +The test procedure was to run each of the following commands 2 times for each compression level, and take the minimum time. + + time ./pzstd -# -n 4 -p -c silesia.tar > silesia.tar.zst + time ./pzstd -d -n 4 -c silesia.tar.zst > /dev/null + + time pigz -# -p 4 -k -c silesia.tar > silesia.tar.gz + time pigz -d -p 4 -k -c silesia.tar.gz > /dev/null + +PZstandard was tested using compression levels 1-19, and Pigz was tested using compression levels 1-9. +Pigz cannot do parallel decompression, it simply does each of reading, decompression, and writing on separate threads. + +## Tests + +Tests require that you have [gtest](https://github.com/google/googletest) installed. +Modify `GTEST_INC` and `GTEST_LIB` in `test/Makefile` and `utils/test/Makefile` to work for your install of gtest. +Then run `make test` in the `contrib/pzstd` directory. diff --git a/contrib/pzstd/SkippableFrame.cpp b/contrib/pzstd/SkippableFrame.cpp new file mode 100644 index 000000000..20ad4cc8e --- /dev/null +++ b/contrib/pzstd/SkippableFrame.cpp @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "SkippableFrame.h" +#include "common/mem.h" +#include "utils/Range.h" + +#include + +using namespace pzstd; + +SkippableFrame::SkippableFrame(std::uint32_t size) : frameSize_(size) { + MEM_writeLE32(data_.data(), kSkippableFrameMagicNumber); + MEM_writeLE32(data_.data() + 4, kFrameContentsSize); + MEM_writeLE32(data_.data() + 8, frameSize_); +} + +/* static */ std::size_t SkippableFrame::tryRead(ByteRange bytes) { + if (bytes.size() < SkippableFrame::kSize || + MEM_readLE32(bytes.begin()) != kSkippableFrameMagicNumber || + MEM_readLE32(bytes.begin() + 4) != kFrameContentsSize) { + return 0; + } + return MEM_readLE32(bytes.begin() + 8); +} diff --git a/contrib/pzstd/SkippableFrame.h b/contrib/pzstd/SkippableFrame.h new file mode 100644 index 000000000..9dc95c1f5 --- /dev/null +++ b/contrib/pzstd/SkippableFrame.h @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#include "utils/Range.h" + +#include +#include +#include +#include + +namespace pzstd { +/** + * We put a skippable frame before each frame. + * It contains a skippable frame magic number, the size of the skippable frame, + * and the size of the next frame. + * Each skippable frame is exactly 12 bytes in little endian format. + * The first 8 bytes are for compatibility with the ZSTD format. + * If we have N threads, the output will look like + * + * [0x184D2A50|4|size1] [frame1 of size size1] + * [0x184D2A50|4|size2] [frame2 of size size2] + * ... + * [0x184D2A50|4|sizeN] [frameN of size sizeN] + * + * Each sizeX is 4 bytes. + * + * These skippable frames should allow us to skip through the compressed file + * and only load at most N pages. + */ +class SkippableFrame { + public: + static constexpr std::size_t kSize = 12; + + private: + std::uint32_t frameSize_; + std::array data_; + static constexpr std::uint32_t kSkippableFrameMagicNumber = 0x184D2A50; + // Could be improved if the size fits in less bytes + static constexpr std::uint32_t kFrameContentsSize = kSize - 8; + + public: + // Write the skippable frame to data_ in LE format. + explicit SkippableFrame(std::uint32_t size); + + // Read the skippable frame from bytes in LE format. + static std::size_t tryRead(ByteRange bytes); + + ByteRange data() const { + return {data_.data(), data_.size()}; + } + + // Size of the next frame. + std::size_t frameSize() const { + return frameSize_; + } +}; +} diff --git a/contrib/pzstd/bench.cpp b/contrib/pzstd/bench.cpp new file mode 100644 index 000000000..56bad3915 --- /dev/null +++ b/contrib/pzstd/bench.cpp @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "ErrorHolder.h" +#include "Options.h" +#include "Pzstd.h" +#include "utils/FileSystem.h" +#include "utils/Range.h" +#include "utils/ScopeGuard.h" +#include "utils/ThreadPool.h" +#include "utils/WorkQueue.h" + +#include +#include +#include + +using namespace pzstd; + +namespace { +// Prints how many ns it was in scope for upon destruction +// Used for rough estimates of how long things took +struct BenchmarkTimer { + using Clock = std::chrono::system_clock; + Clock::time_point start; + FILE* fd; + + explicit BenchmarkTimer(FILE* fd = stdout) : fd(fd) { + start = Clock::now(); + } + + ~BenchmarkTimer() { + auto end = Clock::now(); + size_t ticks = + std::chrono::duration_cast(end - start) + .count(); + ticks = std::max(ticks, size_t{1}); + for (auto tmp = ticks; tmp < 100000; tmp *= 10) { + std::fprintf(fd, " "); + } + std::fprintf(fd, "%zu | ", ticks); + } +}; +} + +// Code I used for benchmarking + +void testMain(const Options& options) { + if (!options.decompress) { + if (options.compressionLevel < 10) { + std::printf("0"); + } + std::printf("%u | ", options.compressionLevel); + } else { + std::printf(" d | "); + } + if (options.numThreads < 10) { + std::printf("0"); + } + std::printf("%u | ", options.numThreads); + + FILE* inputFd = std::fopen(options.inputFile.c_str(), "rb"); + if (inputFd == nullptr) { + std::abort(); + } + size_t inputSize = 0; + if (inputFd != stdin) { + std::error_code ec; + inputSize = file_size(options.inputFile, ec); + if (ec) { + inputSize = 0; + } + } + FILE* outputFd = std::fopen(options.outputFile.c_str(), "wb"); + if (outputFd == nullptr) { + std::abort(); + } + auto guard = makeScopeGuard([&] { + std::fclose(inputFd); + std::fclose(outputFd); + }); + + WorkQueue> outs; + ErrorHolder errorHolder; + size_t bytesWritten; + { + ThreadPool executor(options.numThreads); + BenchmarkTimer timeIncludingClose; + if (!options.decompress) { + executor.add( + [&errorHolder, &outs, &executor, inputFd, inputSize, &options] { + asyncCompressChunks( + errorHolder, + outs, + executor, + inputFd, + inputSize, + options.numThreads, + options.determineParameters()); + }); + bytesWritten = writeFile(errorHolder, outs, outputFd, true); + } else { + executor.add([&errorHolder, &outs, &executor, inputFd] { + asyncDecompressFrames(errorHolder, outs, executor, inputFd); + }); + bytesWritten = writeFile( + errorHolder, outs, outputFd, /* writeSkippableFrames */ false); + } + } + if (errorHolder.hasError()) { + std::fprintf(stderr, "Error: %s.\n", errorHolder.getError().c_str()); + std::abort(); + } + std::printf("%zu\n", bytesWritten); +} + +int main(int argc, const char** argv) { + if (argc < 3) { + return 1; + } + Options options(0, 23, 0, false, "", "", true, true); + // Benchmarking code + for (size_t i = 0; i < 2; ++i) { + for (size_t compressionLevel = 1; compressionLevel <= 16; + compressionLevel <<= 1) { + for (size_t numThreads = 1; numThreads <= 16; numThreads <<= 1) { + options.numThreads = numThreads; + options.compressionLevel = compressionLevel; + options.decompress = false; + options.inputFile = argv[1]; + options.outputFile = argv[2]; + testMain(options); + options.decompress = true; + options.inputFile = argv[2]; + options.outputFile = std::string(argv[1]) + ".d"; + testMain(options); + std::fflush(stdout); + } + } + } + return 0; +} diff --git a/contrib/pzstd/images/Cspeed.png b/contrib/pzstd/images/Cspeed.png new file mode 100644 index 0000000000000000000000000000000000000000..516d09807b304fa64bf563eb0468fc3b2a1a59c2 GIT binary patch literal 58612 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 literal 0 HcmV?d00001 diff --git a/contrib/pzstd/images/Dspeed.png b/contrib/pzstd/images/Dspeed.png new file mode 100644 index 0000000000000000000000000000000000000000..e48881bcd05b70a41121ae03a09621a7e6abb7d2 GIT binary patch literal 26335 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 L^>bP0l+XkKsbHG{ literal 0 HcmV?d00001 diff --git a/contrib/pzstd/main.cpp b/contrib/pzstd/main.cpp new file mode 100644 index 000000000..7ff2cef74 --- /dev/null +++ b/contrib/pzstd/main.cpp @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "ErrorHolder.h" +#include "Options.h" +#include "Pzstd.h" +#include "utils/FileSystem.h" +#include "utils/Range.h" +#include "utils/ScopeGuard.h" +#include "utils/ThreadPool.h" +#include "utils/WorkQueue.h" + +using namespace pzstd; + +int main(int argc, const char** argv) { + Options options; + if (!options.parse(argc, argv)) { + return 1; + } + + ErrorHolder errorHolder; + pzstdMain(options, errorHolder); + + if (errorHolder.hasError()) { + std::fprintf(stderr, "Error: %s.\n", errorHolder.getError().c_str()); + return 1; + } + return 0; +} diff --git a/contrib/pzstd/test/Makefile b/contrib/pzstd/test/Makefile new file mode 100644 index 000000000..3b0ffec89 --- /dev/null +++ b/contrib/pzstd/test/Makefile @@ -0,0 +1,46 @@ +# ########################################################################## +# Copyright (c) 2016-present, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. +# ########################################################################## + +# Set GTEST_INC and GTEST_LIB to work with your install of gtest +GTEST_INC ?= -isystem googletest/googletest/include +GTEST_LIB ?= -L googletest/build/googlemock/gtest + +# Define *.exe as extension for Windows systems +ifneq (,$(filter Windows%,$(OS))) +EXT =.exe +else +EXT = +endif + +PZSTDDIR = .. +PROGDIR = ../../../programs +ZSTDDIR = ../../../lib + +CPPFLAGS = -I$(PZSTDDIR) $(GTEST_INC) $(GTEST_LIB) -I$(ZSTDDIR)/common -I$(PROGDIR) + +CFLAGS ?= -O3 +CFLAGS += -std=c++11 +CFLAGS += $(MOREFLAGS) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) + +datagen.o: $(PROGDIR)/datagen.* + $(CXX) $(FLAGS) $(PROGDIR)/datagen.c -c -o $@ + +%: %.cpp *.h datagen.o + $(CXX) $(FLAGS) -lgtest -lgtest_main $@.cpp datagen.o $(PZSTDDIR)/libzstd.a $(PZSTDDIR)/Pzstd.o $(PZSTDDIR)/SkippableFrame.o $(PZSTDDIR)/Options.o -o $@$(EXT) + +.PHONY: test clean + +test: OptionsTest PzstdTest RoundTripTest + @./OptionsTest$(EXT) + @./PzstdTest$(EXT) + @./RoundTripTest$(EXT) + +clean: + @rm -f datagen.o OptionsTest PzstdTest RoundTripTest diff --git a/contrib/pzstd/test/OptionsTest.cpp b/contrib/pzstd/test/OptionsTest.cpp new file mode 100644 index 000000000..1479d6cd2 --- /dev/null +++ b/contrib/pzstd/test/OptionsTest.cpp @@ -0,0 +1,179 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "Options.h" + +#include +#include + +using namespace pzstd; + +namespace pzstd { +bool operator==(const Options& lhs, const Options& rhs) { + return lhs.numThreads == rhs.numThreads && + lhs.maxWindowLog == rhs.maxWindowLog && + lhs.compressionLevel == rhs.compressionLevel && + lhs.decompress == rhs.decompress && lhs.inputFile == rhs.inputFile && + lhs.outputFile == rhs.outputFile && lhs.overwrite == rhs.overwrite && + lhs.pzstdHeaders == rhs.pzstdHeaders; +} +} + +TEST(Options, ValidInputs) { + { + Options options; + std::array args = { + {nullptr, "--num-threads", "5", "-o", "-", "-f"}}; + EXPECT_TRUE(options.parse(args.size(), args.data())); + Options expected = {5, 23, 3, false, "-", "-", true, false}; + EXPECT_EQ(expected, options); + } + { + Options options; + std::array args = { + {nullptr, "-n", "1", "input", "-19", "-p"}}; + EXPECT_TRUE(options.parse(args.size(), args.data())); + Options expected = {1, 23, 19, false, "input", "input.zst", false, true}; + EXPECT_EQ(expected, options); + } + { + Options options; + std::array args = {{nullptr, + "--ultra", + "-22", + "-n", + "1", + "--output", + "x", + "-d", + "x.zst", + "-f"}}; + EXPECT_TRUE(options.parse(args.size(), args.data())); + Options expected = {1, 0, 22, true, "x.zst", "x", true, false}; + EXPECT_EQ(expected, options); + } + { + Options options; + std::array args = {{nullptr, + "--num-threads", + "100", + "hello.zst", + "--decompress", + "--force"}}; + EXPECT_TRUE(options.parse(args.size(), args.data())); + Options expected = {100, 23, 3, true, "hello.zst", "hello", true, false}; + EXPECT_EQ(expected, options); + } + { + Options options; + std::array args = {{nullptr, "-", "-n", "1", "-c"}}; + EXPECT_TRUE(options.parse(args.size(), args.data())); + Options expected = {1, 23, 3, false, "-", "-", false, false}; + EXPECT_EQ(expected, options); + } + { + Options options; + std::array args = {{nullptr, "-", "-n", "1", "--stdout"}}; + EXPECT_TRUE(options.parse(args.size(), args.data())); + Options expected = {1, 23, 3, false, "-", "-", false, false}; + EXPECT_EQ(expected, options); + } + { + Options options; + std::array args = {{nullptr, + "-n", + "1", + "-", + "-5", + "-o", + "-", + "-u", + "-d", + "--pzstd-headers"}}; + EXPECT_TRUE(options.parse(args.size(), args.data())); + Options expected = {1, 0, 5, true, "-", "-", false, true}; + } + { + Options options; + std::array args = { + {nullptr, "silesia.tar", "-o", "silesia.tar.pzstd", "-n", "2"}}; + EXPECT_TRUE(options.parse(args.size(), args.data())); + Options expected = { + 2, 23, 3, false, "silesia.tar", "silesia.tar.pzstd", false, false}; + } +} + +TEST(Options, BadNumThreads) { + { + Options options; + std::array args = {{nullptr, "-o", "-"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } + { + Options options; + std::array args = {{nullptr, "-n", "0", "-o", "-"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } + { + Options options; + std::array args = {{nullptr, "-n", "-o", "-"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } +} + +TEST(Options, BadCompressionLevel) { + { + Options options; + std::array args = {{nullptr, "x", "-20"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } + { + Options options; + std::array args = {{nullptr, "x", "-u", "-23"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } +} + +TEST(Options, InvalidOption) { + { + Options options; + std::array args = {{nullptr, "x", "-x"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } +} + +TEST(Options, BadOutputFile) { + { + Options options; + std::array args = {{nullptr, "notzst", "-d", "-n", "1"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } + { + Options options; + std::array args = {{nullptr, "-n", "1"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } + { + Options options; + std::array args = {{nullptr, "-", "-n", "1"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } +} + +TEST(Options, Extras) { + { + Options options; + std::array args = {{nullptr, "-h"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } + { + Options options; + std::array args = {{nullptr, "-V"}}; + EXPECT_FALSE(options.parse(args.size(), args.data())); + } +} diff --git a/contrib/pzstd/test/PzstdTest.cpp b/contrib/pzstd/test/PzstdTest.cpp new file mode 100644 index 000000000..a6eb74596 --- /dev/null +++ b/contrib/pzstd/test/PzstdTest.cpp @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "datagen.h" +#include "Pzstd.h" +#include "test/RoundTrip.h" +#include "utils/ScopeGuard.h" + +#include +#include +#include +#include + +using namespace std; +using namespace pzstd; + +TEST(Pzstd, SmallSizes) { + for (unsigned len = 1; len < 1028; ++len) { + std::string inputFile = std::tmpnam(nullptr); + auto guard = makeScopeGuard([&] { std::remove(inputFile.c_str()); }); + { + static uint8_t buf[1028]; + RDG_genBuffer(buf, len, 0.5, 0.0, 42); + auto fd = std::fopen(inputFile.c_str(), "wb"); + auto written = std::fwrite(buf, 1, len, fd); + std::fclose(fd); + ASSERT_EQ(written, len); + } + for (unsigned headers = 0; headers <= 1; ++headers) { + for (unsigned numThreads = 1; numThreads <= 4; numThreads *= 2) { + for (unsigned level = 1; level <= 8; level *= 8) { + auto errorGuard = makeScopeGuard([&] { + guard.dismiss(); + std::fprintf(stderr, "file: %s\n", inputFile.c_str()); + std::fprintf(stderr, "pzstd headers: %u\n", headers); + std::fprintf(stderr, "# threads: %u\n", numThreads); + std::fprintf(stderr, "compression level: %u\n", level); + }); + Options options; + options.pzstdHeaders = headers; + options.overwrite = true; + options.inputFile = inputFile; + options.numThreads = numThreads; + options.compressionLevel = level; + ASSERT_TRUE(roundTrip(options)); + errorGuard.dismiss(); + } + } + } + } +} + +TEST(Pzstd, LargeSizes) { + for (unsigned len = 1 << 20; len <= (1 << 24); len *= 2) { + std::string inputFile = std::tmpnam(nullptr); + auto guard = makeScopeGuard([&] { std::remove(inputFile.c_str()); }); + { + std::unique_ptr buf(new uint8_t[len]); + RDG_genBuffer(buf.get(), len, 0.5, 0.0, 42); + auto fd = std::fopen(inputFile.c_str(), "wb"); + auto written = std::fwrite(buf.get(), 1, len, fd); + std::fclose(fd); + ASSERT_EQ(written, len); + } + for (unsigned headers = 0; headers <= 1; ++headers) { + for (unsigned numThreads = 1; numThreads <= 16; numThreads *= 4) { + for (unsigned level = 1; level <= 4; level *= 2) { + auto errorGuard = makeScopeGuard([&] { + guard.dismiss(); + std::fprintf(stderr, "file: %s\n", inputFile.c_str()); + std::fprintf(stderr, "pzstd headers: %u\n", headers); + std::fprintf(stderr, "# threads: %u\n", numThreads); + std::fprintf(stderr, "compression level: %u\n", level); + }); + Options options; + options.pzstdHeaders = headers; + options.overwrite = true; + options.inputFile = inputFile; + options.numThreads = numThreads; + options.compressionLevel = level; + ASSERT_TRUE(roundTrip(options)); + errorGuard.dismiss(); + } + } + } + } +} + +TEST(Pzstd, ExtremelyCompressible) { + std::string inputFile = std::tmpnam(nullptr); + auto guard = makeScopeGuard([&] { std::remove(inputFile.c_str()); }); + { + std::unique_ptr buf(new uint8_t[10000]); + std::memset(buf.get(), 'a', 10000); + auto fd = std::fopen(inputFile.c_str(), "wb"); + auto written = std::fwrite(buf.get(), 1, 10000, fd); + std::fclose(fd); + ASSERT_EQ(written, 10000); + } + Options options; + options.pzstdHeaders = false; + options.overwrite = true; + options.inputFile = inputFile; + options.numThreads = 1; + options.compressionLevel = 1; + ASSERT_TRUE(roundTrip(options)); +} diff --git a/contrib/pzstd/test/RoundTrip.h b/contrib/pzstd/test/RoundTrip.h new file mode 100644 index 000000000..829c95cac --- /dev/null +++ b/contrib/pzstd/test/RoundTrip.h @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#include "Options.h" +#include "Pzstd.h" +#include "utils/ScopeGuard.h" + +#include +#include +#include +#include + +namespace pzstd { + +inline bool check(std::string source, std::string decompressed) { + std::unique_ptr sBuf(new std::uint8_t[1024]); + std::unique_ptr dBuf(new std::uint8_t[1024]); + + auto sFd = std::fopen(source.c_str(), "rb"); + auto dFd = std::fopen(decompressed.c_str(), "rb"); + auto guard = makeScopeGuard([&] { + std::fclose(sFd); + std::fclose(dFd); + }); + + size_t sRead, dRead; + + do { + sRead = std::fread(sBuf.get(), 1, 1024, sFd); + dRead = std::fread(dBuf.get(), 1, 1024, dFd); + if (std::ferror(sFd) || std::ferror(dFd)) { + return false; + } + if (sRead != dRead) { + return false; + } + + for (size_t i = 0; i < sRead; ++i) { + if (sBuf.get()[i] != dBuf.get()[i]) { + return false; + } + } + } while (sRead == 1024); + if (!std::feof(sFd) || !std::feof(dFd)) { + return false; + } + return true; +} + +inline bool roundTrip(Options& options) { + std::string source = options.inputFile; + std::string compressedFile = std::tmpnam(nullptr); + std::string decompressedFile = std::tmpnam(nullptr); + auto guard = makeScopeGuard([&] { + std::remove(compressedFile.c_str()); + std::remove(decompressedFile.c_str()); + }); + + { + options.outputFile = compressedFile; + options.decompress = false; + ErrorHolder errorHolder; + pzstdMain(options, errorHolder); + if (errorHolder.hasError()) { + errorHolder.getError(); + return false; + } + } + { + options.decompress = true; + options.inputFile = compressedFile; + options.outputFile = decompressedFile; + ErrorHolder errorHolder; + pzstdMain(options, errorHolder); + if (errorHolder.hasError()) { + errorHolder.getError(); + return false; + } + } + return check(source, decompressedFile); +} +} diff --git a/contrib/pzstd/test/RoundTripTest.cpp b/contrib/pzstd/test/RoundTripTest.cpp new file mode 100644 index 000000000..01c1c8113 --- /dev/null +++ b/contrib/pzstd/test/RoundTripTest.cpp @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "datagen.h" +#include "Options.h" +#include "test/RoundTrip.h" +#include "utils/ScopeGuard.h" + +#include +#include +#include +#include +#include + +using namespace std; +using namespace pzstd; + +namespace { +string +writeData(size_t size, double matchProba, double litProba, unsigned seed) { + std::unique_ptr buf(new uint8_t[size]); + RDG_genBuffer(buf.get(), size, matchProba, litProba, seed); + string file = tmpnam(nullptr); + auto fd = std::fopen(file.c_str(), "wb"); + auto guard = makeScopeGuard([&] { std::fclose(fd); }); + auto bytesWritten = std::fwrite(buf.get(), 1, size, fd); + if (bytesWritten != size) { + std::abort(); + } + return file; +} + +template +string generateInputFile(Generator& gen) { + // Use inputs ranging from 1 Byte to 2^16 Bytes + std::uniform_int_distribution size{1, 1 << 16}; + std::uniform_real_distribution<> prob{0, 1}; + return writeData(size(gen), prob(gen), prob(gen), gen()); +} + +template +Options generateOptions(Generator& gen, const string& inputFile) { + Options options; + options.inputFile = inputFile; + options.overwrite = true; + + std::bernoulli_distribution pzstdHeaders{0.75}; + std::uniform_int_distribution numThreads{1, 32}; + std::uniform_int_distribution compressionLevel{1, 10}; + + options.pzstdHeaders = pzstdHeaders(gen); + options.numThreads = numThreads(gen); + options.compressionLevel = compressionLevel(gen); + + return options; +} +} + +int main(int argc, char** argv) { + std::mt19937 gen(std::random_device{}()); + + auto newlineGuard = makeScopeGuard([] { std::fprintf(stderr, "\n"); }); + for (unsigned i = 0; i < 10000; ++i) { + if (i % 100 == 0) { + std::fprintf(stderr, "Progress: %u%%\r", i / 100); + } + auto inputFile = generateInputFile(gen); + auto inputGuard = makeScopeGuard([&] { std::remove(inputFile.c_str()); }); + for (unsigned i = 0; i < 10; ++i) { + auto options = generateOptions(gen, inputFile); + if (!roundTrip(options)) { + std::fprintf(stderr, "numThreads: %u\n", options.numThreads); + std::fprintf(stderr, "level: %u\n", options.compressionLevel); + std::fprintf(stderr, "decompress? %u\n", (unsigned)options.decompress); + std::fprintf( + stderr, "pzstd headers? %u\n", (unsigned)options.pzstdHeaders); + std::fprintf(stderr, "file: %s\n", inputFile.c_str()); + return 1; + } + } + } + return 0; +} diff --git a/contrib/pzstd/utils/Buffer.h b/contrib/pzstd/utils/Buffer.h new file mode 100644 index 000000000..ab25bac9c --- /dev/null +++ b/contrib/pzstd/utils/Buffer.h @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#include "utils/Range.h" + +#include +#include +#include + +namespace pzstd { + +/** + * A `Buffer` has a pointer to a shared buffer, and a range of the buffer that + * it owns. + * The idea is that you can allocate one buffer, and write chunks into it + * and break off those chunks. + * The underlying buffer is reference counted, and will be destroyed when all + * `Buffer`s that reference it are destroyed. + */ +class Buffer { + std::shared_ptr buffer_; + MutableByteRange range_; + + static void delete_buffer(unsigned char* buffer) { + delete[] buffer; + } + + public: + /// Construct an empty buffer that owns no data. + explicit Buffer() {} + + /// Construct a `Buffer` that owns a new underlying buffer of size `size`. + explicit Buffer(std::size_t size) + : buffer_(new unsigned char[size], delete_buffer), + range_(buffer_.get(), buffer_.get() + size) {} + + explicit Buffer(std::shared_ptr buffer, MutableByteRange data) + : buffer_(buffer), range_(data) {} + + Buffer(Buffer&&) = default; + Buffer& operator=(Buffer&&) & = default; + + /** + * Splits the data into two pieces: [begin, begin + n), [begin + n, end). + * Their data both points into the same underlying buffer. + * Modifies the original `Buffer` to point to only [begin + n, end). + * + * @param n The offset to split at. + * @returns A buffer that owns the data [begin, begin + n). + */ + Buffer splitAt(std::size_t n) { + auto firstPiece = range_.subpiece(0, n); + range_.advance(n); + return Buffer(buffer_, firstPiece); + } + + /// Modifies the buffer to point to the range [begin + n, end). + void advance(std::size_t n) { + range_.advance(n); + } + + /// Modifies the buffer to point to the range [begin, end - n). + void subtract(std::size_t n) { + range_.subtract(n); + } + + /// Returns a read only `Range` pointing to the `Buffer`s data. + ByteRange range() const { + return range_; + } + /// Returns a mutable `Range` pointing to the `Buffer`s data. + MutableByteRange range() { + return range_; + } + + const unsigned char* data() const { + return range_.data(); + } + + unsigned char* data() { + return range_.data(); + } + + std::size_t size() const { + return range_.size(); + } + + bool empty() const { + return range_.empty(); + } +}; +} diff --git a/contrib/pzstd/utils/FileSystem.h b/contrib/pzstd/utils/FileSystem.h new file mode 100644 index 000000000..deae0b5b7 --- /dev/null +++ b/contrib/pzstd/utils/FileSystem.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#include "utils/Range.h" + +#include +#include +#include + +// A small subset of `std::filesystem`. +// `std::filesystem` should be a drop in replacement. +// See http://en.cppreference.com/w/cpp/filesystem for documentation. + +namespace pzstd { + +using file_status = struct stat; + +/// http://en.cppreference.com/w/cpp/filesystem/status +inline file_status status(StringPiece path, std::error_code& ec) noexcept { + file_status status; + if (stat(path.data(), &status)) { + ec.assign(errno, std::generic_category()); + } else { + ec.clear(); + } + return status; +} + +/// http://en.cppreference.com/w/cpp/filesystem/is_regular_file +inline bool is_regular_file(file_status status) noexcept { + return S_ISREG(status.st_mode); +} + +/// http://en.cppreference.com/w/cpp/filesystem/is_regular_file +inline bool is_regular_file(StringPiece path, std::error_code& ec) noexcept { + return is_regular_file(status(path, ec)); +} + +/// http://en.cppreference.com/w/cpp/filesystem/file_size +inline std::uintmax_t file_size( + StringPiece path, + std::error_code& ec) noexcept { + auto stat = status(path, ec); + if (ec) { + return -1; + } + if (!is_regular_file(stat)) { + ec.assign(ENOTSUP, std::generic_category()); + return -1; + } + ec.clear(); + return stat.st_size; +} +} diff --git a/contrib/pzstd/utils/Likely.h b/contrib/pzstd/utils/Likely.h new file mode 100644 index 000000000..c8ea102b1 --- /dev/null +++ b/contrib/pzstd/utils/Likely.h @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/** + * Compiler hints to indicate the fast path of an "if" branch: whether + * the if condition is likely to be true or false. + * + * @author Tudor Bosman (tudorb@fb.com) + */ + +#pragma once + +#undef LIKELY +#undef UNLIKELY + +#if defined(__GNUC__) && __GNUC__ >= 4 +#define LIKELY(x) (__builtin_expect((x), 1)) +#define UNLIKELY(x) (__builtin_expect((x), 0)) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif diff --git a/contrib/pzstd/utils/Range.h b/contrib/pzstd/utils/Range.h new file mode 100644 index 000000000..3df15976d --- /dev/null +++ b/contrib/pzstd/utils/Range.h @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/** + * A subset of `folly/Range.h`. + * All code copied verbatiam modulo formatting + */ +#pragma once + +#include "utils/Likely.h" + +#include +#include +#include +#include + +namespace pzstd { + +namespace detail { +/* + *Use IsCharPointer::type to enable const char* or char*. + *Use IsCharPointer::const_type to enable only const char*. +*/ +template +struct IsCharPointer {}; + +template <> +struct IsCharPointer { + typedef int type; +}; + +template <> +struct IsCharPointer { + typedef int const_type; + typedef int type; +}; + +} // namespace detail + +template +class Range { + Iter b_; + Iter e_; + + public: + using size_type = std::size_t; + using iterator = Iter; + using const_iterator = Iter; + using value_type = typename std::remove_reference< + typename std::iterator_traits::reference>::type; + using reference = typename std::iterator_traits::reference; + + constexpr Range() : b_(), e_() {} + constexpr Range(Iter begin, Iter end) : b_(begin), e_(end) {} + + constexpr Range(Iter begin, size_type size) : b_(begin), e_(begin + size) {} + + template ::type = 0> + /* implicit */ Range(Iter str) : b_(str), e_(str + std::strlen(str)) {} + + template ::const_type = 0> + /* implicit */ Range(const std::string& str) + : b_(str.data()), e_(b_ + str.size()) {} + + // Allow implicit conversion from Range to Range if From is + // implicitly convertible to To. + template < + class OtherIter, + typename std::enable_if< + (!std::is_same::value && + std::is_convertible::value), + int>::type = 0> + constexpr /* implicit */ Range(const Range& other) + : b_(other.begin()), e_(other.end()) {} + + Range(const Range&) = default; + Range(Range&&) = default; + + Range& operator=(const Range&) & = default; + Range& operator=(Range&&) & = default; + + constexpr size_type size() const { + return e_ - b_; + } + bool empty() const { + return b_ == e_; + } + Iter data() const { + return b_; + } + Iter begin() const { + return b_; + } + Iter end() const { + return e_; + } + + void advance(size_type n) { + if (UNLIKELY(n > size())) { + throw std::out_of_range("index out of range"); + } + b_ += n; + } + + void subtract(size_type n) { + if (UNLIKELY(n > size())) { + throw std::out_of_range("index out of range"); + } + e_ -= n; + } + + Range subpiece(size_type first, size_type length = std::string::npos) const { + if (UNLIKELY(first > size())) { + throw std::out_of_range("index out of range"); + } + + return Range(b_ + first, std::min(length, size() - first)); + } +}; + +using ByteRange = Range; +using MutableByteRange = Range; +using StringPiece = Range; +} diff --git a/contrib/pzstd/utils/ScopeGuard.h b/contrib/pzstd/utils/ScopeGuard.h new file mode 100644 index 000000000..5a333e0ab --- /dev/null +++ b/contrib/pzstd/utils/ScopeGuard.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#include + +namespace pzstd { + +/** + * Dismissable scope guard. + * `Function` must be callable and take no parameters. + * Unless `dissmiss()` is called, the callable is executed upon destruction of + * `ScopeGuard`. + * + * Example: + * + * auto guard = makeScopeGuard([&] { cleanup(); }); + */ +template +class ScopeGuard { + Function function; + bool dismissed; + + public: + explicit ScopeGuard(Function&& function) + : function(std::move(function)), dismissed(false) {} + + void dismiss() { + dismissed = true; + } + + ~ScopeGuard() noexcept { + if (!dismissed) { + function(); + } + } +}; + +/// Creates a scope guard from `function`. +template +ScopeGuard makeScopeGuard(Function&& function) { + return ScopeGuard(std::forward(function)); +} +} diff --git a/contrib/pzstd/utils/ThreadPool.h b/contrib/pzstd/utils/ThreadPool.h new file mode 100644 index 000000000..a1d1fc0b9 --- /dev/null +++ b/contrib/pzstd/utils/ThreadPool.h @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#include "utils/WorkQueue.h" + +#include +#include +#include +#include + +namespace pzstd { +/// A simple thread pool that pulls tasks off its queue in FIFO order. +class ThreadPool { + std::vector threads_; + + WorkQueue> tasks_; + + public: + /// Constructs a thread pool with `numThreads` threads. + explicit ThreadPool(std::size_t numThreads) { + threads_.reserve(numThreads); + for (std::size_t i = 0; i < numThreads; ++i) { + threads_.emplace_back([&] { + std::function task; + while (tasks_.pop(task)) { + task(); + } + }); + } + } + + /// Finishes all tasks currently in the queue. + ~ThreadPool() { + tasks_.finish(); + for (auto& thread : threads_) { + thread.join(); + } + } + + /** + * Adds `task` to the queue of tasks to execute. Since `task` is a + * `std::function<>`, it cannot be a move only type. So any lambda passed must + * not capture move only types (like `std::unique_ptr`). + * + * @param task The task to execute. + */ + void add(std::function task) { + tasks_.push(std::move(task)); + } +}; +} diff --git a/contrib/pzstd/utils/WorkQueue.h b/contrib/pzstd/utils/WorkQueue.h new file mode 100644 index 000000000..3d926cc80 --- /dev/null +++ b/contrib/pzstd/utils/WorkQueue.h @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#pragma once + +#include "utils/Buffer.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace pzstd { + +/// Unbounded thread-safe work queue. +template +class WorkQueue { + // Protects all member variable access + std::mutex mutex_; + std::condition_variable cv_; + + std::queue queue_; + bool done_; + + public: + /// Constructs an empty work queue. + WorkQueue() : done_(false) {} + + /** + * Push an item onto the work queue. Notify a single thread that work is + * available. If `finish()` has been called, do nothing and return false. + * + * @param item Item to push onto the queue. + * @returns True upon success, false if `finish()` has been called. An + * item was pushed iff `push()` returns true. + */ + bool push(T item) { + { + std::lock_guard lock(mutex_); + if (done_) { + return false; + } + queue_.push(std::move(item)); + } + cv_.notify_one(); + return true; + } + + /** + * Attempts to pop an item off the work queue. It will block until data is + * available or `finish()` has been called. + * + * @param[out] item If `pop` returns `true`, it contains the popped item. + * If `pop` returns `false`, it is unmodified. + * @returns True upon success. False if the queue is empty and + * `finish()` has been called. + */ + bool pop(T& item) { + std::unique_lock lock(mutex_); + while (queue_.empty() && !done_) { + cv_.wait(lock); + } + if (queue_.empty()) { + assert(done_); + return false; + } + item = std::move(queue_.front()); + queue_.pop(); + return true; + } + + /** + * Promise that `push()` won't be called again, so once the queue is empty + * there will never any more work. + */ + void finish() { + { + std::lock_guard lock(mutex_); + assert(!done_); + done_ = true; + } + cv_.notify_all(); + } + + /// Blocks until `finish()` has been called (but the queue may not be empty). + void waitUntilFinished() { + std::unique_lock lock(mutex_); + while (!done_) { + cv_.wait(lock); + // If we were woken by a push, we need to wake a thread waiting on pop(). + if (!done_) { + lock.unlock(); + cv_.notify_one(); + lock.lock(); + } + } + } +}; + +/// Work queue for `Buffer`s that knows the total number of bytes in the queue. +class BufferWorkQueue { + WorkQueue queue_; + std::atomic size_; + + public: + BufferWorkQueue() : size_(0) {} + + void push(Buffer buffer) { + size_.fetch_add(buffer.size()); + queue_.push(std::move(buffer)); + } + + bool pop(Buffer& buffer) { + bool result = queue_.pop(buffer); + if (result) { + size_.fetch_sub(buffer.size()); + } + return result; + } + + void finish() { + queue_.finish(); + } + + /** + * Blocks until `finish()` has been called. + * + * @returns The total number of bytes of all the `Buffer`s currently in the + * queue. + */ + std::size_t size() { + queue_.waitUntilFinished(); + return size_.load(); + } +}; +} diff --git a/contrib/pzstd/utils/test/BufferTest.cpp b/contrib/pzstd/utils/test/BufferTest.cpp new file mode 100644 index 000000000..66ec961e2 --- /dev/null +++ b/contrib/pzstd/utils/test/BufferTest.cpp @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "utils/Buffer.h" +#include "utils/Range.h" + +#include +#include + +using namespace pzstd; + +namespace { +void deleter(const unsigned char* buf) { + delete[] buf; +} +} + +TEST(Buffer, Constructors) { + Buffer empty; + EXPECT_TRUE(empty.empty()); + EXPECT_EQ(0, empty.size()); + + Buffer sized(5); + EXPECT_FALSE(sized.empty()); + EXPECT_EQ(5, sized.size()); + + Buffer moved(std::move(sized)); + EXPECT_FALSE(sized.empty()); + EXPECT_EQ(5, sized.size()); + + Buffer assigned; + assigned = std::move(moved); + EXPECT_FALSE(sized.empty()); + EXPECT_EQ(5, sized.size()); +} + +TEST(Buffer, BufferManagement) { + std::shared_ptr buf(new unsigned char[10], deleter); + { + Buffer acquired(buf, MutableByteRange(buf.get(), buf.get() + 10)); + EXPECT_EQ(2, buf.use_count()); + Buffer moved(std::move(acquired)); + EXPECT_EQ(2, buf.use_count()); + Buffer assigned; + assigned = std::move(moved); + EXPECT_EQ(2, buf.use_count()); + + Buffer split = assigned.splitAt(5); + EXPECT_EQ(3, buf.use_count()); + + split.advance(1); + assigned.subtract(1); + EXPECT_EQ(3, buf.use_count()); + } + EXPECT_EQ(1, buf.use_count()); +} + +TEST(Buffer, Modifiers) { + Buffer buf(10); + { + unsigned char i = 0; + for (auto& byte : buf.range()) { + byte = i++; + } + } + + auto prefix = buf.splitAt(2); + + ASSERT_EQ(2, prefix.size()); + EXPECT_EQ(0, *prefix.data()); + + ASSERT_EQ(8, buf.size()); + EXPECT_EQ(2, *buf.data()); + + buf.advance(2); + EXPECT_EQ(4, *buf.data()); + + EXPECT_EQ(9, *(buf.range().end() - 1)); + + buf.subtract(2); + EXPECT_EQ(7, *(buf.range().end() - 1)); + + EXPECT_EQ(4, buf.size()); +} diff --git a/contrib/pzstd/utils/test/Makefile b/contrib/pzstd/utils/test/Makefile new file mode 100644 index 000000000..4c6906330 --- /dev/null +++ b/contrib/pzstd/utils/test/Makefile @@ -0,0 +1,41 @@ +# ########################################################################## +# Copyright (c) 2016-present, Facebook, Inc. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. +# ########################################################################## + +GTEST_INC ?= -isystem googletest/googletest/include +GTEST_LIB ?= -L googletest/build/googlemock/gtest + +# Define *.exe as extension for Windows systems +ifneq (,$(filter Windows%,$(OS))) +EXT =.exe +else +EXT = +endif + +PZSTDDIR = ../.. + +CPPFLAGS = -I$(PZSTDDIR) $(GTEST_INC) $(GTEST_LIB) +CFLAGS ?= -O3 +CFLAGS += -std=c++11 +CFLAGS += $(MOREFLAGS) +FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) + +%: %.cpp + $(CXX) $(FLAGS) -lgtest -lgtest_main $^ -o $@$(EXT) + +.PHONY: test clean + +test: BufferTest RangeTest ScopeGuardTest ThreadPoolTest WorkQueueTest + @./BufferTest$(EXT) + @./RangeTest$(EXT) + @./ScopeGuardTest$(EXT) + @./ThreadPoolTest$(EXT) + @./WorkQueueTest$(EXT) + +clean: + @rm -f BufferTest RangeTest ScopeGuardTest ThreadPoolTest WorkQueueTest diff --git a/contrib/pzstd/utils/test/RangeTest.cpp b/contrib/pzstd/utils/test/RangeTest.cpp new file mode 100644 index 000000000..c761c8aff --- /dev/null +++ b/contrib/pzstd/utils/test/RangeTest.cpp @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "utils/Range.h" + +#include +#include + +using namespace pzstd; + +// Range is directly copied from folly. +// Just some sanity tests to make sure everything seems to work. + +TEST(Range, Constructors) { + StringPiece empty; + EXPECT_TRUE(empty.empty()); + EXPECT_EQ(0, empty.size()); + + std::string str = "hello"; + { + Range piece(str.begin(), str.end()); + EXPECT_EQ(5, piece.size()); + EXPECT_EQ('h', *piece.data()); + EXPECT_EQ('o', *(piece.end() - 1)); + } + + { + StringPiece piece(str.data(), str.size()); + EXPECT_EQ(5, piece.size()); + EXPECT_EQ('h', *piece.data()); + EXPECT_EQ('o', *(piece.end() - 1)); + } + + { + StringPiece piece(str); + EXPECT_EQ(5, piece.size()); + EXPECT_EQ('h', *piece.data()); + EXPECT_EQ('o', *(piece.end() - 1)); + } + + { + StringPiece piece(str.c_str()); + EXPECT_EQ(5, piece.size()); + EXPECT_EQ('h', *piece.data()); + EXPECT_EQ('o', *(piece.end() - 1)); + } +} + +TEST(Range, Modifiers) { + StringPiece range("hello world"); + ASSERT_EQ(11, range.size()); + + { + auto hello = range.subpiece(0, 5); + EXPECT_EQ(5, hello.size()); + EXPECT_EQ('h', *hello.data()); + EXPECT_EQ('o', *(hello.end() - 1)); + } + { + auto hello = range; + hello.subtract(6); + EXPECT_EQ(5, hello.size()); + EXPECT_EQ('h', *hello.data()); + EXPECT_EQ('o', *(hello.end() - 1)); + } + { + auto world = range; + world.advance(6); + EXPECT_EQ(5, world.size()); + EXPECT_EQ('w', *world.data()); + EXPECT_EQ('d', *(world.end() - 1)); + } + + std::string expected = "hello world"; + EXPECT_EQ(expected, std::string(range.begin(), range.end())); + EXPECT_EQ(expected, std::string(range.data(), range.size())); +} diff --git a/contrib/pzstd/utils/test/ScopeGuardTest.cpp b/contrib/pzstd/utils/test/ScopeGuardTest.cpp new file mode 100644 index 000000000..0c4dc0357 --- /dev/null +++ b/contrib/pzstd/utils/test/ScopeGuardTest.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "utils/ScopeGuard.h" + +#include + +using namespace pzstd; + +TEST(ScopeGuard, Dismiss) { + { + auto guard = makeScopeGuard([&] { EXPECT_TRUE(false); }); + guard.dismiss(); + } +} + +TEST(ScopeGuard, Executes) { + bool executed = false; + { + auto guard = makeScopeGuard([&] { executed = true; }); + } + EXPECT_TRUE(executed); +} diff --git a/contrib/pzstd/utils/test/ThreadPoolTest.cpp b/contrib/pzstd/utils/test/ThreadPoolTest.cpp new file mode 100644 index 000000000..9b9868cb1 --- /dev/null +++ b/contrib/pzstd/utils/test/ThreadPoolTest.cpp @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "utils/ThreadPool.h" + +#include +#include +#include +#include + +using namespace pzstd; + +TEST(ThreadPool, Ordering) { + std::vector results; + + { + ThreadPool executor(1); + for (int i = 0; i < 100; ++i) { + executor.add([ &results, i ] { results.push_back(i); }); + } + } + + for (int i = 0; i < 100; ++i) { + EXPECT_EQ(i, results[i]); + } +} + +TEST(ThreadPool, AllJobsFinished) { + std::atomic numFinished{0}; + std::atomic start{false}; + { + ThreadPool executor(5); + for (int i = 0; i < 1000; ++i) { + executor.add([ &numFinished, &start ] { + while (!start.load()) { + // spin + } + ++numFinished; + }); + } + start.store(true); + } + EXPECT_EQ(1000, numFinished.load()); +} + +TEST(ThreadPool, AddJobWhileJoining) { + std::atomic done{false}; + { + ThreadPool executor(1); + executor.add([&executor, &done] { + while (!done.load()) { + std::this_thread::yield(); + } + // Sleep for a second to be sure that we are joining + std::this_thread::sleep_for(std::chrono::seconds(1)); + executor.add([] { + EXPECT_TRUE(false); + }); + }); + done.store(true); + } +} diff --git a/contrib/pzstd/utils/test/WorkQueueTest.cpp b/contrib/pzstd/utils/test/WorkQueueTest.cpp new file mode 100644 index 000000000..1b548d160 --- /dev/null +++ b/contrib/pzstd/utils/test/WorkQueueTest.cpp @@ -0,0 +1,176 @@ +/** + * Copyright (c) 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +#include "utils/Buffer.h" +#include "utils/WorkQueue.h" + +#include +#include +#include +#include + +using namespace pzstd; + +namespace { +struct Popper { + WorkQueue* queue; + int* results; + std::mutex* mutex; + + void operator()() { + int result; + while (queue->pop(result)) { + std::lock_guard lock(*mutex); + results[result] = result; + } + } +}; +} + +TEST(WorkQueue, SingleThreaded) { + WorkQueue queue; + int result; + + queue.push(5); + EXPECT_TRUE(queue.pop(result)); + EXPECT_EQ(5, result); + + queue.push(1); + queue.push(2); + EXPECT_TRUE(queue.pop(result)); + EXPECT_EQ(1, result); + EXPECT_TRUE(queue.pop(result)); + EXPECT_EQ(2, result); + + queue.push(1); + queue.push(2); + queue.finish(); + EXPECT_TRUE(queue.pop(result)); + EXPECT_EQ(1, result); + EXPECT_TRUE(queue.pop(result)); + EXPECT_EQ(2, result); + EXPECT_FALSE(queue.pop(result)); + + queue.waitUntilFinished(); +} + +TEST(WorkQueue, SPSC) { + WorkQueue queue; + const int max = 100; + + for (int i = 0; i < 10; ++i) { + queue.push(i); + } + + std::thread thread([ &queue, max ] { + int result; + for (int i = 0;; ++i) { + if (!queue.pop(result)) { + EXPECT_EQ(i, max); + break; + } + EXPECT_EQ(i, result); + } + }); + + std::this_thread::yield(); + for (int i = 10; i < max; ++i) { + queue.push(i); + } + queue.finish(); + + thread.join(); +} + +TEST(WorkQueue, SPMC) { + WorkQueue queue; + std::vector results(10000, -1); + std::mutex mutex; + std::vector threads; + for (int i = 0; i < 100; ++i) { + threads.emplace_back(Popper{&queue, results.data(), &mutex}); + } + + for (int i = 0; i < 10000; ++i) { + queue.push(i); + } + queue.finish(); + + for (auto& thread : threads) { + thread.join(); + } + + for (int i = 0; i < 10000; ++i) { + EXPECT_EQ(i, results[i]); + } +} + +TEST(WorkQueue, MPMC) { + WorkQueue queue; + std::vector results(10000, -1); + std::mutex mutex; + std::vector popperThreads; + for (int i = 0; i < 100; ++i) { + popperThreads.emplace_back(Popper{&queue, results.data(), &mutex}); + } + + std::vector pusherThreads; + for (int i = 0; i < 10; ++i) { + auto min = i * 1000; + auto max = (i + 1) * 1000; + pusherThreads.emplace_back( + [ &queue, min, max ] { + for (int i = min; i < max; ++i) { + queue.push(i); + } + }); + } + + for (auto& thread : pusherThreads) { + thread.join(); + } + queue.finish(); + + for (auto& thread : popperThreads) { + thread.join(); + } + + for (int i = 0; i < 10000; ++i) { + EXPECT_EQ(i, results[i]); + } +} + +TEST(BufferWorkQueue, SizeCalculatedCorrectly) { + { + BufferWorkQueue queue; + queue.finish(); + EXPECT_EQ(0, queue.size()); + } + { + BufferWorkQueue queue; + queue.push(Buffer(10)); + queue.finish(); + EXPECT_EQ(10, queue.size()); + } + { + BufferWorkQueue queue; + queue.push(Buffer(10)); + queue.push(Buffer(5)); + queue.finish(); + EXPECT_EQ(15, queue.size()); + } + { + BufferWorkQueue queue; + queue.push(Buffer(10)); + queue.push(Buffer(5)); + queue.finish(); + Buffer buffer; + queue.pop(buffer); + EXPECT_EQ(5, queue.size()); + } +} From f381d2d39c0cee44d98b1e8ccbc31044ad3c154b Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 15:30:36 -0700 Subject: [PATCH 03/32] Fix small README things --- contrib/pzstd/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/contrib/pzstd/README.md b/contrib/pzstd/README.md index 1a5a0105d..eba64085a 100644 --- a/contrib/pzstd/README.md +++ b/contrib/pzstd/README.md @@ -1,6 +1,7 @@ # Parallel Zstandard (PZstandard) -Parallel Zstandard provides Zstandard format compatible compression and decompression that is able to utilize multiple cores. +Parallel Zstandard is a Pigz-like tool for Zstandard. +It provides Zstandard format compatible compression and decompression that is able to utilize multiple cores. It breaks the input up into equal sized chunks and compresses each chunk independently into a Zstandard frame. It then concatenates the frames together to produce the final compressed output. Optionally, with the `-p` option, PZstandard will write a 12 byte header for each frame that is a skippable frame in the Zstandard format, which tells PZstandard the size of the next compressed frame. @@ -31,11 +32,11 @@ Compression Speed vs Ratio with 4 Threads | Decompression Speed with 4 Threads The test procedure was to run each of the following commands 2 times for each compression level, and take the minimum time. - time ./pzstd -# -n 4 -p -c silesia.tar > silesia.tar.zst - time ./pzstd -d -n 4 -c silesia.tar.zst > /dev/null + time pzstd -# -n 4 -p -c silesia.tar > silesia.tar.zst + time pzstd -d -n 4 -c silesia.tar.zst > /dev/null - time pigz -# -p 4 -k -c silesia.tar > silesia.tar.gz - time pigz -d -p 4 -k -c silesia.tar.gz > /dev/null + time pigz -# -p 4 -k -c silesia.tar > silesia.tar.gz + time pigz -d -p 4 -k -c silesia.tar.gz > /dev/null PZstandard was tested using compression levels 1-19, and Pigz was tested using compression levels 1-9. Pigz cannot do parallel decompression, it simply does each of reading, decompression, and writing on separate threads. From 4c3b1881f2fc6ae132bdfbd8cb90ce837dd054b1 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 15:32:10 -0700 Subject: [PATCH 04/32] Remove old benchmark code --- contrib/pzstd/bench.cpp | 146 ---------------------------------------- 1 file changed, 146 deletions(-) delete mode 100644 contrib/pzstd/bench.cpp diff --git a/contrib/pzstd/bench.cpp b/contrib/pzstd/bench.cpp deleted file mode 100644 index 56bad3915..000000000 --- a/contrib/pzstd/bench.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) 2016-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -#include "ErrorHolder.h" -#include "Options.h" -#include "Pzstd.h" -#include "utils/FileSystem.h" -#include "utils/Range.h" -#include "utils/ScopeGuard.h" -#include "utils/ThreadPool.h" -#include "utils/WorkQueue.h" - -#include -#include -#include - -using namespace pzstd; - -namespace { -// Prints how many ns it was in scope for upon destruction -// Used for rough estimates of how long things took -struct BenchmarkTimer { - using Clock = std::chrono::system_clock; - Clock::time_point start; - FILE* fd; - - explicit BenchmarkTimer(FILE* fd = stdout) : fd(fd) { - start = Clock::now(); - } - - ~BenchmarkTimer() { - auto end = Clock::now(); - size_t ticks = - std::chrono::duration_cast(end - start) - .count(); - ticks = std::max(ticks, size_t{1}); - for (auto tmp = ticks; tmp < 100000; tmp *= 10) { - std::fprintf(fd, " "); - } - std::fprintf(fd, "%zu | ", ticks); - } -}; -} - -// Code I used for benchmarking - -void testMain(const Options& options) { - if (!options.decompress) { - if (options.compressionLevel < 10) { - std::printf("0"); - } - std::printf("%u | ", options.compressionLevel); - } else { - std::printf(" d | "); - } - if (options.numThreads < 10) { - std::printf("0"); - } - std::printf("%u | ", options.numThreads); - - FILE* inputFd = std::fopen(options.inputFile.c_str(), "rb"); - if (inputFd == nullptr) { - std::abort(); - } - size_t inputSize = 0; - if (inputFd != stdin) { - std::error_code ec; - inputSize = file_size(options.inputFile, ec); - if (ec) { - inputSize = 0; - } - } - FILE* outputFd = std::fopen(options.outputFile.c_str(), "wb"); - if (outputFd == nullptr) { - std::abort(); - } - auto guard = makeScopeGuard([&] { - std::fclose(inputFd); - std::fclose(outputFd); - }); - - WorkQueue> outs; - ErrorHolder errorHolder; - size_t bytesWritten; - { - ThreadPool executor(options.numThreads); - BenchmarkTimer timeIncludingClose; - if (!options.decompress) { - executor.add( - [&errorHolder, &outs, &executor, inputFd, inputSize, &options] { - asyncCompressChunks( - errorHolder, - outs, - executor, - inputFd, - inputSize, - options.numThreads, - options.determineParameters()); - }); - bytesWritten = writeFile(errorHolder, outs, outputFd, true); - } else { - executor.add([&errorHolder, &outs, &executor, inputFd] { - asyncDecompressFrames(errorHolder, outs, executor, inputFd); - }); - bytesWritten = writeFile( - errorHolder, outs, outputFd, /* writeSkippableFrames */ false); - } - } - if (errorHolder.hasError()) { - std::fprintf(stderr, "Error: %s.\n", errorHolder.getError().c_str()); - std::abort(); - } - std::printf("%zu\n", bytesWritten); -} - -int main(int argc, const char** argv) { - if (argc < 3) { - return 1; - } - Options options(0, 23, 0, false, "", "", true, true); - // Benchmarking code - for (size_t i = 0; i < 2; ++i) { - for (size_t compressionLevel = 1; compressionLevel <= 16; - compressionLevel <<= 1) { - for (size_t numThreads = 1; numThreads <= 16; numThreads <<= 1) { - options.numThreads = numThreads; - options.compressionLevel = compressionLevel; - options.decompress = false; - options.inputFile = argv[1]; - options.outputFile = argv[2]; - testMain(options); - options.decompress = true; - options.inputFile = argv[2]; - options.outputFile = std::string(argv[1]) + ".d"; - testMain(options); - std::fflush(stdout); - } - } - } - return 0; -} From b2490e975a65c44db5b7d4e4cb01c8429050f67c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 1 Sep 2016 15:46:09 -0700 Subject: [PATCH 05/32] changed test to avoid issue #316 (reported by John the Scott) --- .gitignore | 1 + tests/Makefile | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 5f2ca97de..62f6d6ae2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Object files *.o *.ko +*.dSYM # Libraries *.lib diff --git a/tests/Makefile b/tests/Makefile index 6b22e6dbd..3ce9f317e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -160,10 +160,10 @@ valgrindTest: zstd datagen fuzzer fullbench zbufftest $(VALGRIND) ./datagen -g50M > $(VOID) $(VALGRIND) $(PRGDIR)/zstd ; if [ $$? -eq 0 ] ; then echo "zstd without argument should have failed"; false; fi ./datagen -g80 | $(VALGRIND) $(PRGDIR)/zstd - -c > $(VOID) - ./datagen -g16KB | $(VALGRIND) $(PRGDIR)/zstd -vf - -o $(VOID) + ./datagen -g16KB | $(VALGRIND) $(PRGDIR)/zstd -vf - -c > $(VOID) ./datagen -g2930KB | $(VALGRIND) $(PRGDIR)/zstd -5 -vf - -o tmp - $(VALGRIND) $(PRGDIR)/zstd -vdf tmp -o $(VOID) - ./datagen -g64MB | $(VALGRIND) $(PRGDIR)/zstd -vf - -o $(VOID) + $(VALGRIND) $(PRGDIR)/zstd -vdf tmp -c > $(VOID) + ./datagen -g64MB | $(VALGRIND) $(PRGDIR)/zstd -vf - -c > $(VOID) @rm tmp $(VALGRIND) ./fuzzer -T1mn -t1 $(VALGRIND) ./fullbench -i1 From 7304eb7c094b2848f229a09f4ffb9ec5783f8757 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 1 Sep 2016 15:47:33 -0700 Subject: [PATCH 06/32] bumped version number --- NEWS | 3 +++ lib/zstd.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 2e300726c..a1ad00400 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,6 @@ +v1.0.1 +Fixed : CLI -d output to stdout by default when input is stdin (#322) + v1.0.0 Change Licensing, all project is now BSD, Copyright Facebook Small decompression speed improvement diff --git a/lib/zstd.h b/lib/zstd.h index d768ded35..f6c5564dd 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -33,7 +33,7 @@ extern "C" { /*======= Version =======*/ #define ZSTD_VERSION_MAJOR 1 #define ZSTD_VERSION_MINOR 0 -#define ZSTD_VERSION_RELEASE 0 +#define ZSTD_VERSION_RELEASE 1 #define ZSTD_LIB_VERSION ZSTD_VERSION_MAJOR.ZSTD_VERSION_MINOR.ZSTD_VERSION_RELEASE #define ZSTD_QUOTE(str) #str From 040cfd8e7d2159b15e2b9254e7eeba4073e63c5f Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 16:21:19 -0700 Subject: [PATCH 07/32] Get ready to add tests to travis-ci --- .gitignore | 1 + contrib/pzstd/Makefile | 7 ++++++- contrib/pzstd/test/Makefile | 12 +++++++----- contrib/pzstd/utils/test/Makefile | 7 ++++--- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 5f2ca97de..13b61972c 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ _zstdbench/ *.idea *.swp .DS_Store +googletest/ diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile index 512a76292..ba86f5d4d 100644 --- a/contrib/pzstd/Makefile +++ b/contrib/pzstd/Makefile @@ -59,6 +59,11 @@ main.o: main.cpp *.h utils/*.h pzstd: libzstd.a Pzstd.o SkippableFrame.o Options.o main.o $(CXX) $(FLAGS) $^ -o $@$(EXT) +googletest: + @git clone https://github.com/google/googletest + @mkdir -p googletest/build + @cd googletest/build && cmake .. && make + test: libzstd.a Pzstd.o Options.o SkippableFrame.o $(MAKE) -C utils/test test $(MAKE) -C test test @@ -67,5 +72,5 @@ clean: $(MAKE) -C $(ZSTDDIR) clean $(MAKE) -C utils/test clean $(MAKE) -C test clean - @$(RM) libzstd.a *.o pzstd$(EXT) + @$(RM) -rf googletest/ libzstd.a *.o pzstd$(EXT) @echo Cleaning completed diff --git a/contrib/pzstd/test/Makefile b/contrib/pzstd/test/Makefile index 3b0ffec89..147d9bd79 100644 --- a/contrib/pzstd/test/Makefile +++ b/contrib/pzstd/test/Makefile @@ -7,10 +7,6 @@ # of patent rights can be found in the PATENTS file in the same directory. # ########################################################################## -# Set GTEST_INC and GTEST_LIB to work with your install of gtest -GTEST_INC ?= -isystem googletest/googletest/include -GTEST_LIB ?= -L googletest/build/googlemock/gtest - # Define *.exe as extension for Windows systems ifneq (,$(filter Windows%,$(OS))) EXT =.exe @@ -22,6 +18,10 @@ PZSTDDIR = .. PROGDIR = ../../../programs ZSTDDIR = ../../../lib +# Set GTEST_INC and GTEST_LIB to work with your install of gtest +GTEST_INC ?= -isystem $(PZSTDDIR)/googletest/googletest/include +GTEST_LIB ?= -L $(PZSTDDIR)/googletest/build/googlemock/gtest + CPPFLAGS = -I$(PZSTDDIR) $(GTEST_INC) $(GTEST_LIB) -I$(ZSTDDIR)/common -I$(PROGDIR) CFLAGS ?= -O3 @@ -37,9 +37,11 @@ datagen.o: $(PROGDIR)/datagen.* .PHONY: test clean -test: OptionsTest PzstdTest RoundTripTest +test: OptionsTest PzstdTest @./OptionsTest$(EXT) @./PzstdTest$(EXT) + +roundtrip: RoundTripTest @./RoundTripTest$(EXT) clean: diff --git a/contrib/pzstd/utils/test/Makefile b/contrib/pzstd/utils/test/Makefile index 4c6906330..6f801c309 100644 --- a/contrib/pzstd/utils/test/Makefile +++ b/contrib/pzstd/utils/test/Makefile @@ -7,9 +7,6 @@ # of patent rights can be found in the PATENTS file in the same directory. # ########################################################################## -GTEST_INC ?= -isystem googletest/googletest/include -GTEST_LIB ?= -L googletest/build/googlemock/gtest - # Define *.exe as extension for Windows systems ifneq (,$(filter Windows%,$(OS))) EXT =.exe @@ -19,6 +16,10 @@ endif PZSTDDIR = ../.. +# Set GTEST_INC and GTEST_LIB to work with your install of gtest +GTEST_INC ?= -isystem $(PZSTDDIR)/googletest/googletest/include +GTEST_LIB ?= -L $(PZSTDDIR)/googletest/build/googlemock/gtest + CPPFLAGS = -I$(PZSTDDIR) $(GTEST_INC) $(GTEST_LIB) CFLAGS ?= -O3 CFLAGS += -std=c++11 From 0c28f62d26268b4dc1867f40c7611d178fd0074b Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 16:26:27 -0700 Subject: [PATCH 08/32] Update travis-ci config to include pzstd --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 350fc1e6b..dc9d2fdee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ matrix: env: PLATFORM="Ubuntu 12.04 container" CMD="make test && make clean && make travis-install" - os: linux sudo: false - env: PLATFORM="Ubuntu 12.04 container" CMD="make -C tests test-zstd_nolegacy && make clean && make zlibwrapper && make clean && make cmaketest" + env: PLATFORM="Ubuntu 12.04 container" CMD="make -C tests test-zstd_nolegacy && make clean && make zlibwrapper && make clean && make cmaketest && make -C contrib/pzstd googletest && make -C contrib/pzstd test && make -C contrib/pzstd clean" - os: linux sudo: false env: PLATFORM="Ubuntu 12.04 container" CMD="make usan" From 2b0830b067d0f4e722f80b5ca00a50b04ce135d6 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 16:39:45 -0700 Subject: [PATCH 09/32] Randomize tests so travis-ci tests can check for existing failures --- contrib/pzstd/test/PzstdTest.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/contrib/pzstd/test/PzstdTest.cpp b/contrib/pzstd/test/PzstdTest.cpp index a6eb74596..9d1256fa5 100644 --- a/contrib/pzstd/test/PzstdTest.cpp +++ b/contrib/pzstd/test/PzstdTest.cpp @@ -15,17 +15,22 @@ #include #include #include +#include using namespace std; using namespace pzstd; TEST(Pzstd, SmallSizes) { + unsigned seed = std::random_device{}(); + std::fprintf(stderr, "Pzstd.SmallSizes seed: %u\n", seed); + std::mt19937 gen(seed); + for (unsigned len = 1; len < 1028; ++len) { std::string inputFile = std::tmpnam(nullptr); auto guard = makeScopeGuard([&] { std::remove(inputFile.c_str()); }); { static uint8_t buf[1028]; - RDG_genBuffer(buf, len, 0.5, 0.0, 42); + RDG_genBuffer(buf, len, 0.5, 0.0, gen()); auto fd = std::fopen(inputFile.c_str(), "wb"); auto written = std::fwrite(buf, 1, len, fd); std::fclose(fd); @@ -56,12 +61,16 @@ TEST(Pzstd, SmallSizes) { } TEST(Pzstd, LargeSizes) { + unsigned seed = std::random_device{}(); + std::fprintf(stderr, "Pzstd.LargeSizes seed: %u\n", seed); + std::mt19937 gen(seed); + for (unsigned len = 1 << 20; len <= (1 << 24); len *= 2) { std::string inputFile = std::tmpnam(nullptr); auto guard = makeScopeGuard([&] { std::remove(inputFile.c_str()); }); { std::unique_ptr buf(new uint8_t[len]); - RDG_genBuffer(buf.get(), len, 0.5, 0.0, 42); + RDG_genBuffer(buf.get(), len, 0.5, 0.0, gen()); auto fd = std::fopen(inputFile.c_str(), "wb"); auto written = std::fwrite(buf.get(), 1, len, fd); std::fclose(fd); From ef9999f0b9762003f96a3e5bddb7e60dace458bc Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 1 Sep 2016 16:44:48 -0700 Subject: [PATCH 10/32] zstreamtest depends only on standard C time.h --- NEWS | 3 +++ tests/zstreamtest.c | 56 +++++++++++++++++++-------------------------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/NEWS b/NEWS index a1ad00400..bbff87f65 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,9 @@ Modified : minor compression level adaptations Updated : compression format specification to v0.2.0 changed : zstd.h moved to /lib directory +v0.7.5 +Transition version, supporting decoding of v0.8.x + v0.7.4 Added : homebrew for Mac, by Daniel Cade Added : more examples diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index f02197491..c49e9d93e 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -23,13 +23,13 @@ **************************************/ #include /* free */ #include /* fgets, sscanf */ -#include /* timeb */ +#include /* clock_t, clock() */ #include /* strcmp */ #include "mem.h" -#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel */ +#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel, ZSTD_customMem */ #include "zstd.h" /* ZSTD_compressBound */ #include "datagen.h" /* RDG_genBuffer */ -#define XXH_STATIC_LINKING_ONLY +#define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ #include "xxhash.h" /* XXH64_* */ @@ -55,13 +55,13 @@ static const U32 prime2 = 2246822519U; static U32 g_displayLevel = 2; #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ - if ((FUZ_GetMilliSpan(g_displayTime) > g_refreshRate) || (g_displayLevel>=4)) \ - { g_displayTime = FUZ_GetMilliStart(); DISPLAY(__VA_ARGS__); \ + if ((FUZ_GetClockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ + { g_displayClock = clock(); DISPLAY(__VA_ARGS__); \ if (g_displayLevel>=4) fflush(stdout); } } -static const U32 g_refreshRate = 150; -static U32 g_displayTime = 0; +static const clock_t g_refreshRate = CLOCKS_PER_SEC * 15 / 100; +static clock_t g_displayClock = 0; -static U32 g_testTime = 0; +static clock_t g_clockTime = 0; /*-******************************************************* @@ -69,22 +69,9 @@ static U32 g_testTime = 0; *********************************************************/ #define MAX(a,b) ((a)>(b)?(a):(b)) -static U32 FUZ_GetMilliStart(void) +static clock_t FUZ_GetClockSpan(clock_t clockStart) { - struct timeb tb; - U32 nCount; - ftime( &tb ); - nCount = (U32) (((tb.time & 0xFFFFF) * 1000) + tb.millitm); - return nCount; -} - -static U32 FUZ_GetMilliSpan(U32 nTimeStart) -{ - U32 const nCurrent = FUZ_GetMilliStart(); - U32 nSpan = nCurrent - nTimeStart; - if (nTimeStart > nCurrent) - nSpan += 0x100000 * 1000; - return nSpan; + return clock() - clockStart; /* works even when overflow. Max span ~ 30 mn */ } /*! FUZ_rand() : @@ -336,7 +323,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres U32 coreSeed = seed; ZSTD_CStream* zc; ZSTD_DStream* zd; - U32 startTime = FUZ_GetMilliStart(); + clock_t startClock = clock(); /* allocations */ zc = ZSTD_createCStream(); @@ -366,7 +353,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres FUZ_rand(&coreSeed); /* test loop */ - for ( ; (testNb <= nbTests) || (FUZ_GetMilliSpan(startTime) < g_testTime) ; testNb++ ) { + for ( ; (testNb <= nbTests) || (FUZ_GetClockSpan(startClock) < g_clockTime) ; testNb++ ) { U32 lseed; const BYTE* srcBuffer; const BYTE* dict; @@ -594,7 +581,7 @@ int main(int argc, const char** argv) case 'i': argument++; - nbTests=0; g_testTime=0; + nbTests=0; g_clockTime=0; while ((*argument>='0') && (*argument<='9')) { nbTests *= 10; nbTests += *argument - '0'; @@ -604,15 +591,15 @@ int main(int argc, const char** argv) case 'T': argument++; - nbTests=0; g_testTime=0; + nbTests=0; g_clockTime=0; while ((*argument>='0') && (*argument<='9')) { - g_testTime *= 10; - g_testTime += *argument - '0'; + g_clockTime *= 10; + g_clockTime += *argument - '0'; argument++; } - if (*argument=='m') g_testTime *=60, argument++; + if (*argument=='m') g_clockTime *=60, argument++; if (*argument=='n') argument++; - g_testTime *= 1000; + g_clockTime *= CLOCKS_PER_SEC; break; case 's': @@ -656,7 +643,12 @@ int main(int argc, const char** argv) /* Get Seed */ DISPLAY("Starting zstream tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), ZSTD_VERSION_STRING); - if (!seedset) seed = FUZ_GetMilliStart() % 10000; + if (!seedset) { + time_t const t = time(NULL); + U32 const h = XXH32(&t, sizeof(t), 1); + seed = h % 10000; + } + DISPLAY("Seed = %u\n", seed); if (proba!=FUZ_COMPRESSIBILITY_DEFAULT) DISPLAY("Compressibility : %i%%\n", proba); From 60181e3aafd3732dee7d71e176c63673198ef8b4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 1 Sep 2016 17:12:26 -0700 Subject: [PATCH 11/32] zstd cli correctly detects console on Mac OS-X --- NEWS | 2 ++ programs/zstdcli.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index bbff87f65..903350032 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,7 @@ v1.0.1 +New : contrib/pzstd, parallel version of zstd, by Nick Terrell Fixed : CLI -d output to stdout by default when input is stdin (#322) +Fixed : CLI correctly detects console on Mac OS-X v1.0.0 Change Licensing, all project is now BSD, Copyright Facebook diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 57a271ab9..e829c93d6 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -52,7 +52,7 @@ # include /* _isatty */ # define IS_CONSOLE(stdStream) _isatty(_fileno(stdStream)) #else -# if defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) +# if defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || (defined(__APPLE__) && defined(__MACH__)) /* https://sourceforge.net/p/predef/wiki/OperatingSystems/ */ # include /* isatty */ # define IS_CONSOLE(stdStream) isatty(fileno(stdStream)) # else From 2741677a8dc07ed963fa1490acb018108b047dd8 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 17:43:07 -0700 Subject: [PATCH 12/32] Update scale for compression speed graph --- contrib/pzstd/images/Cspeed.png | Bin 58612 -> 66235 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/contrib/pzstd/images/Cspeed.png b/contrib/pzstd/images/Cspeed.png index 516d09807b304fa64bf563eb0468fc3b2a1a59c2..fcafe23c4b47c5a2607061ac4fdcb688cbc68985 100644 GIT binary patch literal 66235 zcmeAS@N?(olHy`uVBq!ia0y~yU^&CUz?{m##=yYPz>s>Hfq{XsILO_JVcj{ImkbOH zoCO|{#S9GMLLkhTKL1h>1A_yDr;B4q1>>8$oO5DJYc)RnPf43}a-OG?kOzmLQ_m)D zy#)$g5`yJR!`5`2ULJM(d(PB->CwMeUEQ~IQLcS%{MB!wg_>D`cSKokvK)1CoS>?h z$XPvY=9||lpWnT2F}^qR&di;~&(A&4yKjH*PWAbp^NauefB*kZ_B;LmOP~Jx{r-QX zxA*Dp$u12Hj7%&X0tyar#)6ZqRn^t?*ESVBJ#|m5JfId$9;C3ADfj2<)YH?R>e~t< zX$48Ka0p~rHHNMZOD&yup~N&i5sP+b4vrbTjmhHZRyR0&=H!@R8b5orA%@fe0Vbw2 z=La`jp_U-5y->r%ly>&|vn(GBDT9`VhMNKwWr7IRP>-6-Z)k}8ymhA(ru%sm6%x!? z`eIRo1!VSFb%lg$HM#4KVM>j937m+Aa4c;xjh}jT1~fYu>^RWID}Bj)-^X5)!beBG ztSjHEy>o?PXVi6!rLK3^8>XPMgKe%<|B+KR^qrhV9J7;vPT ziG}0J{)UFgzuOl|2`F56!1Vk3dw+kMk1ka&7PeoS>~A-7ncv)5({v&yP1B8j)?UNA zy_O|LuI9tRFQ>xy8QH$CJiq$kB)+&UUteEe{`=i-ecP{Bf-}$0v(3D>r}E{G$Nm29 zGKEJTu$vy3_hZiAH|h4Lvpz-d{~BHT+4A<*?C@RB=T%?YSNr={)WW4pRZB}smK^4{ zw>dl4dVBuwg%uY$=67aYdARL%o_D|fzY42gFBTuSNf7&fr2gOY`ek#i%a>(dUY2^_ z^(jk(@`CSQF8lYtFq>(b9ro?Ncj1MKYlSYYGKbH6{k7MyO~uc*fuV7ixPZax-)bR@ zOe|8757g~{dj8s8|9ktE>hJHGS6SBn`cm@m=kw*)_dGMrJj9~>hM&o*Mf#EZtL^*$ zzFotzcJ10yaf9v$H^TRQnp$$V^!iuseNUFC&n*f1b)nsE3Tx7*+T0v`zZf^;}bh+PEe`mV9UiZ<7)D1O=CtTUi}Tp z$NRpt>end#{WN|5nOC>2U7I%V>zedb+iJ7CJ2QBn-`rK2{p;5CebcI{t1rKf|NpDa zeC2X3xBhw0?Y{4fTz^2=-)7^yu6a|xUh>xWHNR8f{LQLrifjI{9p@KaKe@i{>*_Zf z%iiAFcTc4>{2-g|hiSUen`Rvixb$b=>z6M*@7}!|dE0gN-@Et!#_oF1#J%Z2=lUy3v%F*@o;HK^SzOH3wO;-N# zY3}sOWEEe#iIdH?E_*Zp7Dwg=U$579obG57X1 z5yn3rAMc7V?haZRXD@uTRB{#HQ!A1A`D@~i2mE`XDyCVb_}n&bGjHmjkI&5a|6CAb z_~e7K{7;WWybnRPQ~zh=*nvCw%FK`i>^#*TW{7hPt&TJ z<2pe(=uF|a*YW?WF6=40vfqK{dHt7*?wfz=#sB*>{d#@t6;Q#?!Xd-NXnTf_ahJD{ zfo*$? zZ+cj%Uv;rNh$V_`+3WTD?T)g15^U)FmZh>h;NJBvIn7W!UsXZrcd zbH$fF&;S2tkA-p--~SmeY?!*V!`4WAx$*x;|Nj$hTaVfVsn02Jir-iCe)T?Pt%^XK z^3XLe!nJa5ZPC12_xtV5zgsFlKRcspxA>vZ{Kbz%7-#PIzUX=2yt*%o<-h7q+K_Q^ zQQhb7y;swf_Lu#WdAje_s@0nmTR+YfzF*HA{O_;uSF2|;lG)y!+b5jFxBtgHO}S5T ztN4^lB`P-l;5lBN%d&d)SIK|h>;Hd$V}z5YjgPU_*H zO63z@zHQ(CcY(u+@B9D%&3U?w!{Kht=d(AHm<3HvUfy~=?zZ{&z%LEmme-P=ZnZhe ze`QAT+h+Mc3HSM*)!gT=`_TO6j$w;>Q>5XN^*`05^~|b^eu*EK>ep%>{jmCxh~uvNzxUp^UeVkAndh68THEvZwk^Zvd`U;g7wugOubMHinQFd&f$0LN%$9;KKH7=w@#Mejk zKX*XnetFHlFjPdz1{|pyh`JPxP zdFRdZ-yXuQbHF{Ah11xXG8g#gpW9~b8?f8-Fb zFL%fD(A8l}{p&tWzS+P2g{tVmc^nN#oEQaHbjcZf6#w_*`d9m3i|w^4cdf7g`#SRd z8s$CFrM)T6{bO_5t1Bxb`!~KQ6=K}>Vx_RXj50@nOp4Y% z4;Br%suv4$J~q8LDfYa>^GUMZHV%cO0+;=39tj_9|FXAF{tds)2ZwLWW>uS9=Pv50 z?AlfTDtP`WroxNaPej*OxOT~(zPMTOmB`*(ht$+;`x9K3+z=POX>r_UdHBE1s!a3RQE(udn;EqoAv-U!?KYhwgb3 z_y0JmziBGt;_Wv)g1>n5+^Z1m>HIu}YliFj6C5cnU6x0bdY{Oh;gdKjFUZp^;&`Vf zu;EXMpilStQlBSsjnj7UFWMf%68&gL_4~c%yX6NrDn5EaHsvMk26pBOMiZSbnD|{8QTYJY-f}#X0xAI9CtB|xjN|B_tYgXW?IFp zx}vzR`?2-1S@R#hSSt2^gF)q|C;Jq49G+UQ^SysrU-HMf@h5BN{SNuH*Kpqak9*e4 z1673!teBY6t`?SvF|lxjxGu0r5IZ<;;xbSr9r;1MPUqjt`TsKa%Vh~3>6*uW(OIdj z^(g<9KkfM+OO+Og?sfY2f&afjbgb^TxkaZmBO7$9jB4Lk-#^Wy1!_diW6eyeXOVeS zD*RfdH{rlLT-X{2AwMv56!6P5KL-~_E(*8W?o%&7bfaG)O9Y*&KScP3WHg!vE z%=Sr-7x^x3PhKL?B|dq_^Y6htOM4U&S(M#p9nx*mw*UY0{O05N|G(wSmvr3_*L?SQ z$8n|5&-+gKMf^~H_fjHGRx`}3=epq;-L}83U7UB>{eH+ZPJ1y`;@~rz1u~V%rT6~W zbt{yzFn@mE`(mqPKS$Mb#?9gTe_d5=cR04Q&Fkt9ACdX?x0IOD1efsiKN6Miex48V z;)O2t4gC+FUHq&QWXZVMOI9gj+U>B%#xmc_>@L=CDb)E_u;OyYD+{svKd0V#o@g=a zeecE}J@T*0NENwNQ&t=EaZ71+z9>^fmd$lOhH5C0L|E4_Go97~y!tN!K}Z$B>N zuhppTV>ptKxg<9x^LFpE9WOo#zSPaHJni^B_)hmn%atmw8_a^f9Fw`>o%{aY-de`h ze>MwMt}cx~y7QCB-a|?bGX4E^Ulza2|Mx6^Q?|SRvLAn3Wxbbj>=0u={mJHHx6Uoo zOS?*6e>xjlz3y82q8$#E3$&|5*2e7IR1q$=q=#4VMVH9l3thqYcI)hW{pmr%$A4LS z*L_R3sG8==zj?*=fX^>NSO0lBC*7n}ru+RLjbhdBl{NQ4SxIdE$D%h+6|oekfpeEH zY!a%R_vrbHo5GrPDI(veX`B;2IQ50D*vg7U?;hC*>YQv2KYIJ|@vXU=4cu$5D$@BG1D zFUQjHuzmjDH|d)l+*c+i-}d;#SJrnUZf#lDCl`g}gj2T_t6W_AcXlISMUF|dd>I9mBsyAdbM+Rd-b1Y-X&OZ_PKy`M8CwjD9^5YPL=$7_LxW3 z&Eq(9+V3Jo4}N%I8R>cIwJ5hvh;`^| zxLf>s?w)u^DPpeqh3xR~A;>j7%)6lmcvgw3;k_ z#@{<_St*y%wcuia=6}IR84D?``hDv|&abP9w-NtdB9*y8zwdl=qT!p>AzGptr$0Zn z{e0-n-1jx<`*z+hzIR)zF5=k!AIE*_q6yd&yQKwm@Bmva{XemwpyYd>kv zUp|=+X=`q4zIT+E-uvKbM(~#_o$fb|Ffk{}at9v~*!6wi_r1ziwJXimZF#F@tG9oD zl0?DQX2nVaovQp}7mnWlIrsj{*GnbdE4v!({!tve_V=nCii`5*|NF9hvv1-0;|5PS zmVxSw+!{2`OimZsDaSt?=jeF0w~^oV=IOm@ zmf%FYr$%`%ELiu*flL2{99EVy$F5z!$tj>1S{OdtJQUzV_ON6p8oHFm<42Xxvl9W}W5qIM?x zVWk8y?N4q7%58>j4i-#l7t;d&@33ck-Dt7lPx8-wbIuoB(S0V>vuC5isRIuW{7hFp zB`NmNvS&e-O_t)+;Ny)?&%6#fSzf35y|Qkv*M9S3pN#D;90QGR2&|ab&=C1mXKp70 zBhz9rjdzJ+_8XF!-W3J^d#wKdqveglkIr^Mb}oe*Y98;k4SUyP62reU=;^8cPWc0d zoxeST{}}YS^L*|;%ARDpMPk`kP^VjZQbf(ZiBELx^y*{JzZKcH@z;y#`~N)6soU~F zO3z{Db?bTFTML)whQBNfZ1=b*ovstavgpxf(Y@1>BGOpSENTz_@~u0ejOVlVRJZE* zsaEM9w_V@=@9WE1+3QX|TikusAldG~qc10v`%|<}xbd%^k*q6r@I?D}w|yC(Yo?xk z^mr9G^bAGx&LnH(Y;n|ynaF3iG|Sd{`4-zWk2mr zj%XclURo2_@ZP(9u+^C{Of+z>$Nl8?PNZ)>Q@}x z;5ftU{pv@rr&r0^7Jgz?OnTUDQ!wv&Z=CtZo1N$5*G!9)(K-Ej%4eG!=`|~okH0b& z-23`d;`EEnI?on%z3=$(?)3ijPk#+rOzmDz)DQaNC+)lH$ihpnU27LDk6}6JW!q=- z>BPQZcl})_gg@WBTD~QF*@qjql9*Cx=fjdS~c=IU{&qhSQf@5;_Uf;*(V>AJ2%&Umm(T zj92PQ*Qe*&T*nIb{&COFIe*#X_Kr*2R)5)X$Ntv0>HGhr#=Y71oWrdCsdoI!>iCo0 zHVI;kJAX9Jn>|&k&+^7k_o{f_vU!i*rq8b}`)1Z;X}DyY#nbKvm)+B(nr5DV7*gZ& zZ-fKU%%%8mR$GcO^@rLD*TU?hsdOzcZqU7H8N4E@POxtvheJ|K&;br;s z{O6u)iuUI{<|j9{gL)#MZiK1XhmZIF|E_;;{j~bt=Xp8*Z{Pp-ZC~!!AB!OAt*$@p zb9J(g-Q%w}rk(O>U}%gI6EIl)^je@96U(e1g}O6G3l@0n|MzwMX2rIP!Kzh#u3hq9 zHrj@j{<*T;?&@`Ar7DRhlSAU`em;HqY1D#lk8W7(t+!JpF75COtyo=sYsc1l zmk%%N|G)n~mp8Oqe#Jz&+AkNs`2YXnfAjSVO~XlvD{eZr+Am^1Ix~IV%)NhqUEe=% z5&PK#BE=T26BMc@J$V`b|7*PQ@xoa!@<}}DkIG-ZWBDWY zG8X=>aO>XdiPGOgm&{;aedUg`axNqZi|NPR*{+nW6BA%_KbR-?_mAWD^S-)BE%|xs zmdxIYMRS#7%WkILlu0Ypf4cgq==>#7-$OrIvRBJ$&wr-f@bAlV`?ul}y|ojQPDE|= ztnvH*{{P?os?FU$OFy-`JgWV&?6m!DXV%}JlOI>=*b855))7nYu#`^L$XfD<)%KCx zIcD+jm_pZW_F-?%wm(RfKJ_Pg3twuS+qNc$rSt!Lz5jf%Cco!-`kk|mpwWV>q6Y1e zrM7c>85o&D1v`GWCb=GZp(|Rf`1JFoE!(%J+yD90ePqJoM+dIozV~h08}@vb>q(#9 zdF$^rIU4`vaqT^4m8Ky19|zdI*sMQ&?z{SV<8is!pibi#P@`0{D)~tNrJAjWXLS5p z)UBs=bNA9YpZ0uRn}4?I{L!CEGbEf(CB5+e6VZ1s+wh5T)wLr9r)>&89$@BA`E2Fd zzv#zBxzDn$`(C69t=u7g%;OVZ_`RsSP7ry}12?EHh)91<^% znd*h;0lql;v;N=t|0kK6{y(n&)4yhU3{PZFLSW6ZN1s3LJYTymVtdahdCdJj z$L4X5vDdxZSNE(xr5VF>x~KE);uym}2J?g8uGxI<)H99lhClItj>f0hDkdNL|6-8@6LH%ej0Nu-2X6b}-Zo>q9#aZ)_{5$C zw|vd-Ub#No{C|6U(W%%u%cb~^KL1>m(dcFOfAP-q+JEm!yg&crLe@7S1&4+M9xPuB zq&uhWVc`&vP;dP4@!#(KzjwcMmj4y-?Xh0`O2hb@Ms*$Ys^9I5?BDpPA(F*<5qtO_ zjlPAU7gxXf_xt_%rqn{8FI>Oo|NmT{*0`iGv@;=Q$w9SU=e9pT-z+PCc&Cy_sFv&d zog4u!3fiZ{4zB&dC|&&O$gA$k^Sd4;w`+&bGp}3YcK5vf|35jY`#C-w3E1%>SIjvu z`Q2KB)6WaL9^doF%z!}!QA^STluzc_LH<|ulS2`Z=-p0b_xc1pZ?jPeiT zOCsHBM^<(HoMUSk_rDxfEQ*i!{h4h4bMmBv^LK=)=Eiv5KDt%QRC)!fX0n80?v%CbC(72aPVBG5+-A zyU!OUEx(_8BVWlZ{^=tUzxc}QN9POA+zquUG5U1;yWhRAk257#O@AFw+ELtlP4V2s z_{Epb7tC2~arCeF^R6lLcURQ!Tz`1kozG9MYhJx}^r=SZ@rQk9Pe-48UT^Zfa_N2P ztABc)mwpFN?mbq%5cDJb*u5Pgf95xU=PtKu3)ng>YIkAT*QFjOzv{?O{_8J7r~UVk z)2ZiK-1V^J@-koTnW}Xz?*5a1Bw9u|=rkp4)UWCmU2gyHvHdxw!2GJqvoA&8`!p>l zoMo9({g(#m8T)_Scz7yw&h{UrXXHg0cm9|te)!=d!w9bH9J5?xlbJp(tT}zwdQwsN z=DCade(uQ@tdRBXa#dfHBWTi9osqC$;g36uGaos;6;rxG`8Z zEn&9j4xG->z$4nx`&;$h>>oV5F%}B@-zD5PtdxCJsWQLw(POr1*|##+`xz4CzG$9{ z&WoNJ-G0$3xN;Oy>i8iFNsN1rX7d)bIIK+Y>dY~_y0O7*-Q#1yC9t{NM!QCvgfx#d z`A%^bkV=ql1S3eupjaF%9D6 z))qJ@%s-s-u$6|h~(A{|$Mv_&Nrwk)5#myLBxA+K4Nk|1mjI7BDGBkgDjA4Dy} z*&G5HE{u$u-Eu-pA#)ALQY#`H4y;MCVh5EdFjqpXH(Aus5UIRvl@;6qxYt8;6cWM= z=Q4wan;?o|Qc{5q2i6Eiht3807F9|MRC&)h*NAL8#Ob{n3JKRb)~@oyloI9Um=Wsp z+yNFrppGNhu0}Tz0fX!#VWH}nQeCVpXS9|TE5H@Q7!Jx@95YOlR<9C?s_TXf$CEZ0fh@4OiXFLH&(p@XJ!;# z0UH|{B1d>MWn?tYn>PI>``-6u<@UPx9f#O~MKReGLtfmR_I0NdX?0jZ%U;-fTL3>CMf}n=ie&yL-Ev*HQ@r7^j-gcy>_piXK@fTn_l7*^`~SXO zxp?tnt>+~|Oe}2TD`v~3UF|u=e=d<9?sqU_MV|x9nN?Gh7XAMbTl4GX^6=MF&N(+6 z5MZjpvMj~o3RC%=Lib(2-|fB(swlSP-rn|RWAgF7H4(eZ-~0W1?HucOUHyUwQ|aZy zYbIW}+6v1MkiMwgyTlRA4^ktpG>T+A|voWz~ z2@4pkK65q@&8IOe=d9oFDLJLN{LAiZyGmbQdT#&!=a*gA_v(SB^qNxtmh4e9sA6Q? zd{Nqp8SFJkNdao=7&o3fcTS}LkxA~YEpOgFY?q(cXZ!8Oqu4lMg#cE@;u)cg7d26% z<3I(M&y0cx&ug-?UsuFk+sa|m+aQQC|l`RM!3q*27DhJ23sf{Y!ja;CDrlnW{Ne<#shh$JeleT-6D5g{^sGvDy*@GIK z0tyZb3_%6W=Gd!Um{LxRjGMV0%7Cjgh!s#Z7iNG8nw#sgFxL+SENp0qY%SnZ2G@Y|y_i8F6T$}NBRT!p}C>O_!P>$yYn1%i^POQ`n~(I?rQVp$9I*dwnkmKIq_VCmGRuodyKbk-FC&z zacUDN=s5&l2n!gzUi<;dQ*dZ-C}rKX?Y-~xn5|}O-`=|W{z&$<_dXwYB&L?{syO<{ zq&lm5|KEGi%+MZoV8R*a*Y97!BY{^-!1lBk>*=o5(v4eA2v@2?LV7_N3(J|T<+@W^ zAhs$vENEm&l6ZDtUh2|S;+0D4p;h1&P?`O9OY0=41uPsU6CBP&hUH0l-YYtXLTZ^tjAlaPLDq8-R>dFYGJoq)dEel?R~3?TjyX6S z$l(c?3vS?n6Y>Ge)7F;{MGCjKM%Yvbb?p&?I^LRzDQ)XzF)t^W%@-1wgl|U$<>$X& zu(d1U=2302<=d`JbNqY*+^1&Y=-KaZ`$zmGQHYa3rkLzIu>am4m#U1z@2&*>(7)6) zQ#knhx4k!C&)BTfmgsig=-{TFBk~}BIxJWQ3i(x*B5m-H7i6g`xZYOsc-2|WS6>|uEqvD>uv?ey2b^vbVon%R_ED-{Qh zq{d$Y0tT!5%YwWhQQg4MXeCgwB|H4r@z-+xKi8z5pJ%VS_xVxxD~=~cCqf*dAtqp; zeeJ_62oIF#TA8BDzAZT)^Eczx*7EwYw!_P|F1NQSpJ;hX>q`lw?8IyUNI5M?kI(;g zJ6q0IdYkNwEBhDMI{iA)rF{7dUoe}w3-f9gaA3`CXo$RQx|2&7QgX0xghVTZ9f|q+ z<=mSs8`Yk@-=CA%HPrx?Qten-&b-=ucgif7;}*EF{94-)sd4!>mv+*;O%+wWQ~V6U zG0f2D#lmt%E3lXcWDg`Xni@n6@3KLH9;2*I=wV&cVR`I;AGm4H!nd%Y;ilq|@=SQh z{Sw)rlvcZKb`aRDjYX_1XHHF>)DLq?gM;INt{HjEVD%S%fJ*iI$Fkrp%K%qK?W8cl z%PYaIbLa)F?#ei!Oh=pxpaaR9EjlHn995Xuk#G; z0Nco;rI28z(JKq~4LG&(aYP7So(oCwvp{X8owuTM;px&%V?$}U|F1V!`_ur_MWG37pY8Y;}S6RFCi~8@s({+8^&%Il* zIZ8th;$edzM#jw(yG}v^8`R(yVG28v)0GzH2@b%3Acq5MRJwcN-aD(4;Cd!4ueMYj z>_bh?89TYbAq5T%5m4Z99QFq5y|9FdDb4rRhgIx8T}ESk`pB zG=pen0hLN!rh7o^IY34WWLPn3Cz-`gUTpy8N`yHa$caz_E%5+*gheX!K-Y}kTbo`% z8USwU3JGRb_U>@QudHkkHGIA0p~!5oPkWjh8g5!B>;`8aP_lN&1^Ip3E$2*da5${x zo2e_Yj^kICzu<_MTX`cfa-BO;yEJYpTPi zWe29a&oW)Ad-vamYg3~5WPWTDKjXh#XP(`O$Q-E0W`R12lF_kqVdn7aUD#Tizw61W zt(m#k-_@DMZmvuJw$J=)+Ijt0Gwr>lkWXobhz8R8Ww*iSk5FqbAyI2hd_o#qePqKhV;mlT<>1K^fkBHu=0xD zZLsed8r8%F46;8-JA8$?t5;2+c-qw2Zj-j(DL&;6u^(v(EF=;*L^z)a{}IoX`tsD{ z6JM2(HPo~=VF82e9crP9;DiNL3wC(pMNkHHJHHX0NKDl?tk}HxjZv>k{!R6%(9(28 zH6!EZUHML;aMx>yS^S+Lc{IPhruNvSna=OFPX-5qK!zV9Bffy5Wb82DlbiLVA@0CA$xN_-I z)xEENSSA-nK8H30q#_PXIK!3~6AEk1n(R7IaB!2``u)G4?a^dqg#epLEry$AXm9xWvHFmc=HDM9YGEY!Ir&t>t zTZ~M}x(W&Kfg6y40j7)xH?0Z;b>~1_j1~D;`Wg}<;mw^!H&D9zA{J&33H=5KW)6;N zrVh%hn84Y3g)$@KW~(e6S7?)i!(?$o0-}&J2H?$4ph^yG9KKdrC`MhR;>_F%E-8R)nqG02-l&(!IG&d^{K{5Xo(@i z#mKnXdEvWRptu69>`)UI&_U#LhhEU?;REO=K0sY3V!)wasRn*^)H;|yUIit*y|wk_G3k6C&|1aIn^I2)m6wg5u_sXF_n*MC_ozqFNGe3`JH#lr>3^D7>8 zmi&IZefbP!-KuqGXBa+SU}GkbA=bEybB3vR^9prXvJzxr}JcR@UmM&u5I!`@FCFzWeeL&&hnJ@|hdm#8=F2i1f+RfR>Cb99Q-^u$=z) zbeTH0?P;^GqTiODPFwnYZsm@I$oU=OEF31y4qtApa(ngWjnDFVRav+8R+m?Ox#%9u zvd`}COaJBlHBXc^t7c7|4IYhJ8N58LaYN(rKY#E4ug%KNKD}@G?p?bsfz}0=Tn!Ch z+Sk`ta?w?s_sl!n#y0U87e1JVI6^XU14Cn>@QLPB^EE;`(&i=NN)8JivL+S%GvD)& z_vPCB+U#9F9(7-KxBqofWOe$nteH=jwaeF8+^v4U_vKUl`pT2@GA|y^In=_ux8ZTs z`>N}^H!YmK|IbtX(|cQLSfsob8educ+AX`m5?V`1UxP~SMTGR5BhzRY`F)a+|zZ{L--nZ7PxR^h_G`HIm0 zY|pmDZ_1xQ9WznRBQ;4EKd-iUduyw9?#)fAvUNWm9-Vy|w2!5#Q}lhug6sNqAG^Q& zd2Vm-`hH{4r9N5fpf6t0n^HV2Z$4GLP{72L#(RB<7A)0DNH@NS4AY7TtJyk3#i}VS zT`v3FrX+FDHi&?g2SmeTCW3bGa69nsUwNbFZwU-<%HL{^oW>g<#{Bh`&WIFD=d7aP*6q zUd)WTPm|{_;h6JOvufJWn9S4D_0OAEs$B?S($x-Mr}JgDe{)^IEH7uc6Ij?pe{{dM znZmnU#>DwzUozk9Id&g^rHkf%1a))Isu%qIdOdxr-S0P>-D7{g2)uUp?%ZYm^XDz| zogMb6#@5z$<<_lV7u0XKu{qs;8`HA`3+FQX3mvxEo_~KH>k~EKStbSBzq8D8O1Qcz zG}88~L3Y+phkw@XDnh;+H6MH*V})D;%^>D(y_9*kOak z=`#~lL#kDrU5w9GFncZ$|3c34R| zzd>>4UyD7627|_jv$wA=XP|tZDhV(`iG_=X13;zoz@;`N~^VCuU`C z{Pk2ZQ$QTr+B+8Na3CjUXDnM>4$Yi4AHMXJ9DV!xSJu~ClbmTF9 z#oIT)n#(L+9ADnN_Pu`Z{@lFCy_UAu_G+KrxMH*N+-)gUzfSJGoB7{Zc)F~0eNolt zmF-h4ntdVmGBh3&6)?!Yvn|vZX0vDGy0_JrRm0c%X74WhetUn~-*Y1UGxqC<9!vC_ zdt+AJ|J3fF*DRl>eQgGphoFtL6^zbC()&6?q5Xe_57090+iaQEh)AC|@f|bwephW@ zzVq^}?(#}leV+kp#4XE6n+j`X$_R8M&S0*dHg!|yf}6|I!%paIy>A9{mNGZT4AY|5 ztE8bRCZPdyv=OwZn&r%^g;S?M`&$YDs*N>G|BL+0C+&GU$6pR+*j^5f8CSi%HDMJw zBI6#|0qV$cZ|;WWv@GX@z;ySUtdgGhHu-BQLv3Hc$HH>v)q;s4aEo{w-|qgtlv9{X zJ7L<)a}idX&&=5bOE*&93)XI2wb%?A=8Q~BS@Nn9f_MG7w*2z6L#L+iZT+7ldS>CJ zM>i*5vOI0wekut%eCn_uk(K3))V9n}SX!EJDf#a1%S+AeXNK8reavv&xNuY7w`p6l z^Yc9yaZHQdlT?_MEo-$R>{9|{;~)bg6R(0o!Zn886wBonTiV7}*O%`~{rb!)AUC|M0+G!f0^dXGh{iM_0fX#i*TTXf z;kiJQC9k@|``4%Q^Sz|EfB$mT+^T!;s}{?by~|~$S>59IN#6Zv$4yN+Sf{hWp`MX( z^FO(Ju<&^mmT>o)?Mv_NHdEi#ltk~hvZ+2e<;y#9`FUccey_IQm4?{Uz|eS!i79Pq zYz{P|aa`$j@J~GRl>gnm0EwoZif?tL>Ma*9Hr@NS_4DPK$Iq>ry-Py_7Gq+d(ba{b zQyM|#C#cJ@@PMscdYysA2y@?&D!(xr!Vu%dVBjFEVnf{R5CH8 zJ+(Xu>#wZPZ1lCRx)h#YtM#|$?6kV4QAW#^_x@IGU!Kf2`%eCDb7)XAGCk$ym@ze9 z6_(H*OyGX={;luzJGbZl`@5|)Gd)rDjE%3J&y#vkiKRX7_Dr=cF7n3kx#Ir_0QT>NHagj zyc2%Et9|+U|3(;cW-_;U3>nmi|=dH3V$_~+?pi0cwd_^EU$9#fV%Zt`u9SEzi}1E z7m-Er*TtSG>`wjq>`TP=M$g~krDaQur=9b8uJis(TiCsCAEteII^TY(S>F4XyVH7h z)Li8(z9j8$sZ%Nvc4!K`hj=W`;Xuxt-dJer$tXf8B_~I0u1oJazV&-)s&>r1-!A_j zPCp-Kpt^Ur!OH)O&Uyar`waIV@``SU2G9sq$Gp^~O{qt9{Bv$imeW5Ic`fLN{y%f1 zWUO`WnJF|CH=wr!&T0xAHemmzqpf=JQc=Rz)6>P<()WG+`T5cn&$UZ8-Gl|cf&m|> zq0#V02of|7pSh0ANqxGkb+g*i?|YtnDB64)mc$ec+?dk(WR)Y9!*W@RgWbV;ODXAX zZ#C6t+dvFpU}Q?xQAjX@kC1bitUvJY-n}jP_4%KfR#-yayTFf?=j58pZNWK^=v+|7 z`t9BA`E}n8Jp1x@x7m_;wXH>RGv?*oVA6l}M#8M02VuItLW0@1W9y)!Y$lxt&e{GB z^7-`t@$TK1md&+|TwBerQxiGufTVZ&w5k5ESlI4U_z_$aJt@*X?6}tQQuD8wfv#NWz zrTN=Ugif?cx}J>(_Wj-aQoL1hy5W~k8{hpa?sxsa@rCENU6Y?LOFui;=9p?>6(mVC zFf@J?6EKj@FT4&N49tp3@IU`AN9e!2h}!4-8*=maZ`$VPaq~{fdd)8y)-}(T)cr;@ z6f7w!c5ylmco=mA$=t|NV)VvTJXu{C#d$UN*~W<<|Rl z`S3zP3RJ`u-VQqp5ouIpnRjcCb?ttGH=ok^bNp;uf1c{PJSX$*%d6^VJoT%;tufiS z)_@O@%rdkX88%9{$!X>zwVY&Mwq|JWCK{#?($hf_3lU(J~kSNv#B$uC3W;MXlP zZpFisXoedj9ZS`>ughl4_&@FH?90h_*_-0Kt*kFedZ#Zdo>snW+qL^&c)Bf5 z=INdB{RYbG>gMlXhM#}o@NH50*%MV$bmv{Zh#V0tP7Vjo)Iw9#l|2sOiK?ezo$>H% z;LiA+4V$b%O`KUq8%<-wIad9B@ve7ew!8oSJ#SkZZbhC>Tl$&1c*@z?b{mmni9>{& zV}@vU1+?9HWe#+HK`(>ll3m%;=FIqo={LXG{_ZYcdh)ICB~T?CS9)iTNpaWyUtbP? zgqPe4mNe`)otCl@+T>dy3Y&M>lzVfle)Lx(`5BhQMcGwXKNerIJpb}c)x4=z$6)PO z22i7q<&4+L8_**8N)NdGn)T8*^Ip%lLmN)-Y<)Q0IJo+COA-H6q-jLZ0Wk8d(DjQB zx?E3$|4a|ww(Z&!y`2e}vkVm9r-z*jL|QrZLSG@_+Wo`Ukj%Bfjb)o?Tg1J;Kelbz zZqOdNbLCb64PhPaaBn+^f(C}hgDfm(*d}j;jzvp^H-Z)ceK;2TGQ2IP}4fp?S%ASzkg zjHjneTjdgw7dQNN3_7~^#5*09lS=j1y5B6A!`_r~T*k~&c7H+Eub<7cFJF%SHtodM z*}mQB{->Hs_uqVkSiRGD3N#yBTr9!}3WW?W=nzcL#%MUyOyWulAn)FQ1;5@KxUBnC3cR-JN;4Kfl_*d)fjTstO5VUPqzP zzF>(1&*7ei8wHx5{+#uf^S%B3jr1<@$iy|NUzDt0&Q!R$`B~YLY15k76WsE*avTUv zmUvN+x_v%8JQ^BiRzs^GjcA2uI@;z(_oOUUlkbj8QMVITuwJ-A{AR)P-h0W~eY5A< zww=y~1RSUXi#j8<;3NxZTw`Tfeq!b6X+PGViA#ApCm7ZrVqj!?3_49n?$kO+OumW$ zmB3#vw8g$`EVaIL?~qK|(`DBhFoOs+aDRdYwpeY6sj&>T9)E0u{b_Vday0*^UMGx*n&c z_b)t^3G1siFf>+)Nc_A%;q^xF{vJjqmMjN_s%gBYQw+N#4$CO}e|ezwE^}M$_kB7G z#3p|_&hqzSbeccR%~mW34dTdt0+NoT;Fc49=qp7g#{k z_!myB0*_X0RRlGAXMbP4qh;dr=<+rGpOz`e=j=J2cKWDz5eN`H4)L%2+()gKqcjAnTo7d=0JA#y>1T0cO zE%u&!t)P6oK#)}~X20>@*XQT?@aNyTy1oj$=-@5u=gV81F6dkWr`)=kh(@G>!vZzX z@W3KT5%757tJs9&^J-t-&ySwIZs+#h2HrRGFCB`Ue5pFFsG{mylji=LFA$XpBa^AF zLPFT(vx^Hrz6s!G+HSW)wf3jD|59J;s$X5V<0Qg<9t-C4UT;_Emo+<~BUQc!TAzcG z)GG&v18exCJO#lnH)Fk)dw24_iqF9>*B#&XRr^6__p+tkaeEZNt(r1K+ml0J1wSL> z=3g1=OF`pjjZ#7{G<{zEKfK#)nfCjL`QP6CKFe?>a`Np<)_(C5;wrxMNNKHI1TQlM z6i}Aj`08%>v{Lrwl7IdAx9&a{VAhNOc;{RRXh>p8C%f9$_qSidhe8+_nM&0a62i)T zmmL6kw?Kns+uK{y_5MG-{IV@{_OIBwlXLpMygRhy-*FkUo%c~)91n7FuC*$tZ`P>A z@kOL9Bn~>-^d;i^x;H&24ah@J{+iov(ENLQt^e}1tqUUOS=~}!Vm`krBI@Ivo0(0X ze~ZP=gPZ_qGfFu*99W|`D^L}b_7?~t_0Q{!_ZXeqoc8zJo5HhuV>4eZ;mrKHi@9;j zAF1WCrnY+@_poN3yYj7OGHfOY6f0654hPmWN(G04O{;-65Ng`g&EI?O-Mbew82%^! z+?H=8frQm#TYD;tLGyO9va+e-ivJi35>EZ^GcMTuorOaNv`j`F6oU@I zkj}fZwffTSdkgAz-h2wFI9WJCLLV$_m-CAMc`E!;?f1La%kMq6eP8+V@B8}tS$4HA z=Djz+SCRbd&U3r#p!VSHs~}Z*#G<3e;IUa#+OF^nuHVCd3Sf2Zr#3pdHMd| zcmIAmJO8~r^Y6wA=Y)oahMNom`JgloZP0*9L|x9B56$v#%vp+0PEvij?RMVl>yL9* zeA>2k>(b5V?W$Mkd=peK04>*K=-3X5=?f7|hCA!ego`;Fe2z;6C6+IJ+qP8SzPCwB z`X?eNg)~g#Z~puH`|_5|%Y2~|W2QSC=Dy;4Am{3;P~Q6zTHqSws_2bPyUk<&y{>)v z^6lUAyFtU;!T056oZJ6@U(ow$=WfjX^FKgS61I@&qgaQ-O^~OqT)jH=-j8GE(@K9@ zJUeIo-pBCb|2sR2FE_Dr2W4kxM;<@rvLKT6m0H5JHM~pifgBw$^T58pD^oV;*hhOS zCS8mEk-tN9(KCPFms<|6?U}i|5>$jTGO_UW9-)-1OrBf)Zs*NEslw`hKGomecxs2Q z^AW81eS81k+h4BN|Glo+lr99?5Aj3&bF*&_$n-`wfey>lZQi=Ajx#^r2la(#99CQI znX7wKId1>sFRU;1#Jq$k;dkec7e<~aDw$( z*;}pm3!MBn=kBh$bpP+SjZdFWhgD()9jr-LPfy?XW$DX%)$e_?udVU)o~|bw*qL^I z-rT4Ca#=!*OiNi=&Uo#<0bYVss&--Xe)GHEk9;fntsH&1UQDjK>Knh+l}+bXE!zQ# zW`_oc$=pYl{1sOFBCoKm?(Z+3)CUtz&6_t*gmLS_AeLK7x{;eys=u*=!iVRtrj^Bh z$cB*xX)I;$Zccw!l~cXz?L^&|=eFLybZ&3!%zyJe4Xx_g^-*k9^ZHFJs~-*4{@qRt7I$_W%3;fAP0= zm*;QOXUbb&^HlpX_)w*qn|$>G3K#qrEZyMbmY=hcE7e{cT<;AgV?nik2)zqfClc3(O7gzzoc z3f2m@2ftpgKOg6MAbX2u*td7@-koELFMfW`ckSA>s{WB#9%(tZcN8XP<>&j)ojaE) zRrP6(N`V7ts`w_i?U_4*AkPLAGyUeSSB%-5m!7!d>9vyTh3YqhqrdS)9b5dYB!zJk z$E2gLAC-LUI{I?b>T7&bCfx~pDSD&Uzkqwzh)ou)9Y-!*zITM=i6UN zL@VkFGFZNe9Sghnx#Em_*o-apZ?A1heZFrJ>&$=`9NTIRPAPdcNiuRlPTs=sn+ciU zu6$eb{l?bs<;$bP^8NZt(h?SEb_GX*E zvog4{NkFA~$A_v7TM~{PVg5W(SJUT~eSXobfElZu;e%qJebg18Q`5HZcrB>lu)vRX z*S7b%mW}^r9a=RNJa}N%Sa(Kx+x`o=IZrQJZ{qyDSpDWNr}di-UNUUGsu}RzZX6?*kDaCvM&FSy;hg0V7M2L>kMH zHgD-|Uqz}W=lU_T2l?gApWtV=$sn^nam7>5)|0>0{dMXu{d>4B6VbnCWMWwbYIf!Z zK38^VaA4-Tv#F~1=GLrE&p+IPn{Ni+;<7!#$FsG@Z=rkS?Uu&GEt~u`VY8Ru8W?$& z`m5%Li0==Czw)mM%wG8UM*N;{kuP5yx|0H*u>&=UUIi{NnA`*D(5zAyusz9gde!rn zK1dovgMKXjzTm6=Z}B2%r{%AC6=IkIybfK!VD)CU;20(rj-IKY z8HG#V-`|>1SMmMWmh$*7n${DdxVxk072kRDWmEcjum2@~AA^=J*M#qX&H&F3u=R&ZD#1@6YY4$Jep|L1&Z=IL{_fBh<71zx^SC?M+tpn=KfHLXD%|08}$RmwOvv6El+0YO<*KAG_#N7Q=CE35WM_*rh-2Cf% z({F2b%D#N7KWqLp{Ta938wmaAz-<<))j=c;YleO}Z*Uwr$QXyLUf+e!|u{CsKV>9l2>YG2-`KSr^$i|NHlTtrKU|W(GxjTm3(>v-S8Klanp~YV{zyaiESyT1x9Mm*b75&x`+$pm zp)K}rk?tH(;a}&y)nzUBqW0nx92V3tGH(8~_KAu^!+{x`;B_Zr&*WfJtO_?4o$D(( zc4;PP*kI*aQ?`g@U#>j6`!aif%yiH+!~RWYX55)|@6Vpfm#OFHO;z);n6N#b4P3*6 zQZ5U}l^G8H{{En@5NLsFR<`iG6&s&r{(7`SdGeIpP?~Iq$ATcZ{N>nU*6`| zTigD8GP(8Ws;Xe`MAq-1lc@K;D_!rcUwP7->pz3dLb2|3KE7%S3D;71*X?5AxH1hk zjgZQ6>ED;=Y_9s58ee3zU)g+n8X6ENs67TCDr!K{NldZoOSA%Cr}x_$n(TTr=@qyNHG3$|47z z!!kEEuL-f_{{L>;r;3}OSc5O!jyJKl&3^YJC=#iD37E_j8yovF`u?x5sBJlsp!1IB zq@9^@G5lQY?y{x&wJ*J2f(|7G9o@SrA?m)LKIqWkzcT!gLxUB>J1kG1@)hPTp8WUX z&hAUc<2Ji&SQBIN6P&j}zQ}TW@UUIp4|G!a``Y)_zgbT&YLJzcz4;B)q^o|nb9vgS zH3~>GMOPZY)5orzD`os_Uzz{s3O{FvG+(how9$9AS*mO2&r@DYC6?_>IN0>%h_L^Z zTe;GWg`lb2@H}zk$`dpVrCD%9&^<-8e_q2`p}*3@AX{gy^K-+Ttpd_75iXHu%GAu*Y$tb zmmC!hUlO`HZ0T{iY8_C2yYln1rQv%XiN2gud~W4eEsgle92_%DJ)KvpF*0pc{czXX zd`bSDZSNcwa&6pnh-pSL8~j0rvH0$vgGh8W^ESNgMBkBHEVy|QNA48 zpYOjNG@L!vIQiTri8e{w(=4a6mT`*0*GewvViH#OTl47VoGyp2EG%bMrGuLi5<%c) zD(}}-n%w>Uq;Fr%dD|MVUl+9de1g@tE<2p=>!sZ{`|kGh+=yEEt6stDwcD4?G)`ak z^z`)Qpj2*}eT^r2DJ!>_igqgBX_dngpaz%WhRebV4hk%w0XDszS-Dk7m*?3{*gWm5 z&-==gf-7~8CI74c_KwA1)vLHaKk|)kes7t;;-vI@pMIAD6C2}E-qm48gVyTitthQ5 zyZF8S>b5N1^&#_ew{8oKS+z}L2X~haSA@&W6mAv+?&@!)lg^y^KTo>&q{`%!O=qV4 zl(Rmw_xZf)b35ltYyYv}=450Ma9{uvjd^Mdm{R=h|87~bWXXw3OTAP7|NHytO0fUd zpHn_RJ3E<`TWm{Y9}~xp;D&~o)}9c0O>&5l)_K`aZ5@+{YR(yD1b~=E8v7B4Lpm@8% zSLFr=2ksSf4d&l2@X1>HRr^-W_!2$yptEdpl4$PHBq6#A%SB>L!@(1u<`SA zb8i=%GH0^W6?k%a@8V((g@y)supvK9?sH5#+A{m;^y%}QH=OCeuNmtO@hKyVz>6IY zsi{kAJ{}bh3J-t2MQHX}wdid*6CXX=ES+Mh>!HRZC!mnwp`S@V<@%B@T!@o^-)ST!hhwM?7Ne3=2c2B*zDYfX;m7Dj~&FlUxSfEh(cI)*g zZ@1sin{(EXDNZoKYgzb>Oc6$wU8^0UtrN?xKYz9p76Tx6t!a>ITxKUKDmr&<*KffW zTNoHO`w0gzGI1yb=s(C&Kl8Hr@zZJCRU3uX>{TG)3kqpQ?GNvEzd!ZxaQo6VYkbTr zemNoWbkc+w&iL%P2n(Wn@Z=OI#kvBJd)QiQPZTIq+3ftD>aurtGG)x|I=Z?h%`{Fw_2%Yg>Ga~B zhHw^+8I`Sb-2@#LSaWU=PAjjPZNrU-sBrE-Z_@3BS6|io{r&xP_4zeH|CcOVW@WPY znm#w5n#;q6L!IHQKQg~)+4m|pH0%e(oV5PC^<{_l)R*nz@k%SKudmr zzv?%G|J=z5PbB_5zQ6aroqszlX)0V0Y<%)0W#{8QYpWvd?+OnJ8JW`L)R#N52z)4K zEIah&ySc}IL1_M9WZ9#+;F|I8Z@2T8?%8uE)7-6BO7(C%e|c4}4~vbe!+|rwm$o!B zFx4psq;EZa>#yskp8=guL_=TyeK%P^FPQDv$roERB7b?VlhIyhIIn%K0WTLHQp{(B zKG3iK*?G2ILE-@b_FfbbPJ6`5t;`q_-km$NrCvvW`Wvf^_U;6xY zLSH$JIwRhe8D{=pal~&1pU)gnVt^)#3yVO>@oU1(A0Hn--E>-S@}^Bjk-JJ(+Whr{}PqmiitYz01etcT2{^ z%}2$To)k@+e)PpKDa|BH-oK)zjMAxg^mDJsoZ;&}&GPl8lPQzVJmfUmSwCm<=_iWtLPMdU zL71`o=%Rj=?w&RG1MT$I{ri4->C&ZDFF$-JkTghWcyoXM{gZc76dyz~GNsM?y3CYC z;6ozg*0hzjEcoH*a#* z2z=mVyk_vZ^SXn&fWrZC7AdhG`wGtzL1c%30=Hf~fcy9T zy5DanO`e==b874LIBiu`)lH?Z!whGiExH;SesbsYdD$_~85r|HK{n0itx`k7dCo7M z9M{Val`KtOrGr#zGqT)LH^{qV5jekXNmEAoox=7zWw&#MM2@Tq-}K&)3XSSDhNRG5i0DSc7P9$f{CH=Jxfk~^H<&}= zK{Qj@?$~?xV-@miD!sNi3+nnUb4o1~Ow(LhWOvJl^|aia$%+S`U2D$T_jSn^Hl*Nv z&k+(9c4~3Ioz`-{xkgqSl8^I6ZpoO~BX1w~@89?Rr>Do)t&H#P?VZ}Kx69+%xw+X> zo}vVDIIC9nnon9pSEe~WJE>F@nAY0tl3uQ!#wjXHVm+_c2QZ71K{ z+?;x-g;PkzXU3w?0KF=ohWRf2ftLlCIDT{;*v@+O(--xfMXT0advWQ(6po$NoBLH< z+Fn!^yQy^jngB{aut;0*n5p{Pn~8F^RVQ?}-%-kHte^UB&u72C!BJ7G-p{Ih*eYJO zhw+`u0beGTGj~|Lw{j>vn9U^3Cvv)bcE9)9(6a~E_!qmYo#K7Iti!S+a~iK&jWQzt zT@Yn@`t<3fdGqpEPKV4bEG(QfV}{1cl`EY#tkIsb@~|w(%!OWaITRigGntl}*#>Mg zTz%w4w2Q|Con0HY`@9m*eo}}qt$~3lR(-*;gjr{wrHQt+w!UQCoF;z4;ea~}$BdsR zx)(@uC_J!hoV?WZm&lnpPTdzhc5SQpzjx-#NmAlj=;r88m_L90`p9cL(oAK8*Tyfq zW|Te4tzS;}a65ngth1I(a-s?e&jRzLoEaGXxpS$s_6Z($#*cd2Zb3QF02f~Zv*_*CZ8}Bt~w=}Jn6_1KM!cl0gtyG3JwR(u&Q(^ zG&JOMheWLTb?9IG^C^ysd%rU+|LY4k06Ucb!Cj8Lc+5yp;Q5f1L>^$ zbXQEXubZU3{B%~Y9iM0Cl%q2HY zt##(?!LJ{xDhogTRUCXo=7@;T{OZIK4y0VqBJe?&ktwb2)1nt@OdNAWSFGH8vU_&_ z>0@hzl#P{CJj8t8>rVc)iMhY=5U3pm_nkt6Lpuw{jM9$Y*#Zs+qM6javQlo|iaDvd zMIzS2pJ#Hl@nuz0y(?(ZQ4_3SZy*2d?QL~cRn?@e^X}f=w{W|%d*76Yhuh8PeCkDR z5Gga=?wj_i`*--&TRpdz^L9;+UiNL$g$D{~f%d_-vHt(>`EXP%EaBV4eV&5oM&4#>=a;``UGaYJcP}@$w*2yG_gMEZFm7)6FUY{acwh8J`5q_k zX-_>puih->g(t)_Z+6QrPe)Er7km^nH9gOB^Qnnkm!|84y9u6+OG4I2z*nq*G8%pTyl zRnE4`Vy~MT(>x)CglC$Ww;UN7%~<>9t(>&{`m>WSH$^ReGhvEHChUe}D~Ek9?dAZMlz*^R~LEZxQQVs1P$=8?16an?x0;lN!`(W~q87gPv1 z_|El>yHgWmmNr%5sghGu%Ed4Dj8^8anRpx}rT^IOP*bz#%v|g1`{kSNvG;&tukxym z2Gc(Ef<;T*rma0Y6;$f({Bp!#r@3)#&|{}w8Ty=?#Y z*YEvZ4xG7vU*WI&0e{wQvbtBrzT1P!*~gsAU@1Mp?1O251*lAb#^M5L&KqybZf?n( zye4X^SD(DS-m5z)MxN{A_CD#-UN?bTZ-+xu(~r*%=2cJYervFt`Sb1S#X^=p!3VN7 zpWeUEM)m!BJp(y~BRP{=pD#ak1|#`j_{$QuE=E&3Y>mL?n>xe{{SFC>K*`5t_u zTsv8*2O}6n#KcZT=kHa`3jF{3{r>o*L1JLKR@^Q-=VN@veA>&(09-uAuE=o4}TGBm!^zp%SJ|F7azkIjwD>}J&otFNwN zKi$SFeeI2uBu7P5gXYY6V#{q=m|Pybn5k8nay79%;pT)X9zWmy`Nwja=}CISaTi!k zA>goJqr?4uwW?}rZu6?&S)M$9KEK9OOI!QvpH-{0YI}{_yXPo8U}bchQOP)0P4LA# zrq}!;CqJE4Uvll&T^XOZjrx*PHLm-62^LQ2(Liq-DNz!;{-~+VwrMaN8#fqo$B)@h-!yK5D~--j4U!N zPR>71m$D1^)wne@%sjt`%c`m2KW9X4S>@x?!JiJkV_N)KF>SJrjeTeupRWO0j2{sB zalZbaa$#X1sDrn1-MVRS-n=nW3fSGxJ8dxo(>oD`glB%SuiO?$bA#)Z?9WdR zob-CES$(V}BiP`#$NeQ7XqDcJJq>5mCO0)PadL4@I&#D%^Sp@L?z?*F=jI4*zPaZ9 z?V$XSoy%6*zBSn!&U#{A-LFhfPtS=jV6}lpadB~so>B&jH@Asl2!EqSOv|H`d7(7c{;{eIEUelA;AM06jhX0o)j+*J73&Gy3*CQnya*H4GI z^-pX*Z>P=7#v@=_yK(84x7+WZYL~CmD1LV4Vso69!nb46`6uT8`=b8x^78(FnLEN+ zuY?HXetB_`H}cnZt?M(FFs1Q&vc7%>A8T_saAtWt|6Jz-#Y|Z#GdCwhK9*Y{Yp`E; zh1le8{lDUKS|Y{HzfnG1?QW`f%n&{p#K6Q+GSQ)`YS*9l|NpHw(doOtJwIMkQ*$Ed zto=z-r(VrE;B$BNGAB28cj>(wqtxLwCc%@Wy{>^ z{=SYs?J6FtVw`^N%I!1f&rd%tSAAwio>k%@mYwhS{rxdyG0fz;PnbiH~O)0-u>CRu% zD`~v!?o}DPnuz`Gy-%`c^~qYR#n*nFy2A9*m5}o{ZroVrYE`mBTl*gmn%!(c z)efj5?Q4El<>}L>zh-5}*L-aCnQ3%X<1i!BJ%JCi8)ljpWC%Ap2w8+CUX}}=o&M5k zQ^sZU<|~gT^#{Kn&6~9EfBT6?oOPT1`wC6%Agnw#t0diYq* zw(3Q&{#B_Qt&D9&PrYh)y<8KyxyS123p3M8JuMB}S^uadJj>O%<-TAyH+1CXsby%S z5OQL!aC2atwQkodt;L3>TCY3R=Upg2UGrjLd(WS1eD~^p=l*=N`TUm$*Dow|o-LYt zBg7##;O*ZJ?e-$3w?Ls6D`p^VSF^*#)%E0Ie)~20ALh@Ut2=Y%%wHR`!h3V9i=K4E z6dYuIHKnB?oK?p8RNPxG&oT4-@?vkjNRQdD`qKJeTqh#o z&eqh^Vw!q-(@suSm#&mic%Tcad%fqC34SnaJhPB4QKcbBVlIUhco_0LQH`b*a>rA;y>$_L=zA9aq z#lX1vTFH(!2L{eHKVEHlb8h{GZzoMUqkh?|s)cy1$|?noP|j*w={8MJhkJoMmz*fv z%?DmHJ)c`XZ(sHjsmj#t*YhteUFM}1wZ%hk&j+W)^{=81znjK8v$C>s5mTlX!?)-5 z|92`z{|{Sy?MjEF0)UcP#^lAm!p%asiGsS=6Lgv46~UhHGCWWD<7 zPFu}mM)#$zC%>z!N$J- z;g^5jwRBxwM!8qc3Y~o``py2#G(A=Gsr8~J3$$Cqz{v6^!a?`h`fJ+3uQ#k*aP!nu z?XQA`o72u3&GYJA5UhNb$@FUE+f8A&dvZT76v}npmNmCgwP?@h%MJ(5SijrS)o`74 zjoEHH-p@Xjf2~pl6&;KIAAKn#n)a>g{tM&`&QW6D`0#MMcSy(-n?D~8|6=-o^XAP- zGiR=pU%fmudxNZHk&E7r2Tkw3f6%iDM4BwPbaVM?ms&F!jZ5Z<-aTA}JMPK)o8{bC z;D0_exa{f+uY8rShtRDw&GMF*^MJKn4R?&^_?u{Ml6mU=`2HD(!@ z-U+<8zkmOqSK<4MqB=GlOn{vQu=Uo~;J0k6mgTWU1zk3n-KSHWw&2u?O~r01R=eL^ zt^`eL+R1+ZcwGMb%Q~}?KEX6zp&DfY1*S-K?Waf29`9XlyFPwRjG@^{i|2@z6o^%@ zykS<=&iem#fxA2MZf`rA;nnL<{CpX+o3mZTf@?a(rdyYkoj!f~GIx7h z+o^8-eJkWw%NotEdZih;DW!ACk|kSWXZBD1aeeyoCx7pz_t@v}TwR@IyLtA%ZQGr% z&fc0B{Mk+QWa;Edy}z}!wVzIOmz!w)eoyfIg(Y8KT}|2;k#b^!;-|&(e?5eRU%PK! z8@bu-+Pc{6JInd5{7UM#`?X@?_m|7(pJJD!V+PJ+|D_&8=<4xA>};syDZ0PhT-N?y6LoqT;>y<-H9vKSo(PEU4zba^+^q%5BPN znNvgSFU%;|e8=9}`B0N&#;v{gZIGti8XEFBu3WhSnqXeKXpz&EU#VHl45D)~J(S&g zCT!SX@blH`^-nH&>*t>MHv9e6YOXtSI>8(a;iDFt-)}ZoIlNxA zdflaR8HQFy$zZss5;=odN$9Nb|`K3%w>{!OMAd6*Y)S3Ewdn(=j-P>FJ^|zQ_j7IVEb5}oy7QEfQ|L@wG-*30O zS?0?K7<_KC%2j%h*!aC{-o&#vm%6RyUcThig}6Y}d3%M1hTW`JGL%i5-t75&E_y(C>;XgBF7{7DL=x;nR|JqZ}!*(jpK}xQ@ddkS{AO@y+VxUsWuIVLcLU+pa>DDIe z-KMU5S%38C(O*g*`NWwYs6<4YKR;+Gxt8s0$OfOOfvL6=rS1hf9lFUdcb3!6yt`IA zAGS&NoMn*u?z-0Z`x)c&FQhqEOw8=J|F>hGUesfw-u|oSR%n^Vaj*SYnlERpdU^J* zExirW;OVP&)15()!TVLy`d1|EEDw|CFRqY+H#R^OR)B-=)?noc({!WN zy2bUyL^}ex&)I`26%Q3rlfcWS$9p6nxBFlJsi*GNdLlXe+w`r!yHh?d6gsKBD!zG_ zztavMNr~nCI>qT5d{_@(u}aE{TH|G1_U6L=%a<>I%{Mjr@bmfn>*eRqoH=vl^PInz zHe6mhYuC!A1zC*$L2YmA1T)#S*LIiZ@9na!{&r%C=j1QRTPH7j)xBMq(KvrU<1P0C zyi8`hWB={Zi|?C$g};>V>OZze`?@cynl;rwy?elSDso@$!J-DP4%?4Mgg-r-o&U;O zdDU&dc{Z9WSFT*tWMG_jreNc;cac7$q z);jF{so(GSuiw8qa&wyGL6gZRm%Q&-X~t9WbZWQ@>-8&Fd_-ezZ_Ay%V&ybmzFDBIm|R3~-;HzO!C= zH_g3I=Hl|eRrglN8ouB6JMZz|IhMt4@1Lw*zwgzB$gPW0_t*Vhl74<()q~ZEm)W+y z*k5&?_kDP8TrlJ2{eR!)FJj8g+O_PK=vPp?abI8q*R|G`mJ?GngHtXpa@|z&GHCA{ z=VUn>Ls^RghxmIQZ_^x{7#RPH-khBci&xiwk?SJ@9Niqfq{J2uU_%Jx{Pbq$|Y_qS5C~H=NB0n zSv9%G;PV!nw~7zk8W*Rpda_=+UsAWnw|`o;+BXx_Mkyo99@PcQ;-YlKYHhXizc!ZL z77KXYTWYfM>-j4t=cY}Pio6!|mp9m4`rTKz4Qm$Wem%S6_%SIN#V=< zGK#al$|43X)kMUfhzC*%}cIb40@e8(MEYe z)3o5_eqVi(>#lGy8rxSzmWVQ@Gs(Z#ol^R{yVv{r{MkB-e{ZQTbX&}XG9SReXwI=h zR#03o=0@7t9IqQ{f~+>6xkBa>CXEfdS-#1xpAf!nj=`$=RtA30di{*h-uFZupDEC^*3`*=-PUq0RCyKIaSY706Y za&vPh%lHX?;AM21@$*>q0&$Kl-UouxFP9(DS-tJm_oJ>eo`MPslNk~fyA?EhkXv1H zq8p;tZpyoB1zPIxJSjNiVs+-Vq!jzPQ}sFn|F|DL9xr_tY3YN)0@gQWyZ1Ridp^H@o%NEXOFeUP z)`WzGdHMPE{dp|^e?sodo#)B0k-PU8G-&DP*UVAkmvHsls2gF&6ii=%YPrLQWXo6OHsHmt&+||+6o*W)u8)}w!XT|iL9}aQ% zX3Ho%;AMQ4U+Fy8Oz_2m1K00QJK43~PrCfP+&l&2wmE+&5mnkp$QtlTZ8JL$!XjFYEMecCwh@>~ZIg#@vV{JH7hyc_>0b^ocX-FNZ(!@WwW z2cLvsxO6&eh=I@24<8Da-!+-(^Xpe?X=$n7`pKOQ>Z}|yDuZTk6L_%zJYDx`zVg+r zW_#N})8FN3@`mg)ts<1SqgAJ(62@sgU*6mdzE|~HcR6qV%}uVL^(D8qWJ1l1IDBUE ztu3t%>?}EEyKO#a&YPpN_I{qF&JC6C=l!P7DERmFEra@Yq_r9i4bxe!yqlhN>uIpR z?b3dg*#?PC7bj0v_dmt0zei!Qd;hi1-`?Czj*+&hFtARSl9H+LYOpV?_L@~D@M6J% z>}dZ}@BVN-eg_&hu3Ge2@eIb4yS(s?z17=oembdpWt^ zR*jjRFK5nCL#8}&g@k8iNm33AavQBy$6kAuy8 zXZOsp*Yi~S+MT=G$qU&Z3`}-v6DCenqpOmQlz~d-Gh~VZ}!FmQpNW*bxXxuZjK7rJbI%T zXkl0W!%fa*)u*iPoZv(r%Tsv3%(UEp{s@x4~ zXo%civ({$HdTtrZVAcG5d*!XAQAf@kE&cObrRDdp0Q9^B|vv8E=K~)xk7ha6jv1=o?tra-Ej{j(tR`mK?b8H+Axy36b3a=Lo zF7OC9bB~2Zs)GYZOMCn2+V6MOK`oE3rDxRq=Dhf_^QP)$P41a;VhRb*5({Q=>ohhm z*ZR70x^>ZPmQyBeo_~{`zWV4XB$+i$b#<}I@(Yhd+@TA-(!!4~vRvEd!}7=Lz-&hC z=$D$B)lcO%&O1?Lu=!c7^3+?$KYhI|RD9Wpvny4rky$_;=8^_(CUw6#7sP+X@2%Rp zqJOiep*dkE>dFr{uElx;u}ir-lRsEPZA_v+k!wi4@~~ffH7Nn%djvPrLh5luyst}e9N_1gE|zuFDj z>-JWEpC+BZC(x~5?yXnhetqWyXWqvv@;~5a(hB%CO-$qWG0z2WYI0p>a!vW#l`W_7 zxpm)Vc9t5?1-6WEoMPKW9``8R9#@8IR-oqAet_ZrXhyWeXvZhrSWuhNL0 z0ef>Vrpnui9h0g6yzFvPWEeMRkpoc(5-))WRcZd!S3S8{k}* zkFBq@SwiFfFFV$SCUXfDe{z^O z!65hJ-+;G^eXH{xM^C+cI^+9``)Q?d_4}>XS9~{5vYz=i==ZZHjCxZS3&7GtgTvkf z@9yq?TE74H+`3D$ zw&mZ~6IS!d&`7I!e`BLFf8E`>-*11}9&YEKuI%2|Ayr!TZs+q?PeTs))S`|C{r;uT za;D}l??wCh0zcID$K3n#Ym1D{O9{^K#!cL8N3)erOE4TQz zlMBmEemO2uckgS^-k+)W`DU#YOw-MYF)e)+q8zd8&vEcT(UlVKbjHoIW=Z`uUAb}= zuOp}nU276``FnU^prZc1AB$E5wmzR*9(Vb)U8UcFGwSi&b3chT-mU!FxXF3>ES6JE zF3VOr+`OUUsTA-`d;RL$-xk|)H%wdqqjg60H*=;$UPePxsWWl0@8T9uDA4^e^+&9L zPi?lNfvv~+jyp5j*t zfvKJx@TS)~?iF9xK07z}^)KGKFr!b8cDvSEO}er|KfGRErt)R|R*5gKJZ|gW6&4p? z9v^fWG%6KP{q<`2)A#lNcmIp)?(Tm2`Mmw~GiT1E9sU3Fe0^7x_}gc*^YhBih8uIt z`1xw~!uUAR8%vo|ZzcV^xqLSJsb&0q)3vv*oNl{l_W#8DtLHBJn5TVV9;-w4x4Knp zUwzHI9x=nY=fU-x4mZjdACz)ym1H#1wKrLG`E8YT!qTP+Lv!89o5J5^8fGkON?p~n zxgmZz;|h3>mP3JYtJ<_Ees9*aE5Fhj=j{3WJ0bns)2CnSAI_UF;lZ0tpj9mq5i|B} zyLoA;x2ly@l-|A{NjslO9p4{uZ+H3nHPPG8B{DNE7yT8Hmhj)@*NxK)tnZ26m>Io{ z{S@DP%iS6aUxjYiHtYWv#g9d;W{cLii!>U&-@o$RedgUugI;~wKXKQcyFukS`PS36 z<)09(rbLJhQd+${H&%IdJSm&7N8KUbUVS@rdmYIsax>%NFRQ3-YzCh7mN zi(uV$g~MU%?WJB>y+%_nUB3MF{j&WvKc7y&w7WbXRJpF)e|XvTt2<|H`}ybd`PbHY zR??Nv-pIwDR8DxNo!?Yf5t>llC#G@vnuhf)l>o6tx`RBAL@zfU#OyWj3Uo~O!jO+-y2LTTCm8--R&?6`bp3rPyzepG*AR@mdS z#qk^wEAMYuu~jxI>i)smc3XO3-PRwQ51g8+oqDuOv??I&#f62xUKp89^3)YuUzTZ8 z^J7E2@9o#8${)VH&%XNTU(ixg@W}A{Nh_BHE?c(j)QyeFbM2nZFid{3`Mlj^>vubx ztG>Kg*l!lR_+rRU(3-pdn~(o~zh57;4goa!#b_a_5Gu{G!k4YaQqbV@)9udp@92H_ zx;^#NG_EP47JVAQjhklP>ezI8t3#xdit?#D|CIjhXzY6vS6TLD2G@j465H*if^dEn{ee*1NoSKrkI4TL2N{%@OD z&wF3%!O~@yRwc}s)fX8Ws>;qMGXZpk$D(^*r-sLArk|U0@pVx4TvI<#lXPkP{<_}H zHScCL%sjtW$m&TRqv@0H*`|wpq8IBdzwqe79Ih#`CMJ%0@{?6QPWHe2e2SJHF0@F_^s@(*|! zx87cKHf?fr{@$yK5zBrF$;qvIzB;q6`r8}M8x1qf?|D1V&eGl4E?*bH#l`jIVv-ie zHH($STQ8@2c>G;3Nj$E?ahcD|OF3sZvCG#4l)b&Rl>H3o8jFr!Ka!P{9`xt;+Le1a zu%6mhYraTlyXOL(5Y|&|nT?w+dJ4HOSmV5}v10P--Ql;+$xJ=9y?&|SR-2$RA`5j( zX4!fD?pgPLNv}w7#FBHXn2NJxbzgU`37dIy=f+SuJtg=u>552)w~JOxidugrGAKKJ zrDntNKH1{3nKt~KD_&03U3%=Y#?@abSuV3~{SDInw|H%6iw-4Ej1^;-EGPTksEy>#1@mp2O{7hg=~ zWi%@8?-4s%lgM~M%=$PhQr9R(B!K(K>LqTocx5a$+>8xd9m+qASK4gNO;e*9{|C!v z=LwmfdgE^YYogqXkoWJuyu54#TA}mpR(9?B!*AaR3O1EB1>UJ=$lpol<(+a=cbftW?zF?PbD4d6t{j*@Mu%R)*mw>w*8APxc4mf zQ+jgiSWmgBdY0_Cx!uYy|Bu^(Z!fL~{XLo;v^#%p$K7k|>Q=Mk}Sau<=l%kC5E>gum|>!zipu~`Y8f3FN$g!#nvLT?}U3Wu9w;6B1!jZ&T|t{KXw()vB7 zc)Nd{u)=^TRW_XOj`iEEVk>{9Z4Jr)nZDWeyYC{l7u)tss82R6Tc5M$AY##(g93L- zO3I19-|w#%UeWsa>bh>V$=UnY^QD;2ySo`Q`u5s4J62->Q|j!^-YHMN85dphT#$3_ zgv-tpmBvkaD*P)}FanPW`%DBgM?}-9G%#xxId?uK(W}sj#-tvh>Wm zslKiji0!2uDveiHhj&K_`~%Hoe!IcDTQ4j;{Pb!4{cF~DUX?A2$oaM)<;PqGM&td} zsUE&&7N3y+l}%6SK^kZkBHOZG>x2UuH=XCtYTP8d z{OOj-4mV?RO>V~IhDbJUT4&5ZW!9V0twEcgC+h|*^WA;s{_Qs#n;%ubTzvA7T8yrv zpdsJ&n|m)cHVY$VT^Sb8dV}{pmg(nY+H2C)KR-V|ef_>)UfXhSt7Wmf2{GLhQAl{^ zTYIa_bAccK6wz$uQ|C_1EIKZj8kp$#Ym2w(DxD_5RK3ce7i${U?s1r1^mCcn*H8D8 zuJ_Ge_F&&*)tXPdLPjNRGn%60Hm-2%Us?3Z8F|RQaWj*9pUgx?X0{ivr_4QDT6bl8 zemrQq-H#86OljvjPkdbf+Gm+`IpjCXt2Jdm-z*T*xLxSRHH+ocqOGidKMz{=0duEeoE6SP>%Nu;4LMmf`;g3D=$Z zwGz@cMp)fbC|A1R%CzqPm9oI*kYoOEcb!%VZUL2qj$ z*S&4HVf<4f9X5+?6C>lowjW0(yjXy-#IMkX62YsYN&CwG-koItjWCWAq(F0 zh=f_Ool4s3aC7M`r+=Hb2&S@}vdWX`D8IEuR!{eKnAg{9=111Ot+{E!y1ajyL9SNn ziw{1AGk@+svdnIdGQ65lxFFdmsO)y)+S=%+Q^VuHjsJ+Lw_HEBipO18|NVXZ{)1uN ztveeU*javDUdTQnb5UvIEGylBW&Ik~%C5f5se)-UzsD}n(b@5Je{aTK(RFOI9PW1N zY^?dXE26A+!P$dr3TtX~4aBUH%KmOSy+yF^x&_j}J42(M@QE{Lru_f&-2T%=cX>}g zzrL8Vo2iSKk~VJKn6&-&*SUt@-`-XSH44|yFTYo5RP*D5UiSPhffp+n7&rHwDvV#w z8nPf~uGykDcju-r+2OfB$B*@toBGK$iJU@S3)Wn|))>?nEB!3CI-%L@?I+Wf)|=Me zOrE`{IwM$b|8n2Gd%~KwBqL`yjve6+zK20$Z%;urgsgShiFXGV^Gg7H_|5jPw zt)0D1qyDD$_m0r$`Za}{5QBhwA7pNvXQOd7`k!X$&3F^NW%9F?9)F#yceQW2|p1#c$Twto7zj!83HN~4==+Ud{*IUY?cvwdarnx%L5 zH%LW>efxB`%3#gXGCfUa+u3EG5WPh(^>V$nA^)FU<9}y9{79{!vDNBGe$Ax7K zjGN6mH7>+4ifa6>$gf?n=FTIFMQhwkI${>C4K%AYXx#K|SK0SZSyIcSEU%?)T9q&} zR(qN286QU>qmo_Q>~8i7UCMWwy*&utVFSf1C=fw=?{_Y8k(|!5Dr-&Df;Ap?H)0ih znNvNr0?f=j^<-r94?6m`vF!}8Zr!!JA!_rPt0~v7sdrn{H@3|EUTb*#d)ZgBQ>c?I z910I6F)$iWpP_MOmcvppjp)tG1ML1RbI{T;YhPr$pL>e0a=^0(hdhfL+gSh2j?bKz z%*8kPgynbB{SJIc%_DH36szEHU=6nb^HwfVjp)C=wF}m4I%XI!O(xiWQO>Oe6^6!( zbOP#KD)Nx336QT?{sc8N%#7b>_P%VFQ}pzlt!9hfDCfFFUUrD|%IS!)+_v0DR>FP3 znceG8zK=3j=NH&2y-eE^bulWa1$2}#p`=`HRfLMXOcDnm| zP)Y{SD8gJ}g@kLC`}1ncBTMJj?ARS}?Tzk~S8B?qs*bmx3cYpZ^hxF<)e}e7O~3n# z{oq;t@U1UXS4Qm(;hwf~$=_9P8#K*UUk(f1pz~~5Y7!S;Vbxq8^g!I@)X)$au2Htx zDrCW%=DV!F&P|_YW%}rOj#oGT!&`pb(s-5yS&EP=lfA>+vk_Rj-&+=VvIN zn#FeNmr9Me#%pnHHMc20AHV*(((ZQl>ZLVD4})5V7uP>1K9+rOlf6fP%NiE-RF5A| zqse2!32A(8G{0KAD?#_n#7t_!>t&wA?E_bG*vhH`I(ds+J&zE0O(@@cQS>l?Gb3w;F(h4wz^ z7n)?KRv=IjgyKd9#{HmR3}3&xcCN0*b+;**jhnu*&tgBt_NC{-^0+?LfNORkUfaDF ze43oTr0TJKPj!>fC6%;?^N-w7K78UaMky8#+8LRhzl8f&u^ac4y_+BRjPSSH|knom2MyTX048 zUe+B!ql#HB@!yxsa8gFw`v-FNhfqeQw0*zQZe3g-40hOWoz;~Katgd!V%GL*NY8Xz zuts)?$lC=CYo9EcvbSb?gukp&7RT(8UYB#fXU=FkgUy+nK+deQ&i(Q>?^?++Gy}{*NSF$eapN_8%cr)$Oue37< zg-Wmf_qZ{m@?FlfUC?r$6 zRb{zFT=|qtszvekhScZ3gEqbj6wID`Z;GZ))S~mrlWeJl6K5TKgdaT~#s``0}v$K*Wt3G4@ zuYORo8C!&ygO0PQN-n)+HZA+R_{p@~sMNT?t@pa^I3F6NCd^6U^PP~e+4EDk&L*$f zRX<(JR+>l#s4c#BBWH?X1+3uVhYYZn4-huXdN$mFJW9woZ}~*Rn1;HJkO6+xM0HQ+U19 z0-iM-?>x@(#xykO-}?F9x!2O&W*@xu<@Ai#(>pZh+>PKB;RDxq2;xszL&MDW__Q_+btpD~&M(x;Keje1-{qyaL$(d!!r{|O&Ofh=7`1G;NMkCX&MGpQ75QJFaXZ_FX}*vH$+aF-|>h-7IZogl*O~IW*;esj?%b8s*6TJjp%wxJ5zejDVyq@63>+dgTb$Lyg z!FpO{$N7$TKM&Z5BAINbRq%Xnxs}hsH@CO1&$+$rY~qob{bh6W6dz1ya;&?VZU3%h zKFjszGqx{ZBR}~;x^P%>lD!ia=e5d~) zXRp!hvuc8ZYs&Bby>I9Lf|qf>Wa6}`Q=jtA*UK+XSmV3Ne|Om10}*I&WTX_$h2X!>&mfxFr=#Ud= z=5hYldGC}S$TQUm8+_(Jx2@W8WB7zO7KxK5i>{sS+bIN^|ELH$kfJ&L2CuiulPh(L zBfWpAXwF{swnb7V>j`ShV4irzzn{;azPPx!=-C;`jS)IUr**e`q@^u;Ztc{~!P3xh zpXCf2zucYNxZ_t39{u#Z^kEG4|tg|=yb4@8ygah z3LZE-mSX2nxKPQ+ls4BqC!AN^D=Q^EZ)e6wqrl{{fA^o(s;W*~nfKOdNx^Q`D zn*Ltw^~q-4`>WYcze(cfn))KUPB&3GeS6F_ZE))wN<0W{eDEM)rd{o>H+Of3Z`!<> zb4KOY+zS=uA_?=Rw%>nsyqddL?0A}su#T~b1!%iN+s&*EXEN@nJbYf)pD^>`LF66J z`xF<5{mP%^H-Fvzz;)^8=lSXv_AxNc6-j7`RA;*DOs`dixp4i5W7*4Gq?; zS7xQITV`cyI~iqnww-c7c)0raJH`F`4*WkcQCU?-$LHJI+sofinK^BmnpS@c15>QF z!+|x~w$W>SSx>LpvS6my-bYTUF8R6IK0-`#0vQnx?oVBYWeeO~;egBf>1SprW?x$~ z(foc*@OmAO+cUBs9qBB(>}&3sp>g_8iQkOff*E=a2iENDj4papw%cT0-%7XXTeo;G zi?(uD3Jo_{eO0GjaJTgONzf@xuCA^@(b22lPs=o${r6kt3k#-wdJYH9IOk`S*R{%A z7yA>gsifkeCn55F;j~OPM#17w41v27cTEaG-8T4RUV~K0#2F_p+3owD_9C!cB;jH` z|K6)ttIz3uv#<5o(~|9IEW|WVh(o%z^R(~QBHC$F0uBs~4q^$s#^(F~X61(a zY_(2vo^|%cCN{2pwmJcu|H@wcHt&Mj>@_C^-J zPZYAsShak&Lp56R;`lL@K~Tng?>gaICpOL9`Lyb7me8}KI^W)_WvI#yj&;X+*FE8X>7S3+KGUwPNS zx$Rs=zUUOh_%tNk6dr)uVGpN|MQ1pJ+Eek3k-*?rk7%Fa&iF?SBm z+QEyKh#LW_a3HqnjMz2=-j?1Ql0vD%Yy|! z;)JYzP4c^X{49C|uuNiL+^ko3d$n}0-fTOSxI8wGw`p(B=w5$sn7KdhOw#|ipB5Kf=ZM%>7yTx6`PWlE zhPJ9XcSK`@4Yxwuz@XF*E@A`}KuwMhuJwDiUs#drQ?+Hukv6I4G71kk8Fz0DTV-E; zF+=N7lYE!vjQP&YEg10-13IUYX_B_co~6daGt;2^9Dq$lL67 z^;o?`>*3{hRF+-fKwsz!PJ+G-4KuU-)=t}W`|YM>7nA;P-g+@rZF1Dno|cB^ELr=0 zM&z{atZH8NL4wo%yVZG9KW`4SFep)GWJ-(uU{sEr0hjZ3y>E(I$;5b_HRQs!s>Ant zqz|3nALx_VQ)J?R+>Qac!C?XDK(bqnSA#AdTYP#Mf9Lc9-3&LU1CyDy{Hl1nr{DNELUMoXE)4%s zdEGO>WliBZrgVFRQHtFJtLw`%`o870AB@v;SCc}PbYf&-1A@fg#= zUVn?SSckV>H!3#%zj^D0(PYoHNp4ODYMH+6irSbHtvD@KzUynqwj0^HRmo}_yKT?h1o;6XRU^;XNmha%EWw|%~ z>OBfpaKnF=Q#6I47`q&b8Z?BvT_N<3uEHUEXmB+V&uU)m$M61FY927VO46>i! zUU#kR=$#p%Id@FmquO~+w83)bV8#p_$-Yp zTxevZyK1g!fD9;KLgEL`IxwA;W5(9UZ*ROUYd`ugJ$vzKpJkhWi)>r0)unVnkFod@ z!}QbJu{IPM89-%h-}JrK`-D-)_w=hvTeH?q`aAKHG3{`i_`s zP2gxmCN9J@G(;9p7Auz0|7KrX0Bf2EtoU2D{94c6v&+svMmWKTz#wY`6>VE|9F8nc zm2TIDM#a44YKfq-k;DV0dR?e73bb~_y zgH7^un@7j1xq7Q0QL_S+7vBl3xVhhPmEGU&z}go(uX`5+wMCTGYQWbAnQrPynCW;F zBh$)?DI`2&iruI+DE;3KSP_fq86OM$qnz>6IXYghT4ireddBxqr{kjVY$Mf?9-+eTdb z7`0Ov9<8ug2A!C9t!>iXyy?ff|DM0*Hhrc~mae7{(>uWpw{wC%zK`!%Yp{J^V}@mwiS_Nd@ruX2v%dV{$PGV}*~}4T;;gjTn{}04 zoZ8lQGwD*d_`fq)vbldv#%M?#Fb1W{f;^jDIlmrMa9(dpQ(?KInsD>$#E|@|lPm0M ze)X%C9MQpA3+!jOI6th%f7-PZ=k65kZJE_lY1sXD`=X;u&v9Lmy{#^~TF!J8r)1GW zmza`vP|TsHmI}9qSfl;L>o(s?efv-8X3YwhPhG3N-uWP~dBxIyzGV{)B`;1Al72OF zyOTDfdcyDjZ|`!>kV&0u@vq!yGV>$>&hME!m?St71;rgzw6`m0G%0Li+O&hazArsI zGSXGxX!1_^hzZVesg5j-tUn96i#nB>1oTv6IKTTUa84-x_gQz%vSrJwm!%uIpa1gw zspPI*uhwo~WtaaoGr#D^O_TO&jIisIh>shKo7nELXGI_pf z@uf%QaVJmZN|~zl{ymksGIPnzh-%TOL>3MK=-mh)#sas-w)c}Hbx&)tp6;qM)P7dV zzjvb2dY1*;S?}$AcVtV!GiG^_+hyx}@4Z%ES-I1+`nSO;w6p42vP3d|G(?sh*2#|y zJE`}{C9cyUpKH(m=h~O{{9dOcy7S66_a%a_b0e$rp;ybp?SE0<*v3EO=#12@%icX% zyVd8XRCt18^y*C+vD20dO23~n?bnrmKcm;9I=SHh1E)#N0Uh}V`^Emhd$M+^O{G+L z!o}PA%PptZ881D`ZQSxK)XZbCeSOa3rX59yQ`AAm1h_Nt${Va+ysJQqSLwp82Y*eT zhu`;DTWtU0(QT8enT}#lZGKw1Gmcu~9tZN5jo+ z(mJ!R$=Gi?J*&fJVz|y-U}I-n=y5c9pv8P4LxzNG^Dh z+&IlnuRcJ;zV6G1FMsb|_uKyO?bi7MA=FBimi!3?ZGcC0#wRw@&>rX1t&(Hd9 zuK)Rd<=4NKH-GX*r!6%}dp|iS`16NdHu37Nkc%*2X;CWH;lP{se%S;yA|L+WY>$aXVX-8zt(rVtzx_E+O(*a?K!+I{0n(1fau7*f7 z8Z~~e`+j-LgOAo;+3xlKZd}>)strq6{t^~2SiRq>(9PK;;osuzOTNGV5&1I9tv2hw zNztcs*ffX;7_2^DRT$PBlkia6e~IP)@|>4iZR>ZZMrG|j?EPLB<{Ma;ebrV-2-{!! z_zc_11CPqnms;jF|5?uZK0VwZ=Z{q8mpFSr-@`R`*fOVD<)S3h3z0?vc=E#JoA&ahiQc_$Z!>Gp1;s4PHWm(n7p9Dio8uOT2sBQ) zxA*0hEgxpuPuDnB^Rea1o8EpT=vD_g99ZM6B*{^uvEj$u@XK$y z`#rkj=KXtkYTHuR^Y)YenwW0gwl~}Bu>H1NsjX-CZ#nkWVqEEWwcK$o+*+ghHs z66lzgx>?`KMmK9V^4(lzy!gJlBQOxe5Zw)2`0iHF^6rBlsngu!#KS?r>mD{T$g; z4vr_xf36p4-aQ%ZU5RwJ2gqthroZY63D@ec^&#Kn1o4{z2gpl~s+LE831q|-&Yk7I z#JVzDFT@Qcz7-r6#IvxR`PF*Uv90OAiIdjXYb5WUj4obigt$Q(YLk*e!ZrOfd`2QH zZgn;1uk87BL{DE8ROX`jPht)eQ(E)pdvm@raF}f6@lXD4t!%n}OWf7P?i+OQtLNm2 zPCIInHGBPb*}Yre?%(#`eCyx*BS9-GzxH0bG~ZTB_w(28peGa2BjBL+1)~c8yEBC) zBpQG6-@j1yVA;0+K~h`4I%d9n`)_}vrJ%biQicV&_m_(U_h!4q&=&!s5r5v@&OG?V z+IzZr(J8&9soZG!`@jVO0Rw5NuP?NIc_#cjY`%0$Mq=5P7i~B9{AsDP+H@EeV=!0! zRaHp17JsSFScT=8@9u3SvdYVA)@{i_uYn}2n3&R>m+QrP%5&>f)w)G}`J*f^vvj9w z?~ROhJG(`Xz_kF#tq_A78$r(CC_OGSh3T{0pADrqKXlIbJ3LL!Tx0!VPxv)BP}j`m z=9p3XE?K+LXTjF3rf27u)p{mBS+;xHG>nqM+1uejPR7;ct6wTNww}K;U2Ol~qOhvV zm*hPUU%$Pozi&wdT7pSfz{+xlZ5NwNeE`Rq>JR^?o-00b%zEjvXAiMt^M$Gk31(Yf zUB3FI(c$%<@0b1@&(oc1gCm@U1q`ICN?&zVMJD`4c+|=Z`8+zO`42W|LoOJNVwKLqf4rasr!DVscy}$ zd;EH9R%-U%aVbBuF#Pno4peiuhzl5Gzkev(SjO~uS?u(n?CfppQs4S7Nj$ygM&537 zjMM>lb)ZA}nYbe%kbCbTnFe%0mejDics}SfIzka^}?{sW~pT zoH}*i-)>3xHFs&Slx6DlOPkPK;gIlxn_~v=U#qg#yCO6GfAH1RjhgZH>YiV2D=U9; z-qmqLx=IloDhUQ4_0Mh%yt@c*H({oFQP+jZt$ zp=`(|PxAofp1uEmyZh<%;VW%kbMSyFzJ+0n*DXKJ z^s!)u{o7xc{wXhi6BV@$5kw6Pjg0~kdnyWdfo|_SYkq&uy~^iv--sORm7f0Xe*OO~ zN5!H`s=TJhRb}paHY!w)_lJxZsstZ#JF26q!DEYFK>j*Eh>=RpxBH8kTuf zH2lqmpX+wN^SZp;-(RrebTG?fB?j}#Pfuz>zeydRz+tkep&?TF+OBLrQ0_Xr??=a$ z7f)ZlHJP=k+*fm!?DliD+NQdfKR&#a$BuLjnSjEDcBbq#8{MqlZn=DEhGDXod*qgk zK+vtTpo@jAcxN2l_c7S#^Yiom7eD2knxdJx^XWA0yi-g?!OQ(}x1W<{>eWiP@ZW9i zPquyUG9t}>FFI_^a=J@p?fY}5n6f5@BWIKY4xC%IY}vv+?P%qi4TrdHzSN1?vElae z(&ux_msu7+TQaxo*2>$zZ#6n7bMnj*OS>2oqI5x;Y4N`@XQ|Jpgk!}-*PfrZ``?={ z|Jba{XNI#OXRI&N4_x-QpDUZScI&lQ*A$cQJbQ6(=dG;OOMA`l?Kr$of8UR!UH||6 zzI*q|1Oq<=fy5t643tFtw&hcJ2#vL3pj;1ga_Pp3t`F`*BY3X0*IXHu^ z+;TD9ePWTr=dRZx_se$OnY(AMSPIIuo6{T~e7l|RKRvcA@>cHlyO;Q^-&}Y*Y2EI3 zyL1$;&8CyEL#E%p2~UoH_xb$q(`=E=31!n}h0mLzv>v&|*4QXi@#&=c zWpM6`NNnA5uj=*E-S7Vu2yK4$>=~$ZNLem+aFa8?t;yPr$E04)Nbbul-(`Nkrnu{^ z_4}BeA04*X%{I*rn`M;hC1su$Q}y*~cyP)8ImYRJx3bsoy*2eIZ=;)_fI;?@S!Huo z8<$OcnE9qIeaTI6`9RYQv4fjV2v@=`_y*U7JW~#QK5svtb+=MS>9?Ed+pT*gigPae znx9SE*fTBl=bLFqeFQbSBTcWzmfu|^JnLvgDrgf@Fw415BD$yh?0&t->z_L9{rjm0 z62vn=7r<@)HY<%u=1u6!KfARr>?&}aReoPI^PaQ$64&*1Q{T!ZPjfXtlM$&FaV<9R zCsMOVgX_qSJ;^rS6| zQZ{m&W|4AB`1OUqYH?=bHG|N1PN!Lw6wI=^_0#R2-rl2*R3aZp5YEV4`A2mBr8P6A zuFIX7I?eN=vY^6*Nsj;TvZ7TNpfKTLVoGD>>Bb_%a^?;*7U5A1pkRZvT^X5JMnega zMn;pyXwn$X52N{Ev_e6ofzkYc2#eAB0aiaOh+_p^JMra`xBk+fpP!e%7Kx6I4*u!C zoom8O2WGA40d(gFaz~kgk*QP%bb@Y5dijfm z?aK_4k1e^Kw_Epa_4~ammo8P6ExVC8cizu8H#cAY@bK{EMs~RydiTAw*Is$I*PMj| zHk5=srobVvLc6h<-_D{}+I(G!-()pkuXg#mimC^V>|U>}qB{=Uy0S9Z`gD>sBU38V zF#~PpkJDhG0U5x!GTFf}LhQLH3~76Z`rFzq6Hx)qU{ zx(tj=wt^M^|9-#Bt-oi&wW#d1v&!Q4R1{WyILIE{Q@icYr_;-)hR0c&nwwwWvfhV9 zDm38&%NePyO)=&u4(Ew$-1mYDblpI@acbWFzu#gH9r=;^`Po_T<#UR>qPAoNPOEPf z4fBZqee3!qwb?nBp8hiu$Ovg%c3i%GkEveWqa&TYSr2w0HSiT28V)QFsQ7j>{c_=P z+3DBf>;KM5+nJM_dv;e|pM;?j_ol5!87}>NK7W3aQG>9*jiKuYA0>we&W$fGFHg_! zN&L336Q$oGpb)T~DS!XpGOM3YCSNW(t$Y3Hn;Ab{UChsLOmpqJ-fMo(;_dGD`yy9w zZkMmKIR9)aL!%KR<7Oj)d{jp{?BzT&*V_E=kK^`!+~RsZ_kUg64jQDWs_8OJbXX$6 z?W^RlU{gawWU6>Jn$B1bmjvxy{`| zvPgv;Sn!`W`m5d?l&EEEJ^<efHklr>q+_tY^R>z7L=U%D2ZKlfTp@!6aU|J&uNG-6AyhGu?xax(KUulbqok6sN9 zGaDKrB|EkwiWg9ue8D_$E}46-^m^>|hg-_-*M8sf{a$tYYjOR!IeU-oNSzoJ6?N&w z#l@Fd#bYM${*iy})+aNw>_%eymg92OOKjrTOy>~D&`+3l=9}C-L6jWR=%_6aYor_y z=Cko#ulYTX!|nY3pp&Z{A_b2dH5xV6Ogr;!{-RAj_t0k;n%x)JM2Mx!y$*}YTzarr z@Z1y!Sq_OW`}wpEYC?y#kvwswzo8-W@0C_#w4~0$A+Um(k#V!t3YO)lnE<3Yz@CXI z?Q5|3Irv@KkW2*{$K(TzV=fSygFd+E(BSZwgJZ^3|5yuQ)Uw8*fuYe#K)_&ivsVEZ z$aqu>zbY#vgxQxq^Z|u9+#WFFl|zC9Q`*|KYghhRZrojdzt&tgV#9*0X&SG$UXSwz z9pQY{#;hd%!QSupy!H2f37TbJUw3ux>DPw$%BQBCoi#PC^6Atq85b9=+jJ|g`M{&2 z-IwQB7F(T-^p16747~Pw{r-9Ps$Q@4JH2^hIm@H(Q8mYQt=eyK>-YQp^RN29mx^T>gHRo8gd)R*_|D|mRwB>&!? zcW;#P?Kvy{eGx9vmsz&@{ieMI9IgTi7u=b2YeVv`CiUBVI`PKaqq6OBzrEes?f0tQ zh8-6_6950#^_R&4=m;z!X7@Q@30%kRxRXZd{2rQbD)pPrn2`C@VZ zx%+AB1toSoo0Wa3*Zkg$XXob5UK^dYa_J>-$r4k1wybjR%Gd`RkIQ-6|Gs(t(rNwu zbLxIwp1*9z|L&F7cJ-XU^mKasysQ5I_#bP$){g(__3O%VyHK4=Mfw~zf4^LQxu{zY zGz6;8nr!#?rT_B!-`nd~NBjci*LVAVzuR(3YxR;~Kg*>_a@y7I3U=J#ah0jNo=%It z^ycQ~%}$TM-wWUW>*|)PVbN3h9{mhnBypj2T^;+ela>E?GDNpp?@0Hx?%P%W`OW6@ z%ih<0-+eRv#fHOt-qCqGQ}50%R-Uuq`S}McpRU_?ns1(V;W8Gf@CEmo(*$pHC4{kr zc4dCO8m_<3m*4(Rfz|&%pM#5wi&vUP?dHkNPdhhfX5Nm6ZCf7qS*IP`mcd(aGj;mX zA4QT+r%sRDxBvT8)^$4`b>$ph*e*BgSdZl7yxniNmAv2kec7~W(?tF!oC>a)DPvbt zVfEnv^XJf;ufg?N?H4Uy7a%w+rU7D zrWyDD|NFk#;p6`QzxSuFzi72Biu)!hpXc`H?YLjB+3Xip`E=@LBis62zPDFfU-@Ni!1wCs<<(!jLuRC(75ez~_c`&n zibN^Bj&Q?p9wz%Z;*9Xn~dDnw7 zFD>zW`Qb3XKflcfhxSQI9oIr*?LM7Qe#u||!?~+yvESz{uU4&o)*ZK7v*3R1_odJ0 zRqM?yIK=t#-|zSS|G(G&58v1l_4<>3{ZH?xyq!-kP2cxrsY%WacH6DVmQf6D3Bd=} zDCPHu9eclg!@qCa_s@0fm6|$L@Zbh7&AR!2ABpexxSg}vw<~Y!MI+x=(Tx@BT(tLb zymFsqkmywP`|bA2mCxr+-^y`@Ic=VOZ8U%E)VSC09nQ~Rr}Ddece?NYD*cleAKlNr z$n~p3x$nfkwR=@kZ*DJVwW+VVzInaZ>ZiMN;$@?EvT%Hvcc6jgOqS(e`F|hUBfFQK z`{cGkEy_XkGMDz63441D#h1+EoKpUp`}q&h_VZ zv?LifXMr-_0pF6NJ@fZmjb}Nf^pvqF>-dI!WsMTMLDL=n>@o!oni1D--n)3$CTiZ> z&FAejqbxmZ^##_>dc1tbr)#r+rYmo&{3p`-tA6LT`;GJL|HhlomA$xmnep6TH^p=< z>VKY&Ulv<-^XPuYSIeWk(m`2o#p~9neW$N|Wwo$mVlirDl>GAlsCfLDsG~>X*L~V} z{M2h@IW}9*O75N)Zfalq9R3`B*(x4)V%M{&(-3(R zTmJoeeLZSVO)STebj3@%Uay;7yKK?yq8c@=zj8fczJcu3?{=o|7MmX{bLd%|ScgJ- z&wh#eo|`F?eXVrweqJ6tFYayxIL_iy;~yQe$(}Z2Lp;}xSF2VB^~8g2$=Mg*_gukS z@4?T_PuljzI{EKE{q0+&Mn6NNs_=`*kSxwV$-6Omd%s@Gd9##b#t~-AV?JGl8{GP2 zcCx507crjTa`WQV{KdUyQHwUc`LobrOHt=cqwV+We)ETK(>?kl^`esIF8!yCn|=gI zPBs?VAK}J5Q{wld3C?_)Z{>fxUcZxH^H@4_X6iJ}wc9UjXS({TsF~Ny zSz^MbbLxJ*e0fCJ|4dX$OJYvVb+VoFDks9V3?5`1l`%^BqvGRaeqwn)hTJmJ2nF!|lC z*Xv5o7#=TqA$OxXeesekrqg=6b8h8qKD)`D!zKKS#Qu*z3)PMacLe{jmQ7=>Leb89}GjI@N|ub+upaCWA# z`q3Rd7q!ge^8_|A?mSar<{G|a@ArGwwV!9-_i>l6E%|o5IqUI>%6~i_rdLY>&d;AU zz)r`t^Ik3#;D8sGF?%AJm?|q-=5MC;Imqq!;p5t=WQ;unO>(2zW zcsAEt+%LPG8|i)Mp7r8mx|(*}6CZuv@axs;=?b3?K3V+LwMK1T<+GVN(N`QRyGlRq z)>C7e@XKA{pM-$%OuLUqgk||&{Jh+_WL;RrzbpQw71yr#$A(x>-v96GdQCOUp9lHt zE+~5cVbuLML%81J#Qz_c6(@`S)q3#r^7=b`ZVe92Ts##AbpAz&G9U1tVX)9WRG>M{ z-Tc4{xfk3i{T$YBL=LWd5%lv~bpBF~X`!#1t|T7cCfaZNEn?fQi1~&O>bEHL9PsVA zDWdr=OEPxuqTlnclrwF;lG<}wMDx{>8H-$l|LDBD(R9>Ey4KQ;d*U+R+0)c~XN5dE z5gp~$z3GQi(VvgU)4wg{$>la=bnux`bVSg7NoZK)(j6y@^toKL_kGg)`_Zy%-R7t7 z=GpJ%xbN;-`R|Kh4u99zH7^;X?B5Hmox8ZS*l_Q!SF2y%DL#MpUx0AzRo2s8R?OSq z{4_53*5}jUkj(id`@oxBuh)5RPCM%ryv*lhT5WfGfr)CL{;TS@Tf?n?W+V#62YeE# zpV^~7^P{U8+n4^lT0?{4o@PT@_pSf)SpM?=Kj;6ae3gkk zuebltr#0MBTc%9hr7zIsd2JpVfQo2weMkbH~eNvp1deOxkv4#r7X}CGJj1KC{E)9sO=;Heb|k-Km}jrr#TdEbYx#ob=Nm%& zk2Y6b3rShIdg?_b<9yD!^CRqP9RA#1bZN8Z`R7~zv)I)CI$!147jnA%rmE)seJmVb z`W=F!Pq$sO2(go>j+ZlI6YW>KE2wcgm?dG~pQrlikK>=deqd^lE{Vfh^UtogdHrtp`+2>uh1VLKvv}N-WAKKf!L@6e z$H%8CyT0k}{c`DT*e!l1~f0nD4({S6(wApXIC#2W! z>^~pfQz$k$%cE9Z;NYeTKi~Owwy$T2xOc64X1MJ9m2IAWpPu}j?9Fo~d+mPa$8T)v z*_QQb$zI*OY?4>)`s#;pQ>`=%vdwAJ6&^5Wgp)ja2}zt|*xaO=av?dkPXdm7TVia2gNAzi=r%<-I6 z)2{z&6zrJRY&b80#YQ51xS4grM=3`|UEya6ZmCSsab#>lw&*4ig3sC{r24uK2?M#jx-D^jL=V3-%MyP+ZS zZCDZdPy`EyNkcUQ>JZ+I`RW@kd%YNfi;X;H&vnLebwKPadDAJ z{l7oI>;uzb)}y(nZPS56t=yM|{cR>bJ2Nx*a;hDuyLri5-yy?&nAor1$A zIT5x0Yjxl3`@Z-6Qcw;2cHi%Ju||JCP2cYW8k)G3KEKwha=HKfd8`*eLr*4UZ*E-r z<;5*HgL|={vfGkQVfAU7wrtx59{3aK$j;0212tg6=IiRt{XbdT&RX!x?bPY9UQbK6 zeK@7Pe#z?f`>b-e-Av28v!ifs>egORP=dU&fSqaX|JQ5TS#Pz1grxi*Ty&TBO`lsD zHp?_SY~TG`8ewZSH-WF%a^mu z*KD{uF)S)`>6gXwe>HTsUI}`8jsKdt;EP|cUVaofGB*Ozvk6WeyD=yb``so~3RtiPrZo&NNu8!Yu*h;ulwW^a2rD2H9}Z3NW| zbIb2lYBn)}mhD8<|NRzz_caANZMz zK7tmxi0Rxey&fC<$7S^<4jot7egFUcc2-N6#=gw%=Slx*bM9Ba-JX zk)QQm?f1JPcW+ONt9tpRS^kfL>j6c7!9ShTg7Wu%4cm7rTb=2&?)I3g3cl*QzusEL z!QvWzM8E#$>6I&2YHF8%K5Op((RfM_%i%??5li14`v0T<{|wM7qmtKaw}-9mo}ItX zvi6Cx{K;KTZU!5JqS9ujZhE(7WA33A&de(-0$)Cv?C%Ha_ysL(h-8@_U;p>(m#OP( zmIf7uUU$8qe=Mo`(@FKqyI!x0Hl5E4iUV+gaaUZx;Pt+l;4JjuJ(KyJ0%x-ezvQal zT-1x0#_Xz~TlYz1GecbY-O`s2+vU$iy*%>64>aU(tN6TaFpK1;5>Qt@d)-bo+1f7` zAzl5j9K&Zj;Pi<)O{f6sjX&$DZ0NB5|TIZjRPw>3L0q}!UbXNAT4>v7e( zx3^sli%w-cxToiYuw3E3!gYcS-Fg**COucKe*67?zrFRb>-+zGEju`8-VE0OjWhQu z9`~-f8nxv^bluOVn?JAkw*N(z1mlH_+|V_fKmEB|*jt;u@wMN+@_Uu)e_w^~KXr^t z+ODSJ*AaEQiFV&M_D`LkcJ%wjDjlX^L%*1%B6s~3uYR1n{qD4R_5XebUC=%+{o6gk za98oM+VJeIym=EpP0M-u+x7J(j-X>3KVP}}SLkO_%}$StLIDSK7JsmZ))WrwIeA`h zog@NY4aKYe;YzT7>UEiei`NC6@sN0I?bZ#P`Rqm4M^J~lYRezH@xJbgq_LH0T z4vsUfY(pfRy z^XmS-j!$o#!;w_;(mVbXTXVnR$xm0Ve$z0vQC66;eBO+sm9F3Sb6h%dgj>+2+i?3M zD=Y8G>i&L$bF80E3Et!oy>-vs!nIe|U8^y?@z3zM%;c+qJ{|&h#g!R;U!MPO%dMB& zqW_;UKA-abGeg+!-un8<>=xQ*9V8iVapdIge!FdI+1g!tOdBSG4og<_?~xV1t^7io z@rg}o>3)}e9H-ns>%2CH|A%jjdST0`d#37q5NL}^hDGD__&Uqo&!W;wdlH;kHYBAT zx?6U8ZHnmHY^58bj2oQ!ESGGt6H}_^vDp9Vlys7lxAJ8EjTeCeE)aM4M- z$@l2AtUxywPo?~xpCT_o4QDlt9iaa8mS8i&=;9>ZQ$LQG@0(b9xckq= zLmEx>f@?a%QUn`17r3*WUc?$RZ_(ATdAn}Sd#=6j+Yz0#>OT+p>m#H({?-5gUcdPy z%OdN9Eyq?(Q#v8O+1D+5DJZ|6h&twK`_ysfr@vW{@OvnD@a*63_wC>Ac-(hW({+L2 zF6Qf#J~jSY9UDAF@ZhD4*oy11<*DDZ&a_BsGDTm^%D?PuzBX%8YR>QYJBugUGfg;e z`#q-W$wc=})oQzHrY&DFJ)<(r?nb@tckdcLr3P```zp{R$1PUS>y=UCqyM*tlK4 z_F|UA!Id0yM59lNe!l)FYIW?|pNqD}%(w(of zckZ0BS0dw7__0d&2A!zwqQNZwN>3+CxoptgWnA|<`o7o2Ng7cOzH=-#Zt)Jk?JfC# z--YNj!wL0EB<8PKEmA7Hxzug@Wr;igPHC_Ax#*-D^;Y!w^{yglvMF5ediUs!)T1H) z_U%}^LuPZW+o|`9A8mV7#8S9eyP)LsndmxK21BOhl^3?n*k4n*d~VsPV^2?CcQ5$X zV|?xe+duoaTP|-Bo3v5)-S-ztGO@FiGs1kf-qR1#_yS?Atspn-YSEEfi~D9xi4!Bc}+?krjz`}0Tg)KA}z?Xrkt zIoK7erae{q_Ttb$mSbIy*M**v22bmWFm96IfAi(C|M^{}pxKvmYBDQQt2f@MGU&b* zQutPI%1YI#yRT)1XYb3nC^4Dui^)^n(^;CzO;H!u8a1_EoNCBLv3Rvf=A|V%5umxS zG+`^3?#+ddkCkqXKKeFM;_-UduDp{I7fGkq*e!Y*xAZ`+?sxAy$0x40aElMy@gF|# zs6O`8g=dif4TmHVFYwM0{N#b`)vd@Zt z58Cpp>*IlaA6oTOg#B8IE3QxabRb?=_l#1<*0W}}Qy%Ydy;=DG$}FV?LSn9}yXUTFdfnmGM>et*2blAT48*WWnb|^GF*7Wb6=l1%!{of5w zr<54?GJdkYE%!9ks{Nk9>Cv{6$MIm4>{&H1qjqFHjBwhiPr&vTgeX(rZ1sj4Mm`OH_jf0T>$jnHVp= zQf*@S`KD)T%%bj;lJ4$}(?2D+R_>F?*juA^>_qVEU()$|47u01aBtu!JMts5vS9U} zh`Cdv&qxb1xL6j}f;t85p3I;nKRG>XGb+U!XM$Q=S=Ys$>U2iFkxpJzYI@R9M*Wrk zIptSs-!%R2mY)6o{?xkNKNra@>Q1Ta3#?;Vy=&Joj;v!n(?QZ;y*%Wmo?@$0VTXKvK5Ub{_e-MbBU zZvGXDO`Bi+Zs(e-QI!VkogKr#Q2-hbD2YC>;Qw8<&CDQot2Q6%v;B5MRZs6&?eFRJKd0yX*{-VR!w5+xOHbmp#v@#y zfP%Fhz)oy*6FG6*;Iisw!R-5{@MZ;A3gqArQ2~S1Udd3$gN=fi>7dNTF~c-z?QWC} z8en~mtC*P5LNjwv>;V}jkm16}xY=z@>2-K@1~)Mv)ZxGytzJY25Mm7jBU3MEzhKAK zceCJ?J1F+SX@3B4ChxB&z%@-0g5iC@agE zRhMqSy$fet=m71Mi+H_mCc+@Fl?o0ERy8z4hDu4py$feZ1v(s9Bba@6G{PWT-3Ue) zB5cWxB;B8`{`2i@s>Hfq{XsILO_JVcj{ImkbOH zoCO|{#S9GMLLkhTKL1h>1A_yDr;B4q1>>8$+;d`5YyW?|f6sjP&bO1aITc$3ni>_v z^^Yh`6bb4QaJaJSg4mWM%}d=k7H#{ddo4V4fmWDykgL~(s)HO~S&wlsU0cv9#G)cD zr00FYcK6MgZ~LtOf41FyJ|(T}+?$yxWi#<_127wO5Z1?V=;q~ zMM-@_<>$2h=cZ0vZ7J%8ZU@K=g=U7vNH!TW#Rdi@4h2-g#lPWzPF1d7uONo=7+H)s z1ZJcjNEF776}s#gEcH61HEzwme(TxU z*||42rIsC&Oi$4}6UMShAR{w#auNT~lPUO(~Zyrsb=T%|tOrT*OYSboTg zMZjS}2`k4LukCMl6tM_&OgOO6x&6)5@VJfBDpyV)k0lWIRS%Wrx0>eBD|cN!ZRtKaQ>{^slT`0abEzyGrT&~N?D!usiy;5FN} zZCi3n`F3~hqfYfd2XY=A=~NY+vG>=j)!VYKuUqo!)#l~r7v9RVv2TC+VeScc*=_mv z^Il(H|NVA`4%6o8aaAY(-|s(l$}4(D!NRk1t<7J4p7_o;KYzXL=QGA)-A6Cw^2K~_ zGv9OY%UsFVR%~$>B=R+`?w3wJbL6Ff14H8^L5;oj)%ucAdLoQ00up-$;g6nLyRrTM zKcBzNeP46@|HIk4s*{r!*S>I;-?V+tQ{9!bEA0E2YHMr1%{*UoY}fAHm*bAMa0pM5A_w_9BQ+x_49Mt_|5YVZHD zR6kW?V%_9_dULAZ?R;szNeo+B_2R)42VZgBs3+gI%rHvrlCAx6QMKTx zyI!E}sl2^k!|W>p^CxaTXLb6^&+OdXt@rPNW67; z+NbhEp&3i8S_%t}OLeEW-s3ubWphAd)(zLaGp>_p(?1 z{vZeOX_Badf%dg&4|fSTC|qKi_5W%7zo+qk9vZwl`#HS+YxuU5lao}0c`JV&^4ZK~?wWeD;yo3;gM%mk{cQh#v+q0`%Q#-%i<_R2?g zg+Ji`_kjP++x+_KKTOtER&VB2zuOr9=aG2bhoAHR|D0cT*7W+3?fu_c+uppA;RhJl`BtK_f#iPz|tKIYRFhlH?0y~md-H#q#{x>NmrZ(YQ)|6TQ$ea&yaxw*ML zG|WEY-zWY5mrU$?pQ$G~D#SBIZ_D|}U;6E4x_WDB)JnO&{My&ipG&p+jNO=KCHy=8 z@6Gv~$H#i>_(RY79}U{a#I+}UecapdfWzN1-+#)@&T0eIX8{(BOq&ngEJ|S!a8OWh zy#M!I`M(Y3Z)^Vje7>Cbv=G0HiTSjum6>ua8rL42Z%ie4}e(Zap{+)fZmit~``ml-L?#F`rlU<+Q z|2*&e47JUEF(L1LXPf;Lj?LDX)VBPk8fTT?_KXvgs**J{F15V;`t|F-57+Pi-h2OZ zd#Ktw;caWGW}OJOWod3(@p8xFT=lywPQo3l)jQknSSEiJIGGyww?szw7f(xUjP=gX z>6ednK0mST?VWu)->6&j*52Dyy1G8N`EkGfz5jJ*W*Q%tHCz2${{N5T|H@tGUtUnj z$}eY=v1{_YY2Et!Eb2bDY?!}#jh=>YBJbwQ6Y~E021qaEdm6^?R6Fg<-7DpesU|l5 zUjzHMUYX7I=g)I{|5uUA82&f1%Y8V%rr@jW`wI6z)*&n2yUfb^bwB;ChO6yrKkK(! z5?#MkhUQz>Uf19CLdl-PPj|_~)vr#j-k<#|Cxdg9vl-vY=NIoESU#`nRQYv*n(wlw zzFMz&e82wx-+%W{EcKrL>$?5D#z?c$l6r5hpXT& z*}UF3=f?``ZFPHAZod=0?~|zgk0SQ#`+gO!l`}X!UB7(w-}LS4*Li==sQuU--@|S> z%inw9qHEa~`DgBazwh?^_|x%*U;5+!eVYF7qyFwUM)iG5k2Lr`oX{rw>RowfH`Ct# zzt;cXw7&Lj_Q$I?r-c4cWVH|1Vv;+ruX}y1^6KvZ#guK`IbWPZkB98m z@UeKcVzHY|PxkVdTM_K%m%eq|93LG$`)<)`UC%PE`uZP-qA4Q{{1Nbf1~xA9k%Jw?%%gB38??P`R^R{L!e03+5fon`e|&D{N~Qi%l6CKV${yb zihgsM_4~|MHH+tSivN7@+B@fqvWxK6)$K>p?SDsJUUL6sRm{udz0a0itZ~V&zJ2@6 zm0?#m_#+bmAZL{Xc!{@9E4nE)DFL_`4w_-~8*JAdhd4y7hle_TRl}*Xr3$ zCRe}jeP6p?o@>>fD#4e(KrM&;?)^>*+y5M||I;o%>%?#We^31X%&V}PURCpA*A-#M z#Cg+lPVIWkZu@-o+$+hRwh8Af9`o$4{o)+CH$>!%<{uAU*QRyqg?p=nR@OdUpm;Dm zzIN+>OAj64FILyh&Mkdg!n^a$@&@PojIpb$d|qx|F(IOSaj3_N^9v8P<%rjci?qEo zUh&>PDc`4DhxL5ay+6;)kKb0CKVQ3iUdef`pf5YGP118IvR!qB+jsAiE0_J|*Rm$8 zn*MG7>vg-|9Fxxf(!cQi4|e+>j+fH|5_dh*pS;+5+qUIwf&cm#|4Y0rSNgc&mFMdB z>;I)`9`Kpl6W2H^`iishSF5eDi8Eh)IA*@@qNwk08>x0sCj6JKI{$v+)`?6%%lCbr z>soktV$AD^75jhQtUDLGxc}MvV-dB-?{59n11ixc$TKo+{$o)j0xHq-nWXfVy4osl ziv6N|zBN_;=Lz>oYJacwN1XLs%HZ;UYRu%i-SYc7pI_{cYt^~+?A@eg4PN$hzA}d- zHi&(_w(HNQ(~oz*I==M~H*4N5w!aC!u_9fW99PusK6WmN%dq;l-0rKUeMR+)h3%Kx zjBkZsFZlcQ`trTji{D*Xo*jJQBdDG8f8+ae>+dOYT{L;Qv#on=#6~5#v=_%WP2;b5 zz+Bg`{Q9fFrScb6&F4P7#cuW{E%_}^HqXu5@vu!rKX!W+`>W&2+RkYGI8he3-j?_D z<$qi<`#!Yl|CrDEwIk@n%*>NZdy8{-$9xagShm6Ebm{fj=k4KDx~skQb{^rs9MYd7 zE?|V`p;@UQ+(}x(HX1TXE3FFH40rYXV;5g zruK8&Jk<5L4qjRP<@Wu5+v?SQSU3H$%M84+!gb-6e`POrKdU6Z{L;EBX}ed5cZmIc;QM#uy06CR=S=R}`gb2<6F>J;Tj<|H`2szi;?-B9 z&+69yXs@3kGA(m&mBmi^X^zgjLf)TfSDuc( zl>4cALT2-;xp{o6KA*Eb&wu$#a=h^T?7$fd;#8t8%?-A7zca}U?2q=3XbKyE8)W&wif4uaszqQP7?yMbFSF(A{%WS8btkEki+V`+d^-_bt2iT2tT6ij8=< zEpwj7&fWWkXO;WzO%WFVF3-kQyZm-dUAyW1IaSj$FV-_$sGp`e@Bb5J7gerD8GY6A;^Amw zGXCy-+5W}_dy#EBkDm2>a_@EI!%5s(F)ya?xVzV{JSX?^o$x%)S9d4hSbTPB>3ONL zrLKDuFX{ebe7o+vZ2Z9q&loQ+QZ%0RTIYoA>`gDv&EKIH|8|Phw4KE}?sdM3obr0n zuT{$@v>nXiGyDEH`tWBFjh!FV?|$5z`#9#7-}AGj_TnmRt%|?qJh6JXi%C=WmEqEb zU7g+B7tdL}Uh`wZQ*h8Nb?|8ncqCgQdS#{hl_{FRLfvgub1UXtscN-4z|s2Y>s$|; zUZ)z}UuRZ@&y1c||F81@)A#@Xz5o4ojf1n}-e{BKQ>#vA79KCS{pqjGrE-hQ_b*+^ zzF3g+H}~@WEk6IHC+vz}c)x#H-_L)u?So>CYtI$`-5t2%{SS}QL~Bqp_P`5PiAk1K z&V8U^k|(|ikB)TK>Fa)Nx)hUqQ-{ml(R`Hr?v;O7_>CvdW$EA7>@MjlTbD+U+S{-Y}g!axZ1pgIL?VCx7$*f6M>Y zEdS@g`?t&FFL&C{D_M|pN^P#iL*IqBJ(m~R{xzBSFGIXl`_Q=zW-lou*TBaX@4%;{|(+#9{R}c-PS6Bf9L=I>6hQ9 zv0-9qz=Sv7B+nKv+$St0wr+#d^>q$DhkdtdoVl_nA^h#4^IO^sca;?$HY^pt=TzhN zXLA1s{_nD7^_Sf>-to7F_j_Oc?*IRbzq?kP$nC>dHVV{#j(xiM=bZj;@7)ylJzghg zqYMgK5x<55YYMi>NHVerobYsDY-!_2wSJ>%$a1O1*|EE>A!D9?Yn0TXV4d!&hMlgq zcAFw&7#7u)07lcJc4Izs|kj8(gyd=-Jukzd!S983Y@w`}tbr-YQ*_h5MO|w@z;| z{W_(zF*1PvNX=F2`NuEv`d+Dfb#`g`-LuApg~xUoc5hob@ALEX{ohM`bfUS|7ytdY zI&WvqIrrb?i!a>&7?feWPLPpBK*N=hX>(AH={iY9mQzs&Sku<;|F`S@VdXa$HXONU z{qA_6mB!x-1-1t^y_B8uF5+j*^op~cdmkK{qr7p?JD%mUrBgPqSbq5Tz3+DQkKV`s zeHFef?X1-Q_N`S<&&)E_w$q>5H`DiK$@PF&Z-+%9i=7uRLD5@4A35@7llj zzVCa^?zDdIw^_2qXAC`$Udaw9E`J$i_hljfOeW>>?E$=*S9Uq+wEpqS@L0-ZeAU}= zk96P5Hs0)YZg-!!%x7iw94=nTRr_`dH(qQUZ?uw-8=X0FawS9gs{#yye1^N@3k94<0jF9!);e?JC<+d-u)u} zMXdh>A4j)GEpNG+k5v^W#=fe&y84{%O}=I?bNiXrVP@y8`ts}+C}w{Ok$!P58|;Q1k3++-fz89#d1|~Ys{|iTu~-A zk&L}|_EJJGpG6lJ&slog|8>#d7*<*R!o5|ycPi%D|NnVj*nH;h&CA2GIM!7q?%JTU z@&QMFQK9ye@FwFU#}K@Ao>@9p`>}wh+`gTEQhS!*%P^ zmF7$wB5M!KzV}6~USgu&hl}p=re~W)wq#WFw(+mp-Y;g}`1f(Y{X2eZ&+Ye}`E8%9 z5A)};Jy!Me*7bd2?;k!pw>H7?@b4`zW?i~_@cdNXs%satb&ZOPr?O6!&D>jeVE1lW zH9xU=dHZut?fTojbMCjdx1WE{DY-uD*BZZ ztt+3FZC~#!_FrPHv)FPkt*WD<;WwV!ecySY{k87$4L(mz@)z&+&bZX*^}<*0{{M?L z=kBJP#Mhg1eU#N-`0Q~ICUsWrae*;bWq4*di8Lj>t2zP>4$c|-*-E%=A)}U z%VjH8u~)Tahk4C^9A>{e_sX4`pjW;hTlH%c?RM_$TPpDKob~&j^XC^UcD&Lxxz>HC zd40`O?QPlD_5NSkr5A3$cE^uL-Nn09uEu`7$p3qN-PhG01q?qXyh>d3a9ZBQ3#;?% zKF>bJ*`u3Dnb-X4_mnN&(p4+ZpVhDX$o;<{ zFZR*K&p&s1+2mcwzN;GCedt^1>uX25HBSWVOX>F8eDb)Y@;|GbRcEG{xr?7Ob8Nm9 zYf)l#l;|hxhlljPuDTo(^7+|W?_I$wxX)ZM{=RbgyicoNGA;8d_1m7ih3{qG$z5NV zcGmoN{|sum9E+$wemQ$1c;HV#obj2#Y1iIb&>$ugw@%cS6Lw!$`u{B7c3Jkx6>T5> zyNqY=emxu8^iO;D>~5dQ7V$+VRlhx!|9@k?{mb+J|D6BZx%TQ~*YX#|wjLs0z1Q!U zs?RN%^nY=Pz0cLdj{lF|zsu=oA9LR7wNCifHS?}^Ke_w9ZhP(j-}lYGGF-Tz^)>7= zzsgzvCGsD+#6(xcR=r&MZ=e=KQ_x%=c0YsRj9Q?x!IQrr5+ zPvg%eUzS#aC{>BLDZI{L;USudAl-cz#9el}yzO zDHC?@T}RjQTeW@L`SFoi`vZ%|uX-u+Ia{2;6NkH(p5f6B_I1xKF!{$*YHUhTN+Ij3Izcvp3I zv-R?7y%1~FdAGN=Tc^)>ZodEL*}4ZGA2jp-TDZE&*_d+T$!WKbS-EY? zeSU8C={l~t_P;Ln&w94z&X>os5H5c{j~-LpTRo~!?6^O`SzuiOpas-bgv z!ig)Yx6g;Z`q%ls)X!F=B5U2MlI~aQ-Z#9PvhrTSF2Uw^Eh}BWhSi3a|2=V?*Wim} zP)F_6(D0dUx3mnrzt0R4SiWCZN@?9^?Uc)=D(Bo^d@DY`^7{1GIR=yY4i+K^> zeyH;5|1X#Q{dFI0?|)spYTKmbT}NYgSNrKs=4Y3^ENeggeZec!r=Kg{6+fMKd9L=y znsst7PwmWISz7Y?Zt+ZZ=ocrpm=ojS+Z&%+_{B=;KB!@TlvVfPlfB)_C zwy)bawNx*h;p+S)Z}(fX|8L9p|Bn41`8?>o=--I*BKuZ+@VNd|-r%YHF2C!^ z9*(W&1ivu*C@#nm6^>J<0zm@8fo}IXy z?)T9B!bi8;$2!F)``&(?Ci)UT4YI9ZY(z<@GuX8>2{3}`i zboKd-P4li~7d zYF0wbXDQ&6114C-5T_aEcfZPL0R~!lr4w;++Gp2@B7j6YBMI%_n6b!Tt z4BFk%riMXW4P8d2&6l$D*MUoakO*kh0P4mC2bfsWSlOO^Kr;}`U8?Gk5Ek|J=v8pQ zKpg^emqPH4f7B0|aNkRf&KQ>dN zm{`)5UX6><#TM+1tQ=>!c+Nb;W~!HW!+|whZgsDAMe-iZw;CH58Y4#|6KiBTXf*EI zXZQNuJHF7c)})O$Hn_i-tJ&c&lQU#`WbFz*^mv#k4w`5N4N`5mcQ0=DyIrqyZf;7I z^p&gsQ~3Ls`98xnXD%)EetS$hUuN4pQI09Q85$#ZZZ`mrhau8{K*yW|vE_GD&z?OC zY6;q#NOL}TaIiTnI5;^xwlp;8;!NXowY|@InF0kB47$C%gZ)vHS)&IBXx76l|6b1Z z`1-xIFTLZrVxPZyl@-0Ort-xn5tjvXKz*P&(bIQ<3kO8Pb@5uTdiCjdJD4$ z=Z5ilo5%0%b8ng07pz{j>eMR<5w`^~EM95`+TsPT8qrd?!=eM<-rn}@zsT2qXJc}^ z?T-h|WpB4$kE*S$eWdADv-aod68E#w`FpSa`u+Rwr_0Ozx36Bm@0OqK*DHeGmoqRu zRSPg`jNEtWTT!;dt7cTQ18y;Ko&K~jV^RIzGP~DnHgB@KQ~7-EbM~^WTen`jd^vfx zS?(t5cRLR6`}0))w)p-ZuE7<@njEIE%s8y$wbnOQ5UChqU}QNJl<@c0*Kc=<&)$!%BgV2wEx^e3%%W=s zSHxi%4{B`V1}15ec)0QiI}p(Avk#Q%ZWRD=2%>;x32z{WBss2 z_|a^+suzmUd#kqAewsXgi>mjuAH~z|)_gu&_U&f+?LC#B|9JkNU;8ccvaI=Zz1S@E zxg|k0@o@?QqKr(NS8Or)>VjJObWCwbefnU_)6mdR&#!V45+Cko-!syEm3i&Pjg0%h zu5Dklef#zJ@bK{T=kx3L#g^SneRk@n^bd9>LBSOt-qi>O$L{1#b%~mjg%%bfQx7ce zw|fW1*K66H zS#;^q6}0+Tp@`|_AtS{lv-kVFPCGY8(o$Kg$HA)c;J=wKw{74Z^R~}?yNjqqkTW4x=$R znnW>j#b_QJEo4B&<`67JU7Q!JUw?kr?%j(6BeOakx%P`dJp+eys!DMyM}#OAc#(2$TDs?%eVs=vKC88c%ohr)!g z2D!A%$cRp~m~(i^`R2yP!*Vqr9Dg5Rw@di?_3P4b#&r^nN`e|Q<@zh73oe2G@ndI$dl1=AySC{jj$nZFle9eRi+j z?yg`(7A^%)dyy-koDai|H#u@{Y&e*-aYpU8>i$mcOrgZoTYYdilM|8=1@JPAhr4yZrsO+xh$N>h1fHWc|3unB(-Ml8RMD7UOqn=R1QPf*^H}6Kp*2z#Okvg1X0QJi z9vV8e$aF^;%dwaSxwK@zbDT&)Vxe^5y8PyL^KCbG>Au~zcJ1l=2HRKOo2EM_Nn6Z3 zvNZJQ#6x>P25%CXVVvHV`OoN??rEXEM-#;r47&fgGcdqYU*kui4BI=|+t=i7oObQq z67L(kmS6r-krO=o?uMdyrsmgFa-Z`uaVSjaWk`%Pi<;t^5YV7FvqlIhy-Tn&e%0|7 zpQaku)nqa?FcQ`XlCWlE+Prb|GasbHdxX`5$9QXcWKHrRZo*2_BX6kA-jvwyX$f^M zhnRu^_qIgjE)}bA#oE$>rvC=&1?J2g!PAVOwi#40s{NJUtLToD7Zj#A{C7BWZM)1C zce8if-|b^uz2X7ijD-(0TEI#AfCRU|j9x>F-PQqcH`OKx%KYz z`2!Lk9^SOm_v=OH#n3g>J&PF{Z(2MmMhrhSeiHR~;Pulf z&=}&6u7HLEYdEA^y+JVq2}zCE#*)1!g|dyeF1&Rv(&Xy7Z%!L-%iT14G}q|4>Ghjk zM}0fnADBW@Cg$kOf?yV)a5_!({nwDK&#ro~T zF|+9HOYh#fYhao_vn_E|x@6)_1G6wlgFB#rk!f?2a4{3ad!WIwBB2vo%_Ao?GHsmd zJ?pP~faEo3bNj#zE`b@nYt7RUfvgeOSdnk^K5zPOYxeggGb2RJ%_9TyHyzou2f78L z@eybQ?A^DAuONX1ic&6x3)khjO?ixB@;7zfyOR<;`I(yb0o7tyxhmnz$h6t;$g>cL zPeBfStbd_uhxuEV-_hSoX36#EW^G=5-|Y6a{beVree*Yc%r{&I35Ujw;tB@R)y3yJ zz{vtc82B;0ik$Xx-IQHxrd`YXUC+NKNA%wRm;diBn!fzZQuCRgAAly`9T*ymKrVfL zZD}6DT2H23(o2~(PMp@&#noNeDl*B?{H8}#4rDZM!5=J4;OZO2~p^WK>G zS-oA)-w_%W8ld%GpEA9(z$4b+`gVfSf(dUKp+gd;>~+f==_yC z5y|FjqZ0V@b2gVo-^z`Sp1wb_cv_~!vZ`r<(^R`YN`d{`_<~blhU@yXD-EG8QwaFN z^po#okoFpfoTro0{!iT1`yCoo4GfKv^TE2nMB^d>jS`PAk*QCVpa~nZd9c8Q<=3y| zNZwKv746=#r&@xb(8DsO!3rwIXO|iMU0ZGC+kf0_>(9wA!$U)EOkOXu;r^bQ17`1D zY>1fk`eMoNUAt3fSf0G}=A0HZjW{HPFtMa9ew2)~DT;GNdgL-||5ZNT+1qRXut%?1 zZl1Q$#C(B@%48u}@hbw_3uU;4={h)Bfru$P8s1r~bjVqC0hi6%4L1v7{yIp8E+6MutWuagOTcr~ft1SetK|(VufC|86aF z*hAlE@@bsmT4yF|z(>18+!_w7F<2)f3XTy*76A>>bl~6o@yhc%U><0sM36J3c=_pLv%G$Puyntj`B^P8^l3s> z+2K=_Wv6e6WoN$ii`ItM09)LXRH5Ol5D>x0wE57lq7ryOXRz+7Vt=#LcJ8-F@A#?< z!!~G`b{kKDg`U;;9ZPbnP_!bqty>e%G&BfOf%y;#?pZv?I z@y03Dv;UM!rQbtRx&uRFkf4Hr_S9*Qte_$ba#&4n*6piKUHSb+*!u4?nl>J6SaX@L z^n1F=6Tj(_l{aAJwxE!LL3coMa46Jk8g`A>4)4u;{x|5x(&NdqZ~dv=(N`tuo!@8lnQmN{r6DsrLH$h5hR4hO zH&o?+s@wje>e_Z0o;h3YPkrn+-LDCj_cX#8nKl>YM!sqVCkzHASN#QvGaSpO`0S~e z`tzCo_UPKu)LT1`3!UC|-My;c^oQb2PGj{OZ!}Buif^T}7TtMiq|2)f4Jm~r(3n+r z!7A8<%TeV8)}C##*Y>`cdbWW1^u((ER$Kn^T|C;ZuqKv9I>`RK*jL~UAq@;nu9^!L z_V4EZRAHuKRe~EklDek@PmDfk((>Bf!Z_@nBwJ{_#>_+Nxe}C)m z5gm6m;gylXgjR;eNU;bJ_$-VN>!FsU&ZoigyXBHszP`NIKJOOG?k8JbzBJp27+_R5 z&%}~e$@a7W8f^w;Ok8VB3imx@x_#~1)A<#7!FzS1Esloin9 z^OA1L#n+zdn-%dmetw^DTHxFWs23O*S&q3i9LUL;V+kw&oPo;E>3|afa*hsX%D-Eoflknsuo)wR3q+=G$+l?`+9(w3&0s<#(Q+ zVZGhPg+GtF-450X)|gh|yK=qh&VRhx&r{cQA2LE}SUWH@ZWL57kd{tA2MJOK6&8^c z(W;Im3Z;CkNy@rf#gF^9&042Df8h-76EXf9C$3YzT=umgc!jxG-pqB)xzO-yU|@1* z;W*P5@qr6kPP!>hIIlSArQYL!@cmm$zf|?x{eJoR_a#x!-FeUJ^7}RjTBYx_JQ^dg z?BV2hrFKpmw}?;M5VZGrm|o^LdCt|9@H#-jz>kR~?J>*wOo*p7)Ei^kJa+vm|IQn6 z`{BL{iEGc!`YsF5sf?ZfmG{UUJ+bbiYZfK7h~%s&IkRWezyG{`Th=S+6xtb{)ix7b z81d&(C1R2!;0qH=n(KX+7;r`5Ai{DiOCx!q&Spua%FF(78#zshwd%}4h-(}e8b5)i9@l?cVg?QhRgHib3wwSl1+FOHu=+e-g|qVG z^K)y9tMkPuu73Dq8vanW_JV#mlcP zUH1$TB}=eFEC;0!rqVb;$Lwt+n{a8Q@r=^p18zU zs@GnBm5}aVdTjQCf9ucBe*RTNn&+h2)%5@Cm9@ppZzj)n+Xrr;1;e|3AkS- zz-CLURA^8tEC$u~lH1kuYF_NDExRH8PlEqwTa*4>+o0E6bL%T*59TD*PWAo9w9d2h zyzr{GAJ6{IeZTPDk<+|2wQmB2-&{#nW6R^;>{vc^PvTRX-Kn|VTep-gzw4Sf^VA9* zSe^vckT*F5W~455jf9l?pL8~?U90^?@Vm^Ljq%a?)km`c<;FhbKNYlOG|!u+8)@m z!27VQdiCCeLi1x4rdg;*%sQO9HEU0shnB!)gR2V`u8It8Is{H+5Q2wOV8+KlP*4gu zC^#z2FJI%sdpz#e<<#4^cb}b|_vVDq=Qi#(8AFSs(!YN2t-pR{LtJ8=LRhM4sJq%x z`=X|el}80mZY+GJ12GO1GRevg31(*J=71}&1uQIX#m7$_DR}eLc5WU2ro@L4Qq4M_ zjG{|#eOdI*^Qgc{6W%k>HsUeI1&bFSzIFTdp|`)+u=?CR}3&#Hd+tCv-M&R;rz$Gy4RL*9KmA>esE39`^x!QdN{{{BCk z*2e9wdt%( zPxTxU!rtln`~?^0lUR&+cW${mcVWBxy4rQ6M>cgnyuqX|V||YMDO0f9dEGRZ=JTA zpT8~8SW5ft6G_psXK%b#OI4aAo#WBt$^L$5L-Soj>kG-(?mcBqsk&C3|5x#S>7zx~ z_Nlur2b-0z36YHi$1SLMOA_AkxX=0xXe0XG>hDqI<=gGmZ+3}lyY2Bg8^7$+_Ip*U zzkdJz`{{E3`FWsCU~{WptyF&J&&YDhcR|wSWxoGvlf`wTLh?!%+kqp4RfJ=f&HcGS z<)xs4=klMJn|gnl4YOar*nG`-@3PNZy! zk*~+J#K60GQCkm~^h|<>m&97|Y^%JI{O9(y(c90Jy}gzC`}_OtUteGUZF}b4?|a{G zxr)b5`Dpa@z31M&d&8omvu$l{pQW-c``~NN{Ns`M{tT}35ds}e4bl%i_%~~8F)Ia4 z6Mxb%xb)s{Zuk5@r?l5^IbZ+pbJ_d7-*0)U z&rRVL*Xzmmu3Z@SgX{E`7eA*2`)vdbM3>#p-F~z9ylwjPdDVHZ^!B(fSi{P3=G3mr zm7q?0fJEbl$f85GZ{Mkx z?b@C>|E^71`nmZzxp5ua*RDOSZw?yZtepI3eZ(D?^P9F5tpWKN65khM_0L;Am#Kc( zDxUND+FD8G%_Z0GfffL~dnNVf`@)jch<1maTmmzuW}gny2Q6rCV0yi6OS|2#W3sYo z?nbKvelPvTBQ4hEyJq9EwpU9Gbj~!2mf3F0d}5t?&U)3kb=AjZnl>7&m;&qAEE2r1 zyj(|>+`{+s@<(P0XUQN8sd6-UToh)Y~N z6`wnE^WT4LizvIf`uDCiN2_!0K5k7=3^u6u+t9M9PU2Z>>?_`7iIaYE9ljJ-bIOZ% z@s&MUQd+C-<-Zhbi&+M(Y#5k0M0PhYZFs$Y|2>m~|4*vV&nYb}-IjQm?Z4m5;%8?* zUf*eD^W}o`wyLjLzn`evXL?W5`S@IC_s?gu-@e&=ep}_|XFn(Zv;X~OGibv?^sbVX z_H!=3J#CD#G)AGtA>Dt!yozdD^7^f%(fReCzWlm3XT!a_YR)njv=>cyb^iR!$L+~y zEx++_moF<=Dsn0FP#v#!sJ~@fOk43{5onuMWP3wZ_seCB_P=kQf3xrRyU+Szd(WP; z`Rud%QJ40k&yzEos}^Mzmh)fV*W26sZEb$->G?n6Z;R}hTmSE8+0E4HM^9(kax(=A zKG2&Pn3?&ilYvQ9b%EzsZrK-azP@u9lCid8a36m_2o4d zOAi^grpVeh|4%hMV>oNC;*`&QtF|ZaZJ!tYt@QTa^E~I;4KMwhr7<6#`5h8+SgvHB z@x3ajG)_;u)tk%V;?|%zb7E$eRu==)QiTNt+OZMSD&Eej_D(ff6L45o zc)D}!-;ei_{?#PMS$x}NzQiciCH0Y>=laqei)X)jbK{xv!x?e@U)~kX+%d;cKkaoi zAImxBXQ#6441Bkohcx~fps_M#5<_F8=2o%mT?|aA;O^z*i|S!^vmxO!3xi+`p?dcGS_MGE*DTc?+-Zv1XTWm+bA01ppIjv zCuosUs^S9A7l-w}um9fWqE>FeeW&!-l39rlZV z9^MjgP$*)0*Vmh@O#7jh$k(#o+wUD?2}*CQ#EUC z%%wN>ho&s&fqD$o#^F#1-~z4ryWkb0%)~KeFL(g|&g=HI=5JDx=XIFRUp?1od0~OY z(+e9;Ena>lDmAsJBkWs6hyb)>1F1$%Y-VVToabl>D!ByQ7OXBmVD`=;=X{mo&Eng9 zH~YTLJ9szgUhs)MuYbJc{Z=k6BmJxdRyx4UIpNfBK!;nWOR2%Zgtg?km+!IlKabv- zaB`>b*KIiuMgH?mSXsWxXSqoC(LX2u%>R1GkTqp?S!u-o10Ng#aNoc{BzX* zh@U`>5HPDJtl>b;mZmrT3{0uI7pit#&x^cm^yb`D_?WhI*g0e0hWWd;lz*u_th2Wv zXNCC9#&!R$D1H3C^>xS>qGXI zr=OnU#Hb$d9x^xKAQ%Se3Biow5bV`SkL=I}-aCRe2k*DhwyE-$-vY;OD7<^J0@ z@?2M5bL_QX+2`Hjx4w98bqx|mpBYsE4FJVFy}{4Oa>^;8-~9cJWpicY9G=;hmE1VT zZ0r6?^z)%j1(h>ptFN#e+N4|rp5}wa86%5VRKtNaOJ?_XyE8QAux@+*zK=acx=d!< zwZpZI?I)DzOi)_j`E&cbk4#4S?+ja0^xwVOFz;W4 z*UBf`gA&}qxlX8E{hY3&EF}%@%pnrmCb0+8)2^kw zX<%U5$+hR*SBcN>6JE9^&)(DdE86;(k(`(LYMZ=SyKAgI1a;kG~=&@S($Sf@N z9mrtiIOC>$;vdJ9jSln6kDrWr&u96-*X-J(vzBHO6H0$L>++XS;}Z7M zY{3>~P3DpHv8Ab4jC0rrT8^Q6>Z5>$aAR1$-Nxu^+akQP%Y%Biy?goPz2DCp%glBs zKHc+UPGT*yVfD@1&zLryy=QRx+`Ti~H_QnLe%YGDb%L!u0+i58+`V8k+2GO&L>vol zIFMuW)zX~FRcpb#)R%`g9XTC3;hVpG<$+-r~DPS2bf5&rbMOfbLE z-J_pwJgZv0Mn&Ot$}z;88aOf#6O9fRxicivvd(WR5#ReeVg1_5wDqj6alX7)JsEG# zSy>tuC>UaOS2CgJolG5YuJu-|X9+t1A3X8)`;_2DO@*|$UHKc0D$7aS}IqVbW4f`RtA zvWKe#GFD2Y#`+q!FW#G6vJW&^v2DV=RL=_7lmkqwMkOQD=1KWZS`7|m zOnEn+CeQvByh<$CbO#R@biAwe}ugdE94#sh{__bxs<^Y+q3Akz>AY*BO?oUuq`Vbi1#0774vWc@+LIZl`^yuSTAn_nr{$d>7S>$^P9G3LWIaP;$yZ-z?_pjNa_xr0{>b^_ zY~Rax?Y@`FSKfWS_nmaq`@LrMv#+V{xmNf0*8dY$`|kwY{C77x$Y1iuzYopk(e2+) zJQw<$l=t6FT9NNn9-F`-4Od7GfRugCeV%5A2%P$10RPv52ydxF#|~wBV%L z2BZD_={pvlW!;m)2i^z=Yj&!F1}Pu$o_P8^e1Y@98P~nvc;p+lro?W0Ul1RrzUoW! z#wdH9*^gP1xQ>0P`En&{^~b77OQXYd@-`he>#bRSrt)m*vQ0WiRVTcUi{CiUPx?j9 z_K5cH5>;|9ZoJ?B?)JuuuRn_9UAz;`p10p9Cee$PqvfjE>RE*~2Mi-n+_6-}AtB6b z?a{5zd><5)XBn7Ttl6|xd3VB^Q@>?xi%%u^8?q)9w>NFPd9QeD&}N-!S??3f*UU)= zHQAQaxt(rhdCoLdepfjtXqw(*w8ag>U? zmX|W;-d0%r;1+v8n=Eqt2>j9c-qc})BIaRiYgPYU&#L4w7zTMgVTHE zcy_T}=bz@yadcHy(F*4uv9HeWD2ZWSy~|jC`@7}u&F``rUk)$~fH)5dS--aHEnTE8Lkm7$qQPtfP?OpB%0o*GM>zLEiLXwE#f zG4~Bi(%-#i7xrCdi;CY~r~gJO55 z&uFvRWi603PlLBMc*3Oh4NlIAe-+Mf9~SB1-LmglYx2F#Oq;&N-i!IftbE0wJ1(Br zH>~_sYn;S1ffnBxZ&;I?A4V({pAve-kHcoZ4`0paQwKgAdmEu=JFiP|!>Tzpht=wT zyfgf%v+LVq$a*YjwRy}RJg_^9=eU?|lu2Fq!I^>{f)^%uwkEzZeOA@|@~dM0yNx;1 zqF9q>h3h0rEUoG}8(1vfrTY4ocwPI~Pt!0*gK0gXO7BJbWcK`CEcu-6@|++kwdM2dU$s8@E>wJ2t-ZYOJj(_8o%41g5o4Jp@%GP?DVSYT~kWKPsHK88SO@EI) zz9H3ibN}n3g8pkS!#0;?~{G0D)z0$@#&%)ueDD!eFd8fBOElj1!hbw zdfj_@tG2}L>WmG?Z){r_aHci+ZtcG*6${s|S-9=ee(t5SOYYZytLl@z>R})zwebuW zU&)r}_t)a1?|vi#S*R*>vNuGO;%R?@f5~^>6GwnPl640bX}!7VQ@A`!#d0TwQkX#uYZZtke&I zC!%9g_Bn+*~m8 z>YNW|?|F)-%MVAZZ>?MH-+=C#?|7S{6b@QneZ+3Ps z=ldq{`H;c&XrH~uuXLX|eP&bCEYY4y#~Iv5zpP1Evv691jGg>UCo4&pFWW^ z`+2xL`g^ebmUrp$oBrL6{u{e0O(q|@!b@3mlPt7g6>w1SLYb0X|LrC6lx&|khr)!& zh66gA{T;Vuo!hkGbp5~gdeMJh&wX~+!(jE(6~`VPoKZDFuqX9s#I{z`JBHH*wC?Xc zafm@%V0HcL-+$a^d_o>pRXD|zeQnLh>Z!Glgy(-?-UOQNt^0L({w>?@car6Qo^W5} z<9t`}gjd4>9Z-~%3IA!gjPk#KV8g_-odwBT0MQ6!Z)V63TBS z_Wiv*Z#&=ni+{iT{b%6*_1&dO!TOtA@5Q7X=RfBvW^bsE9OnxjFkO$U&b=gX=I7_< zw}bs{r(Suy=7IP@P5}*OMyAa|87^nvnB)c(87xbj;$Cfg?A0pHV_pk5yPpYN-Tdak zUHPs5V)fbLCac}FFL>U*_S+Qu4g1#a-)5)pvN__wT_r8IdEGi6T%fB#KRd#CS(Z4Ted&dqd_<4S(b3AHqi(|0zqecKvweew3{{K@K%u77LXb@E#AmzCFy zpPRg%yZ1^`sq_EC)i-YcNGcFmUJIX61;uv48y1l%wjT~KU%PcHYkFMO%NuiN7%K*_ zF{ujAnA%vnhf7^1^dW27Lp4?F+Ok&hX}tWpkJulxGDUJ++52hrg{8@H5zGAJ&g`yE zy&uTEcfy zt>$zHel``+5d zP~jO#mDf(&dE!uapb*GM(BPfJvMvAqyvuct*1D0Kjx6(? zod%tYy$#w>9vd55cF|Rw_3fc576A`IjhP2@p6>E~mm6Py=HKU&vEME~Z$B1TC0k1S@%4&VKCViWkzXa#ijr_ESXZrH{dP|AxsBp+6$iKF z-StWc+v9t5s_0D6Sq9QiF8bzzIyh^0zdtc2b@}<(8THRvQzyS#{dUUEH0|jo;y<)3 zY?#vi?vCHo_-*b&%~#Xn1a|4LB996#uwXH{Sf84jdgd3vgGR?M}|hw z*$xS_19M-#c)30QZ&CZ-XHS_wKF;{`@B9ALWplr?F>SKo(m&?1p!#q9&4=^Q`aXclz-ga1z^`)}K|%j))q#OC_t zXDo}K&A7Hvp}_%ko^^p=aQ53>#m{dUp8fiDH?!c^lomK5#X@VM(gpd+qNP{w9i*Uc2e%48iaH?~ib!j5A0!e&6%l_S?Ji{kL!3y0vEe z_Urqj(?UZ-PsZ4bcR6eX&FHLD{U-Zv>-uWp-}$$03fdlWv~y07`Q^IslBiJzyc0S|d~GxO547JSMI%`yh9(XJGz|1_2P+xz`@^4fQ^KXSJ& z3~P@4-Dg=AqI~$)BaJ;0TN|pT$?o~R_}c#0M-L=xdc6MeQa4WjI9L8YqxBKKd*!EG zom&?8zz(H=1n1>OWhRz1cb!djS1+3FUSM2F~x@F-BXJ^c)hV z9cF*9KR#pY`+sTEzkZXSdGo*Sao=tH^K5TRWzF<4WDhFi)O?`!gFkf7`q>+FL_|=s zFQ~8rZ&3U2;xp5R_Pr0MzTURw_O@*YPhNU%cJ=nA*5I6@kJMhjdAr?O?tY&CcA2jC zq2aNK;qy#SPn&;3B>p%`I&lCQ=fKc-NmRi=I%{{>d%fx4{`<9eORn7k?Es7T{m1V0 zr)N6W7jDYmuX_{tfBs(U#!ahseLnhMdlgpzTdG*V5l&Z;#|?!Js&7RM`#L)fm(_M2 zt6#M7cj^8mrhm=nmaye6|NiOB1v}GYD-8EcGdot+VVK{kCgsb}$tL8G!NTq!AepN5 zYGeO;t?etA@)SeME#`0Dwd47`-S2N!*MBq3{#yQabNTl-f@c(WmuK&L*5csKVX`h- z_H|s{qS)>0WOu~xKBeu`z`)4F!XcpG0AcJ=S1`E!WGb(YU?k%T=-O**QMDHk`}k4qwp|aYgq5>>q^n~f5T@t zZ)#6^yk})@(Y3d=E+y8V?!2h_a5C_6>GZJFAcbpxXZWYpueB>_M00vYJQLIAvo+P< zUe0^l64{fY^;%&==FvwHmG|#&snXf>aI5{XS!e4ruSHbpZ1R5Jcs4>he*V1J`2Uym z7Zu)J_3X8;x~c7N_w9%6C!JMW{nmWV%T3|P=|w=nVZmfpmbBRi`)}88U&OpQ!)No!TRQibn!R%GIqS6POVs9{dz;Z* ztg#jp*#A$mf9+hJo>#y8Ok(Zs)KqWllq~(N4pG)?&VI=5Zd-aG=clfGU&fs+N7V&m z&CV-$bN?v+eIWAU((wOb^LL{LDhtOHKZk^EH}|dErnax-*M=QGJAWGff4fcmtAApo z*5p%GpL8dP-O~S-aYtsF>HnlxyWOt`F5hE5JLiYs(R)so*!=sSe@}k@uOG)Oe;@n* z^Yf;PcT=C9wN77VdiqlGj7+gv8`ey&pPL#LdwJcIqo(T(v)bh*={}fWwtLOZJ+GHy zcsQT2G4kM#B@S=C1lDfe-*noK^UVf_v(F+^cmJ*b{_ex~TJ-ezqNY)AZv0Xy&X8>m zZx8(Kdgt(V0dtq$Y6X8jY8>?>EU=5OucM-(hz-Ho3_HoT~>?CTDjy70iU*3C&z zZNuFjuUZ=US}Ob1;@gSxeQm0r7lE?KK3Eo+`cNTD2-RB}P7VoSER&R2%+CCGUbE}{ zs*IT3uWWnH?vDSq>%+1aRR=y6pxdh@EHESVX2UDL->g#Rc^##b)sO9(E|*`t>_*%F zKe20OeR$fzo4tAV^^*6Jw)UmjSKj|^{1*O98mV9ex!>U?H^-S%bsZ)flv&NzM#yNt z|2;e7#-AOj4=;YWC7*ou+5WBP?~1SV$;molx9;Ud^QzcS%`0y2ahr)A<173a8zW2m zR>z4dPDq=!cF*@!84)JctCyPZ{rv3Nd8x%4H>zd*=D8;`?Qiy#Z_?YYezHR;?iwGl zhOLR1DEjX1?(&cEL6aQRIXKQ(F-D3$)tn%A_)b{Fx9MTmU8cwVUGzQGzDsW}`@g4~ zj8Rf?#`Xh~RJ|WHvdeXxoUDF)_xpXbYwM@SmUSxkSt!~4``CZHfsy%Et$)12hRv)j zY1YwK{%W}?tc?tNGqK4iF>+(nDM2I`Ey!&&S^VZ!_WIs0Rs|0ZNLUu7T)BGn=pxtd zM>o^w_pS_HeysF*tavjUZ`NAW3^?{+-y(+SD`cYw1|h@(W)aqcSgL|vp| zkbZ8CgmIcrkF51J*NABvd=?KHEZ*<=ob>F>%#DSQkIB4xQDn)#5oS!Qt|I_4b*D-TQ8SaYI4aY*|l8S%;D}9{lA5-Qi=HdTPoH zgG48rPbZWorp>u}{d#(Tf?nJni(Mrzg{H@r-Tb}!$ac;*ER2me{TxcVPl>*`RGsi; z-=BLC=9Nins$YK0xMgX)HgcNWVISkuEvZwj_eJ?33Jr%!&MVihrCBG~{d_X{A`h=I-g4Cu;R0|C zvqi|GKY{CX)W#hFY)o<|b($S7u%LvpLO?GQ=(yS?NB{KO{d#eu^m=Ua!6w#?Nk_YK z%S>A{l->L8q)EyPy~to}jCAMTZI46ZJ(W)xjD9X z_q$!%yUO41yIaI1@InKm`Q$ErG|f8?7#^3Iyf(1xf+KsX5NHALHsxpAA};Y5C*IuF zQgcm`iD~n>fG~D6Z3_?V`~6P(<@%QvpH3*x_S-9@k zH?E!bk+JcdfWVB>woO(jX{mvs@hz85`ad?e#>D}4j2mlqK zV8)Y}go}$@4HFNw_&nTtJ+Av`xA^h@f9C(+Jk@L0Jh_u^E-rTe^8Wt*tIzcWUig4A zNY?6ZuttOlJ`d)W-|G~Qt61o|e!5=ltw*(0!O_vu&h31?pnHU@^Ft4?GR@UcFnH~f z6N+3`H83>p;gqR((D?DO{C^86Y3a>hPl7t2KMslSyU?|8-#)vntgNWm@%tZ_99Sdd?mL?k?nDs7ft3rCy+m(h z`JP+ca6qSj@w)Sq=f;T6#5IPvC_P zCo<=EQ3TuWxQnpHutorrhnE;1fsB2?@+7owlV4 zHIf|8v8wsak(h0k+jUxhzsma#Pnh$KxJD@>DACfo96uY zJGJolGxKSOH8Pmd0;2Ii6D#+l_5WV2Pr9?C&@ku5h8GtXyMHq;d2s=h7#}Cg|Be71 zd1pD{!qFmeff-YOb7ab+nz2F%bamdbyxniF-I-moujuKiXWtjQ^>!WY7EgaY+cdt! zX#oop(`KOst0$rPJ}}|KgM%M$Wv@3auRSqA@ngIFAH}fsadTsTU!MPO%HF6C`)8i; z>||M!7^YNl6}7VfivQ9pffKJ8dRwP{dw18m&-UAmA1D3mg#Lfv|1VJc`&+MN@v|dw z)o(>>e|_Z=5pVKOyNKbPavD`A-Avhwz#I<(wo-RL=4Ew$E<`{DDS9=p@4 zM2yexZ}{}$_nOGfo7!e1hRHo%D2keH6s|LA>dZFJkMo&fpcqu~Od-IFF?+_;p!mCJ z>e!j~|9zYP7*^fd%|{B~?R@U`{oUPS zg~i=ZwG<4ruTQ+Fhw91zRwjR&k1l_H9JkMVck<26&5xf>kM9E=25wmT>dKDi^Qt%b zG6Xd?^V?bYSw0m>jNCkJau6rS886lKai}I;U}So~?|0s|6?OaT{(`cQ>-7saX6P2) zTU6ug>)ZYPZS7>awKX3OvM+Y;mn%Ff8h*s?_Z#8*Kab@ff4iOE54y(W`rq&O+b{E^`_EhkS(D-=y`9II3M@^~TZ@dqbhVSOzul=4|c;Do*j_mwj zSC+S=>Za_`6lZLV+}YDo1}aw&nW6E!;)k8*Yu8mO{wY+iwwPa)URR z<=xql)Fl?XEOYzmEzD^TL8DPwqP-fmYRXIVUmlxHIwx6-)nrn!dm1>+9>sS1zBo>+bL63E6jdl@^}X-R|P->>O5ipn>sb(%*L3vKwXl z_itrlm#avq`F;ETxp#ZF8b!zX8=u)Wr*vDYn(C8-^v9G4`PR-}D7uUz{fBUZQ=9L8^IZHR_$iDn~!I{6e zTW^=fY{_q*PV1YCe)du@XJXoXhi7XaO1b8+m3vLxUaNKc|NXkLqcAz9_UqM)o72zB zzAL(Apdg#6Yr@{Rhm+&XC$mStyitm=P|XQVYrp(>vMKfSBl-Uy<+uNoNaeb+t^CXk zLuGBFnjatDihi|hTqV4rE%GYwN>`ha*Dow| z-n_lv{$ItN^82-sE6lR4tjIaLX?ET&O+Sl=EfQuq5!+t;_txKgWe?k}6BCtZ=RKb4 zkjKK3wo@`o7G>P1F-r{8{QMa2Idw)x2@V8mlbiLhgP2J?) zFZc1V{67P;oEsBjy?2MqE4!6h`0wX)_u!pxnEwl$=yW*yY_7gkaJ+ku#Kg&Zuhl^5 z9FdhYBsZ>f6sH&Zg1nT`!uoNrAp_DZz-Co9Bt}^AztE;Q0I?PH`)5$*i$Vr`>cGKMwSr5Y z>PM$&20!|GJzjtNvP}L)F($c_IR_u|q6V7;fQao?V|J%Phvn?{(`24n?s`5fNPBJlV zp3C#93^jZj9BwnY%a(@RmfzvE;0%k-ulO|PMV6>)6!gI@wlB{W0`i#BW=y@^k-?AZ z*A+>PUp$^M^V5Kzh7(q`la6PH$v_*g)TLJ9?SPLGP8vgzAs%H#KMv`)hOte zCu&mAicvVLw)@+y?9Gv>%a%`#!fER> z{y72~jSZ$}yc*Vdp;lfK+#4;6pBKd;IIcc3YA090L7|ODQt0!uv$t0r`SbJh=JUV4 zz1@A)$WGw`_Rf!j0D(^NV%G&rn8g0-YwkaVn!FwASkBpeJ~P88)eF>qlB;|&G3Qt$ z=q_Q7XOE6{FZQ0US9mjZ`crln|I=X(31Qou7bl|mZx>5ecJ^jj1JK3pwV~6*zOP@sx_e{t z@ndhd-HE)|E5DgK{m1Y9|9?xEW`%saXV2oLn$Q-x^2t@y+K{D4Z9>z=E%PFSBh0d|tDQ+jn(sO6Q5Pm6>$v_)F>wrxYyXP{w|MlCUmcRLQItRn=!ROAjB87 zMC5QeGt*c-Y)ypV@3-6MZ#cuhNQZ?bE!B`m3T=>QRlLI4Wz~N^9)JAn_4j8;h0# zR?Xvk?Rk`o3zIo$Np;$ROG~|zZ)`|B+gtajQ+*TbzyIIz|C{;Qd_1z_#iH&< zz2^5iKxby|>=9;KDj+aps#EM-)RIM^oe9(hy&hk0`?|8K_F=0ysQ3jn<=#ZCkK4QH zdg$t~rvdKMPkSDaN_%P|b;}7gtX4%l*zvfpd;Y&K%Rjzaz5dvrpPv_R+h%sGU;h4` z$2*IkyZQS1etCU;{m-a-er9GS@ac<>w>E8!{QmvTW!t{n_1i6eF0cy>kDkmuDSO>c zwM*}xzS(?U&$FI2O*JuG6Se3$F&}3lX+}#Ve_p!m*<0m&=g*NpGyZ?uC3biI=WDws z{+l;%rw(Xd=Ed#B`;I=J`YG+_r>7Tpm%rB$xf?h8e$D5zpxXwwhp#zoCqAQ8Vy!gl zs30R#D^qD{=}oKD%(a_z`%N~#d9`|dpIz;*oS%Vu_mo$K!yrS-YD_}khY zGt<`Z`Q-Hnbm8Ei?f<{se{@2*U*}C&9kJB~ICbHfa;md7y<; z_|Zjo`QEm+wl(grudVIge9lVS`v14>`(>TmcsfC2a$Z%@HRhLQr5=@A`R3)~Ut-ZU zyPwwm`#e8=w&9%ObC!kg_kKSH8kCc;C`g!7bV{@ER_5~5`}cPgCf9sj9pCFa+idHO zc&Xi63Z1Q{=|oPd{qJA@D|pE#W%s@w{hEi|A5SRvr}RHuX7v3|vA@lS1I&dF8rhGf z+yA~U1`kiglW?u5kPm4PL=8<*! zn+vts=JweY&ldiD{5YA-tls|YkKd2w|4V>cH=wpkA81VGY<`_Er~of{f6o?_gFN~# zFT2hwZI*IBdP9Pv&F?px&mK(Q_2cgQI&)C>5_C_6SzYGZUFG*G)%h%+Oz=4PLfGF% z5HzA>`~8kFx7+3NxT=>+f4u+ycmHumc3BZn?>gz*o0}iMuCKS{zH{mMQY)6UrIW(Z zI+!PBI&ei?I6qxdn$fBSke$e(&D@d-so{`gIdoQ(q?C+M+0%S@vM-^*HJ3 z_j}7fzu9E+S!Zo<*}tF9k1uSOn>E$jG14l?VOrC6kr_+Bp6;`Hwc<_HFZHu|-mkAM zjWyg|_ExI?Yk2)s)>Yf@*HstK44M;hc#X)bS7@^!mLe5@zg~a5WU}9_S8q0&bZg3H zngyxKEzhm$&pQ_g@14MwxDm-kx_hG4hu|Yq;i*s;^hWqix$}mKo2rudg!` z-mq<(+3s*qJN^IP@8_AerO&He_ChjAGsSx|Z*Z;e@g0Saz4lF;E1BlI_~BI40#V>a zKqDhF+mTaKwYNtd-&6T{lY#Ek)khu+C$qW!x;^o6=ga)#d-umiTnV?YeIsXG%DZ`O z=&jx5@83+kX_|e_<@`L`%}LzidOe-O>S>$Lf<`OF;{UGy|8@T{{`wz>cf8$p`_aGO z@B6o?-DdviZ%oa4+)0b_{RMe%C2T4Rwte`2T7SRH=Cre2Upi7RKi<}A%=7y7>&>QbFWGYUZ}CpQ zmAZL~``c%8x%bVJy0AX}_49D=n+E8a-iBkzae>Q*H9tPQ**fdL=j)iIre`koEH%Gh zV;r_NYU8;p1Fx?8=J zSN&3A+s(3-BA~_-auruL&0$uiN!_0xZ}jgPpH1;{ob}kY(WLs#m$S3km+U`h6BJN( zQgymZRgclzkXiTYKTV#WbC1=yb@t0Du8RC!FPA-Awv_ev<>VQevYWODI^41~meu@V z^>j+`qbHO7&)xg}>Z-T#qJ{)9!{5t-Bl}zoIi;*A7u0^IFouul$$-VS)-<)PP z-cx1IeI7<@){C3YUVhtj*|dHML#5XZ^{BNKL*qGNP#ZWbvMOiot^WMF&$B1W{q&wfFH*U;0dr!(Z z%_pYn<-6`+Us^G^;y5$;lcgKTvmVYm!O*ezwh_o&fBem| zedl@7_Pb@-d1qGyE`GFX^|~(c_?nGBqcr_N!}XwkZeFIZ?r%su?6)TMcz)@tmCKJ6pSPX9cKx=evDel{i|_w+ zb^WHJ|JivYHatCkPxwVhW7o_?P#@E;HBb^YH>@ZD&x^fYzhCb6o6Y`_B~M#~{Z5n} z{{w0&f+q{knEk$azD_KY{p@MI-8#|DYgZnu{q?0&(YeiJ?^mw{!JyId*CM=A(Yu5~ zKc4E>&wM*&W?I?Q!xNh}-Q1j>?z{8pwCGzom;V2X|G)a^*Dc#_nqH5&{AiXri&fNu zB&N-R%5EiFT7RQ80sX{5-C3(8=YD>E?mlblRHo%yPSjb-q|Yr4+qR;<`c_g|Wc8LQ zcOya53~$%)?M5FYD{FjudiwFR=J#d({`%UjU-z*)_43^<&dZt|R5>EP>=$zLltvr0 znaH#{e0|^R_51BE`ArUwuPt3NS5uaSV~hU*p3UDXHPQNvr@|C;Kh;0(H9rO#*j^p~ zH*3o4yvd-+)}!6x?zbe1PlLKew>67TzrH7oW16{S!2!^@TseV%jnCT{$JKtl`eyyg zT@MepAHT5BdG@_&PfS^>?0+2OPdYV4^Wu#gJFc4DDZL)6TXx}2mZQAD3kAl;NcMxf zbWtPsMNVVo=Vz&*cE4UMUc1Wf`JCcM!t;N)tbRSW@}au@PfwqDHkP0o%;L?4!%3hu zIkRTX67%&iKC`R*{XKB3L^~v0yZPvqGHN=MRrvtAOSbvlp5naU$L;?aR^RyqT9cy} ztFwAvamd}J$}E4Ztevjfq6RBNV;EDWS!7hWZ^X5(W35KQ0yC!GY{=k4b@Ym`#$bP2 z)4Kn^?;k(Z%DsAP_peY@Cb^SqCN*B+Max+4n3m74tJhR;%@An9Azf*KN?Dpj6u}0E}wOajcM~jUX z-)j8ry8U9h%l5@~Vy2~Eqf1}E;J;V*`>jX)@Av!b-NQpa{pLR7Wyd0g8Z<1Q6bnE- zr_=iT@9gnT>zA{QdOK<6HS^Q2^=8fv)9IV~{jaZy&YY`{*kKE~N`vd=K+9)#eUZEI z=<~U%$xUZg1}{H0xBQ;u?svPg(?g>tRk2|03_ILX{d|C#zXvq4D)#Sw(vQn$HckB= zC(7HMmjCf&{Jmugx~y4y_4jXAF+O{&UtYdlt}5edd#dN1MgQx6-nB%FR2Hj%1^M^) z85TTnNQ{&|_R{c7ZfmM~`D51RS=w(!Nu@fn}_WN_*iT2*<<;lYy0eqEqx zPf!EnMsmOHsRgc2FSl$-i`?k0b^GrOe|Z1$IH;_7KBw4k$?>%Rf1cZ?AAF{{e2x(4 zZnw|hyANHf2k#Qwi;^Ea2i3{r@S|9?*^TRgs|ut&zyXtwW; z%*)H3RWF$g>f9ddlfC^YYUf_T8Q8|jgBlW205p>qWmm*30gm_vz@f_sd)VTzH5wsa(9#aZKt*7f|?FIl13`8UcFMPHD}F! z83FB6Uc7MOLQQ{>t>BEQuTNy4wNq9YHNvMNUWb~#o3!)W&-rB)5#RRJ3(Y>f=gPT5 z2XyWlzm56Ts$ZiBn(128B{J*p-TQxI|2z`kf5K_~wpYH!r|rHhp|4)u*jZyp7M?s+hAm8r-3iTCgs5cN+Jf|G%#9@B8ABDm7gf+;ae3 z7GATn{QbR~mC>n}-)sgI;#G4tZ`-!*r}fh+C9L!Rso=Shsb5#5mY3OnUFA`4U9==J zK3i`0$`$M1S^Ybm+ZN|qyHmC`h~e6e8xq;q)|^c1&HZ^5G~4v~yuEz&+pXbmHlMcx zjX@j%&4;wh6nS(ng7)s*X60^szwh_9A8TUnbDTLg`OKv8cnwqX%RsDMH_N04zDnCBeueSsZo`%ggkM)?%1e$K$_i5^Sm#R0LgZLUF zm-b94Lz@QR68A7Zb?MOim!`S`^UDtVSZnGa|mz9eGVKF>;lPZPiS(RF8#E|NVY14_erF2b#jMc)R8DrjNd_Xa9YoZhtauUhbV8hH*6? zkG_e^bZ$sI+_tsye$h@11MTS(FQCor1UzNRlwCblJN%evc#Pn2`TCmfnckaGR&uT` z>RMZ-&zh7O_-~uov-_{6mmOly{B~V+dW_Kbd)4`7pY`|tf1+-GGs*q*mWcYxO@A}l zZmrw>Zr0k^vPYfj$J%(MH}T5OtA4lh2WZqNQ~b)7Z;@PQpXJW$EW7MmA30z5tlFPT z)Awx=j=Ed8U0_D(oVC`d4H`x!Rwi%{G~myTjmg~W_LWz`rn`jId^${|HP6}1nC`$MSaGetJ|qeokw~%Fy$6zjOY)^sny%HBoi1>3~)%lB?>)pl{~<=d`RH@%zldlxrq^{U{opw)pZiH%?G&ZDS@v%grxJ3Bis`MObNso;xS zTeH7;yqT65ndQ4HEdKw!UzxA3tvxOEJk6}cqY-o! z>CqKm=K8ayJ^lOpvK3n0ZVFn6dQNU<&5wue$$x%))Ro&k$F8;tbX?|5r%zM2%uX`p zId@Whz6>aH@7^`(n*Q%w*Y}BWi|L#&%k|iu^X>h8`{)Put!Y#1IL>I*vTQ}0i3;gB zFf)CgWbiVdlTMr6iiS??_uQ8fe^=jn|(4wpZ^;I?cxoiaoS;a&C+zL5E0$uiINPgT-cq9#*cg)_XMjsYb%|Get6@xoGp@ zp-LZ)N#`G_{eD+`{r-PdtL2u)iqAf@>G`~BJ_v$2?0@u>4h|NkHTxzj!?;#hX+z{h%tuPbxzrY$$>Z-o{{ z9|tX#YL_jGu=)Gt@@=hW#!PdS6bxRAq)kQZ4E*8z^Su6F`n8wMd{!$~oRcp&zzC{P zB6b#~20j&4x)8|3w7G9mX*_D6Oek*L;udN3>Q&e6;tebR>?(a-q__O{QZa!UQ>$36 z_@njKLlS;`co=y;?cAKm5dO=LXW!jb+FkeemuP65B9pADg28Kz$nPl2Js6oR#U+eV zJV1w?egu`hi(I?6tpxRXXV_F0z1jVKUtn)W<662?P zO$^mUhXqM2ZoN{X)8p$Z|2$E*2aj<7_U?Uuak0DD64O$Zw~H!413;IP=lEXVE2Pod zZ~)s#tdPaaC6klR%rN};<8lA-IhMsq4-d88%$osQ1oiEH{eKDjx*E^~pS#wssB=tA zn~UURbCprsvY|>7(k9e?SuDSH(!CdMTe%|+ues$O@gKFf4;qEIrK(_%Ek6AaTGN4n zk?E?Qg2C!b_&)8;Js#J^~MVBrwRnBH(;O>7JodLhih5wg4Cz^Io-V+oo#aU{GV^$nSq zmql#N3f=wd?u{D~(f_u-X5$iGq13ob>s9wUw4&|~H>i2_ZqMg)5jzSJ|GeJ+FZ<25 z+j+N5E1%6wf3)p(UUq2Smlqcc&zfG}vg;jFV-{#p&2Ns^sOy!VI3>KjwYBhm?f0X> z{C^oPw9n<^J!z&&L4g@hQh8kF$F{z<<`P~J#@HCSwQo%w>T0`nETDNY z&?m*BnX9j_?>CR< za@B7H-+!O?ezoI1p9L+T!I`NSJg%aa5ertaUiy^^nxF(tYDh^*ZQ92tVW0qB2b*`qIQWe%mh>K$GX2BGdKv z|0#N{zr$(4DOQ%WP`d-W&{olvxqy1hH?Ln?7rR?I{<|!bsIG#+>K6|$SD=Q?g}TOG zY4g1A-o|NXPMqSAx2xHmDyk5W%OtwFEpqF@*Jy)PH)etdC_sZ{+hew51b&<4v|i4( z>d1qG&B+fAG~V2LT)y7s-R}4MejKm=a~!n5WLw%zU-P>npjH--w3$xym;Xzr$8~{r zyBk)0c_Cq0^yFO5vTDH<9E^>Tt~`_7giwan8*{nO%re!EUa)_|mGAq$?|t+3$H&Ky zA9d@WJ7=DIYs%hbrKc|ihKEmY%>r!+Ns7remXd{cQRW64Hwl4e3a-s7e0;1lYWF4OnVK5fHkY<8^`7olTU-0( z?^FF3A42?{TjggFPXbZf-7BLy`M@@%d zW0+W(m^L%X@+zbFr|bu}b)%wxdDnIXKSzVFNoG%!8LPA{-oNxD4i? z#v#}#0yBQ{!j&Ny8Ug|{L=!B~8a^PWC>VT}hBzC<1A7a^2vATk&^}Orw&E1z6o-Uo z=5S>Q#sUY2gs{}fc#=5g;;L@@+5)G9RLSdZk#rTVp+di(!;0?l16zqZzh>8e6NIPcsG?5I_4-I#nlt#!H2%%D5drzklrmk(&onk1>L2xBBC163)H3RL16IY?uR}QWm zYa%zRrJbKQcWddzMXonr_edHqa_^TjtoZN%w0g|-i?cE(j=_iVQFK_4%h(usTW=j|jn~*E3|dYh@cn*${n4$_&74zaH<)5u z{sKBQVhIz|=1HGcqphC=$=qRKNqcIBF-^`Ppb!wm*cj>gGZblWF*pnaKuhtNHWx_? zzC~Mc3JSZGp!59yXsM&d7D&b%w56)I;~Pq805)+-h(khHP~B9_-szOt4F}fTadWAL z#VsNsc!BoKVXQR=nRm+9At5YJd66wbF-*IvsKAV=Q{tau$~;w5FwovEcoA)Du0sPu z<0LMQGhV0G7op{x28PBW(DWVVmIMWd1_w`8mNeC;;g+bm2PCr-v1I10TL+S}PO(K_u4AO5j`whYAXD(ReOTP;?* z_}H;yVX4_!Sy8dYjSg}wENPVzskNw`;uwxS9}aPYjwXoMk`Z|AzM^y6k>~dRf8MD7 z|9AV%|L~2%sfdljdIBrTK%+JN8~&kG&klMl%Y0{xfi~0Ed}x+G$*W_n6U9kYzS?isvQpMaMwIDo}U?*c42{|?!9Vf1$`!_&3k&b)S*rqzr#H8Q?kNkq?{__HZPe9QRS#RY)ZN@w`ufqd=)BG?-y*Fp%}NK2n>^ld zm`_{h)f@*s(Ap-@+?pPni#kf9MuVw}ue$y+>ixaF(Xsk_CdeK3NsP3*^m<{toRoNMNub&5H&3U>>p>jK zlD1PKZ7yoaXNldY{eE}zJKeZFJ3`IH^U%vj&#~n{5c_;n)^77`^EonEm@BjO@;@Qk}(De1q^7?O^=N|>F1Oe^Bo2cw= zR)3wdaSunx8Blr?KuvFqOx8*Tzg{kPk6QmmeE$zu(9~35c(`~b^Iq-sdyKfs&*$8d zP*@v1tuJI9%2wXj*VabQ!Mc}s(^``^PBSM9UPm4F)X)a+X0yP`owGgD2WG@lp7bZu%x*f zYNGZ}K(-wXh&1a>hrG=oiW6}A0 zr!q6fs%$7`VM((-elZr5-NDrvnBj1e6|_qpw3#TT@Te$w+u%WVd71FI%G5QnyUjpt z&q>?;nK?qb8V;-pa`UoAtrriNv0R#!YMgea19ZaR?9gi=C8!Nfi^i_V!#S6hcpm-g zp^4&hg$d4$OTDM3?Ul1C$yj}lU&`c!^31g%F$x<>8I8|u%X=)0s{Dc`)8k{k)%U;Z z#P74&eOzz%n@g|aUe1y=j^4P#Az?L>sPW;P^gsQ_!$fP)hJ>_Y7Hr&T_!@LLP1M&V z-qZCa{*z(h5K(`yf7+utQ_u$NQgttEPCvhmu<3IvX#Y4L%~2>aUXo>@Bj9_zZ<44+V$AtpSuI2 zrdCJmDvhiIO-fqd1iF+uI%HivJd_M4YIt4{Zu0Wkc(hG0VB--5N0S>4O@SL4SXl%G z1Q=O$IuiPj*!eoFXI>WQ|-9-SRkLa z4LU{nlfs3q+2NNfQiGTIY6+5l*S>Yf|2ecM?fjX;dYMd(TABg5A0Hjf zlj7amr~$iA1A5p!heCiU%eA%9>AX@V1&*6nee9|bD&Gevb*t$=px}1cb71 z%m|%oeDXEOBj~zMbTcq+-eYiyg9)k}Y%_##2o+RF2n#)@vYr_ll3#L?UNJDJlzO^2 zh9H}ypwGl|=F=e$WlTdDm_l6}8X`k2C-31y4^_~ez!01u-@g@f(&DP5`St&H?)`qR`rFm;_|!GWC*S0JVY4=N_cgnJ zKOXl;FPrcGch1V3o12c#UBBb=td+d;v^786OrJk9$6MR;{PJzjZ9``zJ-n8GW5Yqa z9}k#+KbxIz^zO;)_4~K!@Bd?zEIaSa{)Tt1ziM~n-L)#`*Usg?x#zrBcJtR%@s8iM zFTNl6d%`=U$9?a4yI(JqqxaX<9<@;DQ(DaMeQ}S?(|;=87Z=&HM#?%|`W+V58zjld za!Ms&u}}2#4Ig_Xjob3(b_C-cQ5Z@of!rQoo-?}tOfV^e>>+ns;v)%N>!x8LpmUl+Y8 z#q&es=N*sxHiPzXYkvKqXx*s4@5iBkU)R^)4EDE8ooiLPiIJIYiOGbsKd)FyS(mNR zjozk{vAFj4w{Mf@|2gvN`g-{_$%^(&zc&1PvAF-%6wTnA&(F@TyLfAB_U#W353jBK z{7kj%(>d$+H#Q_5j!F3Ps9S$qpRDz?6^Fb2aJ~3zT=>Xkwps41+&eoiny&kOnpHd| z0d!!)T4iYe=pxN<8Ik) z-KXh)AN@FP|1a^p&F3D|tScvM#iwrla>1E@=W{k*sg%;!*Gj*}*ZoXg9k{sd<^Ss*69zMU=t#?!8=CqrjYgpdi+PeAe_WSqlRlU{? zzM!+}PWAh}+fM84-r=*w=VnXb?M-^-jI|n9rrTP%uZ`YbHdCKV>-FjRVQZsqzT5r& zos{1%yVGwqZfJEnOuFBm)&4tB=i=PVBM*?tMQKE;S3zUHIrbWw?`X0MCH0}O(; z{jyzF{NnVy>US^2gXR8K{+a%oGUCwVb z>(RD{KmTxOR0sb*bTr+j-I#ImzVnCPZkQ9_sIs4br~K3FHt}m;{#(3m#;@o1!?~uc z{IQ^9-#1t5RN)78A!hrNa?bu;A#v*GP4`K^!|J}dXmcn`=yur2^J{(G*W%~rN@se3 zmbA(|u=;!0^OE@T**k89{IXNs&veQzsB=^7=Ct0^ELU!%)ScP<;ojehh~IBE_gmY) zYFZ}pWW}zBZzKA|FUY&@oU-Ec&hPiCcdy$jwchjb!k2&Ulw9^*w&3Q@=ksQt&by)3 z+;C6aDEzwDUirAHm#Wu8efBF~KCRqulO(lxWBB^Gm;N7%C$fqZT-;IoOX%T)X8vwJl!i zTwKHNz;9`yiQMnpMIY)+v1wEJzIIadMm9#4F4YOQ)!!P)%sr-md3*b-dymqaF1PdX z2FZOtsC+Wd)MIbjsW*3a7EgM0#ZzNJWc0kcUzx$-8@J`&UY4`eaQc@y$7Ww6xwo31OXdIle*ga1x}Q(; znwH=H^UVBR{*lRkRwsFX&G)X(36=n{Mmz+Vcf_HN4OXoJG z=vT{@GoGG$wCL^D>$~#b6d%2I;LNXU-p%KY7YD8Dx_J9#^P)|?Cd$j?qmNt%?TL<^ z?!bCVgrVZk$K$bOlk9G2ODtPdymrw^#$BbapEVg|`8Qs!ez)^DZ`r=fe%3p!{)AUF z`aCbbx+?VBv)TEx)E)(x`}i(e_5%4ThSwe$JB zV&+q?r5}7*^m&ECtaEGs{Qmm-`KE~?86S_%FJ?XS=SIfY*VoN!tJm;I&sTf*`g4Ru zqtEl;gGTm~S2i&=v-58QEuz!Y4oKGd!9T0jdROl1)V=ysb}oPCR`vhI{9W>ybNBfK z1SYucm;U|b<>O68=DD|0el3e|-EaPPQ}n@lR^bcp%}U#CU$5DGhi8*>JD+K-=i-}2 zi@lb8j|{23ChFhtJMiP0%{M2jT?}aO5oUhLJqvVPW$npj`d2~Srkq)e@)|*b#Ifzb z*&=%_1B0{54GvSqG^*Mnrz~T88Gi9c^3Ew|XPe)i|Nl?A`MFCQ5)U^u_~_+s^j+2P z&qLO}ZqMvzwzKmqO8uthKI`6O93pZf=hl{$@v=N`UhjU+=D#68pmE1%%gRqFmlK}Z z|9$D-+{q%Ma&^tj^82;hxgTkq@IBeza8LXpr&!HusSmf<)x~wAo}4?`A*lT4^1;P+ z9F+u<5p+6f>T=I}>6+3XoG-HOkQbe6Q`Rme9pJkE{~=f5b1&x0^PB#WIek?8L0w4A z=S^=W{tk@%azl)fMc~JNhd}1EQ!@h0^?5ngGjwk1imU(oHQ~-iWQ z7LTh?%y2NCu_}A%DPez`L}{~}4bFU)N2E+L5`HPHzmos=;_5Xj@g|WC=hmmiUD=d& z`pVBM-(EE>GxXBf$*B{+?@n#O_vqKNt60^(GR6y=te72t>GR6ZD`uX2x9hdu>nkfK zSFe95ojO;2?&1}aL4WHi&%`+#GfqFZ1=OkA8-K;N@=y!sy4gkN=h?}rRu5Hb}p77lBe)>T+P~-CCd*^rVmFfD$ z%~tBacIW;2^77rj#nsdArT?mYe6082eVNpIg1l2a8-54Q{L!_`@cZILMbk6WJ>NyA z-eQziZg7~%vZ{9a!_A?(KNy#vz7loj*sp2-rbXu!CVzU-ys&GZ>6Lc3r4O&K3|7x@ zWZth;tg~uP&8L&sw&%xxpK@&H2jjS<*KNOE3BFrl{POir%Qy$kJ5v5Ct26`_H*}qw zdv5ZsR?UEC-Aq%T&nfPco_}ur<;9yAOue5qv2x$Z;w`KD^YQrZT`4D8mTrm;OMAL0 z^|V*c5{H@R7B-#nJilD3^3BHMcaHG>n((NbDf9XK`nt{>ssA?pJGp&ko1K-b`EW2n zXH(kQSvw<}P1i+crha{Oby?&SlWaBNIa8KR+5eSm+RN{ECaHSIe2>_=ChxgaxZWLK zo!Q?b|Mm4vI|Qm(SJ{1EydmmnMBL`I-q%TTa_7U6=e|14{P(W=${9>|`n8@_KCBBd zJnwxaP<3yts#KVVL~Em{};h7sB;M(znSG@ep+II!l^9PK_4 zEYb@&m{`tqHJs5y(F0Ye)z84V`Bd>biy$l(H!5)m7-%yY?Z(!NUtz<8Wj&(aK=MCqyP(Ugo>4 z=4a7&#k%RSWgGu~zkfgL`ntK5%JXW7=p+iiC37Q1^7-oj5$PKIqx^R<4n;c(OSMGd!Cuiy8o?%nnE^HZz# zZcaP9DLQZG(W|zPI+Wi`^48Mt?&A507G`=(2u6Ebj=t=b; z4MAnojs7h2nVA&3tHh9%d-18{e%rL_Hyhi*<5FL*$1e}L_}lU6`ue}CZ`VCO*1L@1 zcKyH4^3s+?OL*n&Y?ki(|8VK_xT2d$$K|Tm#Ebr}xDx1o^UO@++;4AgF8eNrNPY_D zj7(`ecWwmVYUjY#s6M~u66@(*rLU)5GWB6(E!BPKw!EvQ+B6S1il-+xhmdZ*O0Jd%09+ z^*>Oj;b!H_V0-*3NtN z|7){vZdy9E?Cr|4Up^g^&fgKrmE+4j?|OEbInC($jx76M7yE;Dg&m94e-*as`}A|hNO4}?_}*Uh$vw7cC2*l5z>!f< zT$&oJ7CG0dwCU?o-4(ZQZb&?QhrNbZZGFO&U3pnH8mvbTxz5a7wf@~au0BnPWs;1b zy{6TVI@Rw~@ou=cueMseX7|5euVW3g_x6g%*IWeMaJhXglc$tQtfWi%y~^|*t4_CE zmI!<0wcKGAQ{}6D%NOaDhR0Pd&8(kmU0!xRQ|&P5+>*7yvZdg2T<>n2S9D5qo%E`{ z>+Bhi=7oc%1PUERb$?GCzj@W~HfCO4_ESw+-qf*~$w2D9_rVoD$xScs zsFeC9Eob(M@|Wy=Wwq+vBCZ;(8%y%~xb##ti!Q#_$rY&My|Dh{u@c=AdNCp{q2ed{ zWUbY9-TS*O%l&5Ru4PhYIWNN5`?(la$rPV4Jh)5cdbifsDrt zUg}KHp>z9IoldfTx8w1fbo<|t7v}m&I)1-fK0ouB3v1e+2d}tV{=W3D-{d)2E%N`r zzi+=@k2gPSQ2BD{^j$MmuV#FIclYh>{QaeGkK4-2xMV1X8}~{s*K5||+QTDfbE9|R zQl4d(x|sB(rqo?o5olKRC8PS)%H?ma1pAlXtO-o6R4w$)n(k|xT5p_DNo* z=^#E&=D_vKkBiUSrtf~gulj;$*y~Ag`gLrT#96x)c}@GStO`Aynyk5DiPghJ@5<~VmVK4l?0Ha{b^W4i z+R?guSMc_pe%fsJ%5P<)hN#U<`}#VsQr2G3>AqL4PL?|Db<_Hk&gX+hYs078@k*^f zFwyJouF|`UjSkgJ@X@Z>etyUEyT>yBKh1j9oSNkq%Xsx{2;)pSNz-u86koU5}S;f7>7ZczR~c>ViOlqx;KBB}2pQdJC7n zQAkzP-0*n!s$;hDdDr!S*G@m`ldJh-agM3%dj1DLYnRw(#e+swW50)`Uei=qvBr+| z(jmsfD(n9Jc>HG4wa<;aHTX+6_h|q7_2J-(ORrv+X5BF_{r{!;zqYr}@Ar3imuJpi zx@P~&WwVG&CZ17I^cE#m{`IgVBr1m z0XwJ`Xxzm0W8Dn92NPG?_LeRa61~B}o@BID@cW$eO5LlUf1kc&AOEI|g3{92+~TQ9 zmCUS*>b~#3zjNc>D{}+yx|v?Pbzz~iFXOx&&kw)-^|WTDYUSLz`TqhXQ>1?T-c2># z*u?hkx<&d-uU*+eyO(^O61(it_sK6K7R?I|2z6y#vtv=ha;CJp5C2yNFW>bbJ#%w* zT9&_~Zt3URr&GgylJEa)n5h#B=7F zQySgcQu^MmlSNwCQ~PiEIr3e-XwT|CC3VS3o+%rb3VpAsn*B6)*?}pWo{1QIZk+ig zndkqUceAeOW!l!)zLYw(^heHqm75WuI@&lO>qEWou?1@!OV!Vr8Z15ITB;nnJs|$d zvZnI%IfZTUQ$NjkQuQ%%(nGyfn!O*G5t(rfL-LHHLV@7!ih?v_t;h8P-=sKd&&)7X z-Q|+?cX7YntF%iwcXw^gj$&Tke*DL!89}EW1r|6YPkFjq!#L~Po12cCZ*6Cb48Jzj zsQQuD>aevjv8ha{T2-~XL8u24_5 z-`{U9vw!uYL^p%SB)&veOJn8D=*!-hULR%ue`s>*lI=&&gR>Esn8|v=;Pjiro537Q zj*M-mDnCElIg{b)-+*L&&18>Ahs|kcizH3BY*s`s+O_%Y9LvjjJ07x4Z(YjJcC6si zw^R2%CU-wAkdQPFTo%6EZ*EN5>^@IPXRB!{lDSsPg!j!>c`!45-p1Ma`)H6lq6PF!)U;9C$*YK%3 z>%S=%S3H<{KEivYz%R!}o!-aGbPP(}L$5_%TEzHIbV_J~-^}t?T`S+60F44StfwSQ~8~%GtH;`u;DA z6r!(Gt-W0Q(lyePtFKqu{G18jRHG^phFACZ*Lz*P^liDN^6a-I=Bv zedh_&!?%BnCcSnw&eMzCb){6+cmA3~+Yc+lLaf1IkHgmYyFz#TQ`*<<{zvT!q}#1v z&Bz-%>9mcL8oXYcx~Y3;#iVy_mqqtEE|m9Q&4vsX=j`Ey6|iK{-02BrPJLLnC! z{+x=slpSmk{9Ivg%44Z}X6Mdb_YLwtzVhRND{n(pUuK7--CW?<9HaJRf}-;uz1ZvC zTLn!uThk^AXgBR1pq1FDzO`j&R)R7v^D#>+23yMLgshwta?|{w!QvasPSOp zO7Garx4+*0UCfj^bJwSzjlXLDemU-Q{6gKnuNFQxDo(GE*MW4b8yJ{w?{YX z-B@U}J4?oxcVGPWyt{W8PVES7=acpMe=6w8*T~2J{(fk;*YP&a0T=wBuu}LimmzsZ zp(MD9Lnl7WZID0Qqc<~#4_qXn6D=$pGg=KIT)x8UUl1Jh`1A<~9}$AY%_xJ@aY@lUWOsm9!}gxzbSx9{6A From bff1c92c8781bfabba3f185ccabe0350684bfbb2 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 17:53:23 -0700 Subject: [PATCH 13/32] Minor tweaks to pzstd graph --- contrib/pzstd/images/Cspeed.png | Bin 66235 -> 69804 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/contrib/pzstd/images/Cspeed.png b/contrib/pzstd/images/Cspeed.png index fcafe23c4b47c5a2607061ac4fdcb688cbc68985..aca4f663ea2e98b6df7c1344b51af99757549d8a 100644 GIT binary patch literal 69804 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⪙>MZm#<@yH82q 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�o;{@SKe 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 Date: Thu, 1 Sep 2016 18:11:12 -0700 Subject: [PATCH 14/32] zbufftest only depends on standard C time.h --- contrib/pzstd/.gitignore | 2 ++ tests/zbufftest.c | 52 ++++++++++++++++------------------------ 2 files changed, 23 insertions(+), 31 deletions(-) create mode 100644 contrib/pzstd/.gitignore diff --git a/contrib/pzstd/.gitignore b/contrib/pzstd/.gitignore new file mode 100644 index 000000000..84e68fb07 --- /dev/null +++ b/contrib/pzstd/.gitignore @@ -0,0 +1,2 @@ +# compilation result +pzstd diff --git a/tests/zbufftest.c b/tests/zbufftest.c index 528979930..9dc164eaf 100644 --- a/tests/zbufftest.c +++ b/tests/zbufftest.c @@ -23,7 +23,7 @@ **************************************/ #include /* free */ #include /* fgets, sscanf */ -#include /* timeb */ +#include /* clock_t, clock() */ #include /* strcmp */ #include "mem.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel */ @@ -58,13 +58,13 @@ static const U32 prime2 = 2246822519U; static U32 g_displayLevel = 2; #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ - if ((FUZ_GetMilliSpan(g_displayTime) > g_refreshRate) || (g_displayLevel>=4)) \ - { g_displayTime = FUZ_GetMilliStart(); DISPLAY(__VA_ARGS__); \ + if ((FUZ_GetClockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ + { g_displayClock = clock(); DISPLAY(__VA_ARGS__); \ if (g_displayLevel>=4) fflush(stdout); } } -static const U32 g_refreshRate = 150; -static U32 g_displayTime = 0; +static const clock_t g_refreshRate = CLOCKS_PER_SEC * 15 / 100; +static clock_t g_displayClock = 0; -static U32 g_testTime = 0; +static clock_t g_clockTime = 0; /*-******************************************************* @@ -72,23 +72,9 @@ static U32 g_testTime = 0; *********************************************************/ #define MAX(a,b) ((a)>(b)?(a):(b)) -static U32 FUZ_GetMilliStart(void) +static clock_t FUZ_GetClockSpan(clock_t clockStart) { - struct timeb tb; - U32 nCount; - ftime( &tb ); - nCount = (U32) (((tb.time & 0xFFFFF) * 1000) + tb.millitm); - return nCount; -} - - -static U32 FUZ_GetMilliSpan(U32 nTimeStart) -{ - U32 const nCurrent = FUZ_GetMilliStart(); - U32 nSpan = nCurrent - nTimeStart; - if (nTimeStart > nCurrent) - nSpan += 0x100000 * 1000; - return nSpan; + return clock() - clockStart; /* works even when overflow. Max span ~ 30 mn */ } /*! FUZ_rand() : @@ -291,7 +277,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres U32 coreSeed = seed; ZBUFF_CCtx* zc; ZBUFF_DCtx* zd; - U32 startTime = FUZ_GetMilliStart(); + clock_t startClock = clock(); /* allocations */ zc = ZBUFF_createCCtx(); @@ -321,7 +307,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres FUZ_rand(&coreSeed); /* test loop */ - for ( ; (testNb <= nbTests) || (FUZ_GetMilliSpan(startTime) < g_testTime) ; testNb++ ) { + for ( ; (testNb <= nbTests) || (FUZ_GetClockSpan(startClock) < g_clockTime) ; testNb++ ) { U32 lseed; const BYTE* srcBuffer; const BYTE* dict; @@ -543,7 +529,7 @@ int main(int argc, const char** argv) case 'i': argument++; - nbTests=0; g_testTime=0; + nbTests=0; g_clockTime=0; while ((*argument>='0') && (*argument<='9')) { nbTests *= 10; nbTests += *argument - '0'; @@ -553,15 +539,15 @@ int main(int argc, const char** argv) case 'T': argument++; - nbTests=0; g_testTime=0; + nbTests=0; g_clockTime=0; while ((*argument>='0') && (*argument<='9')) { - g_testTime *= 10; - g_testTime += *argument - '0'; + g_clockTime *= 10; + g_clockTime += *argument - '0'; argument++; } - if (*argument=='m') g_testTime *=60, argument++; + if (*argument=='m') g_clockTime *=60, argument++; if (*argument=='n') argument++; - g_testTime *= 1000; + g_clockTime *= CLOCKS_PER_SEC; break; case 's': @@ -605,7 +591,11 @@ int main(int argc, const char** argv) /* Get Seed */ DISPLAY("Starting zstd_buffered tester (%i-bits, %s)\n", (int)(sizeof(size_t)*8), ZSTD_VERSION_STRING); - if (!seedset) seed = FUZ_GetMilliStart() % 10000; + if (!seedset) { + time_t const t = time(NULL); + U32 const h = XXH32(&t, sizeof(t), 1); + seed = h % 10000; + } DISPLAY("Seed = %u\n", seed); if (proba!=FUZ_COMPRESSIBILITY_DEFAULT) DISPLAY("Compressibility : %i%%\n", proba); From 7df55e17e976105436af7233ce72064d190cbd27 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 18:26:19 -0700 Subject: [PATCH 15/32] Fix up Makefiles, and fix include issues compiling with gcc --- contrib/pzstd/Makefile | 10 ++++------ contrib/pzstd/Options.cpp | 1 + contrib/pzstd/test/Makefile | 8 ++++---- contrib/pzstd/utils/Range.h | 1 + contrib/pzstd/utils/test/Makefile | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile index ba86f5d4d..2ef10b6ef 100644 --- a/contrib/pzstd/Makefile +++ b/contrib/pzstd/Makefile @@ -11,12 +11,10 @@ ZSTDDIR = ../../lib PROGDIR = ../../programs CPPFLAGS = -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/dictBuilder -I$(PROGDIR) -I. -CFLAGS ?= -O3 -CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wstrict-aliasing=1 \ - -Wswitch-enum -Wdeclaration-after-statement -Wstrict-prototypes -Wundef \ - -std=c++11 -CFLAGS += $(MOREFLAGS) -FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) +CXXFLAGS ?= -O3 +CXXFLAGS += -std=c++11 +CXXFLAGS += $(MOREFLAGS) +FLAGS = $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) ZSTDCOMMON_FILES := $(ZSTDDIR)/common/*.c diff --git a/contrib/pzstd/Options.cpp b/contrib/pzstd/Options.cpp index dc6aeef14..693e9a515 100644 --- a/contrib/pzstd/Options.cpp +++ b/contrib/pzstd/Options.cpp @@ -9,6 +9,7 @@ #include "Options.h" #include +#include namespace pzstd { diff --git a/contrib/pzstd/test/Makefile b/contrib/pzstd/test/Makefile index 147d9bd79..5f0e144e9 100644 --- a/contrib/pzstd/test/Makefile +++ b/contrib/pzstd/test/Makefile @@ -24,10 +24,10 @@ GTEST_LIB ?= -L $(PZSTDDIR)/googletest/build/googlemock/gtest CPPFLAGS = -I$(PZSTDDIR) $(GTEST_INC) $(GTEST_LIB) -I$(ZSTDDIR)/common -I$(PROGDIR) -CFLAGS ?= -O3 -CFLAGS += -std=c++11 -CFLAGS += $(MOREFLAGS) -FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) +CXXFLAGS ?= -O3 +CXXFLAGS += -std=c++11 +CXXFLAGS += $(MOREFLAGS) +FLAGS = $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) datagen.o: $(PROGDIR)/datagen.* $(CXX) $(FLAGS) $(PROGDIR)/datagen.c -c -o $@ diff --git a/contrib/pzstd/utils/Range.h b/contrib/pzstd/utils/Range.h index 3df15976d..111e98f58 100644 --- a/contrib/pzstd/utils/Range.h +++ b/contrib/pzstd/utils/Range.h @@ -16,6 +16,7 @@ #include "utils/Likely.h" #include +#include #include #include #include diff --git a/contrib/pzstd/utils/test/Makefile b/contrib/pzstd/utils/test/Makefile index 6f801c309..16b89804b 100644 --- a/contrib/pzstd/utils/test/Makefile +++ b/contrib/pzstd/utils/test/Makefile @@ -21,10 +21,10 @@ GTEST_INC ?= -isystem $(PZSTDDIR)/googletest/googletest/include GTEST_LIB ?= -L $(PZSTDDIR)/googletest/build/googlemock/gtest CPPFLAGS = -I$(PZSTDDIR) $(GTEST_INC) $(GTEST_LIB) -CFLAGS ?= -O3 -CFLAGS += -std=c++11 +CXXFLAGS ?= -O3 +CXXFLAGS += -std=c++11 CFLAGS += $(MOREFLAGS) -FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) +FLAGS = $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) %: %.cpp $(CXX) $(FLAGS) -lgtest -lgtest_main $^ -o $@$(EXT) From 4738e221ca4fda20e61f0ff5d7edea021888a652 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 19:46:08 -0700 Subject: [PATCH 16/32] Update travis-ci g++ for pzstds job --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index dc9d2fdee..c84edcd4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,15 @@ matrix: env: PLATFORM="Ubuntu 12.04 container" CMD="make test && make clean && make travis-install" - os: linux sudo: false + install: + - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.8 + - g++-4.8 env: PLATFORM="Ubuntu 12.04 container" CMD="make -C tests test-zstd_nolegacy && make clean && make zlibwrapper && make clean && make cmaketest && make -C contrib/pzstd googletest && make -C contrib/pzstd test && make -C contrib/pzstd clean" - os: linux sudo: false From 779c489a9c965275d707586654b8e543e3f2777e Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 20:01:07 -0700 Subject: [PATCH 17/32] Remove if statement, make language cpp --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c84edcd4b..3b77dd94a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,9 @@ matrix: env: PLATFORM="Ubuntu 12.04 container" CMD="make test && make clean && make travis-install" - os: linux sudo: false + language: cpp install: - - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi + - export CXX="g++-4.8" CC="gcc-4.8" addons: apt: sources: From 724e3d534f2561fe4d1a040b7a38ab0ee7761edf Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 20:45:55 -0700 Subject: [PATCH 18/32] Put linker -l commands at the end --- contrib/pzstd/test/Makefile | 2 +- contrib/pzstd/utils/test/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/pzstd/test/Makefile b/contrib/pzstd/test/Makefile index 5f0e144e9..b7077e2fd 100644 --- a/contrib/pzstd/test/Makefile +++ b/contrib/pzstd/test/Makefile @@ -33,7 +33,7 @@ datagen.o: $(PROGDIR)/datagen.* $(CXX) $(FLAGS) $(PROGDIR)/datagen.c -c -o $@ %: %.cpp *.h datagen.o - $(CXX) $(FLAGS) -lgtest -lgtest_main $@.cpp datagen.o $(PZSTDDIR)/libzstd.a $(PZSTDDIR)/Pzstd.o $(PZSTDDIR)/SkippableFrame.o $(PZSTDDIR)/Options.o -o $@$(EXT) + $(CXX) $(FLAGS) $@.cpp datagen.o $(PZSTDDIR)/libzstd.a $(PZSTDDIR)/Pzstd.o $(PZSTDDIR)/SkippableFrame.o $(PZSTDDIR)/Options.o -o $@$(EXT) -lgtest -lgtest_main -lpthread .PHONY: test clean diff --git a/contrib/pzstd/utils/test/Makefile b/contrib/pzstd/utils/test/Makefile index 16b89804b..23f111e55 100644 --- a/contrib/pzstd/utils/test/Makefile +++ b/contrib/pzstd/utils/test/Makefile @@ -27,7 +27,7 @@ CFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) %: %.cpp - $(CXX) $(FLAGS) -lgtest -lgtest_main $^ -o $@$(EXT) + $(CXX) $(FLAGS) $^ -o $@$(EXT) -lgtest -lgtest_main -lpthread .PHONY: test clean From 7bf8c4d7ff4980c637c4c97041fa4e077d3143e5 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 20:53:34 -0700 Subject: [PATCH 19/32] Add zstd/lib in includes --- contrib/pzstd/test/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pzstd/test/Makefile b/contrib/pzstd/test/Makefile index b7077e2fd..730938041 100644 --- a/contrib/pzstd/test/Makefile +++ b/contrib/pzstd/test/Makefile @@ -22,7 +22,7 @@ ZSTDDIR = ../../../lib GTEST_INC ?= -isystem $(PZSTDDIR)/googletest/googletest/include GTEST_LIB ?= -L $(PZSTDDIR)/googletest/build/googlemock/gtest -CPPFLAGS = -I$(PZSTDDIR) $(GTEST_INC) $(GTEST_LIB) -I$(ZSTDDIR)/common -I$(PROGDIR) +CPPFLAGS = -I$(PZSTDDIR) $(GTEST_INC) $(GTEST_LIB) -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(PROGDIR) -I. CXXFLAGS ?= -O3 CXXFLAGS += -std=c++11 From 2ebe1cf7329107949f3a5b17a9e53f32accf13c1 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 1 Sep 2016 21:12:39 -0700 Subject: [PATCH 20/32] Put libzstd last --- contrib/pzstd/Makefile | 2 +- contrib/pzstd/test/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile index 2ef10b6ef..6f9231b2a 100644 --- a/contrib/pzstd/Makefile +++ b/contrib/pzstd/Makefile @@ -54,7 +54,7 @@ Options.o: Options.h Options.cpp main.o: main.cpp *.h utils/*.h $(CXX) $(FLAGS) -c main.cpp -o $@ -pzstd: libzstd.a Pzstd.o SkippableFrame.o Options.o main.o +pzstd: Pzstd.o SkippableFrame.o Options.o main.o libzstd.a $(CXX) $(FLAGS) $^ -o $@$(EXT) googletest: diff --git a/contrib/pzstd/test/Makefile b/contrib/pzstd/test/Makefile index 730938041..5fd167d18 100644 --- a/contrib/pzstd/test/Makefile +++ b/contrib/pzstd/test/Makefile @@ -33,7 +33,7 @@ datagen.o: $(PROGDIR)/datagen.* $(CXX) $(FLAGS) $(PROGDIR)/datagen.c -c -o $@ %: %.cpp *.h datagen.o - $(CXX) $(FLAGS) $@.cpp datagen.o $(PZSTDDIR)/libzstd.a $(PZSTDDIR)/Pzstd.o $(PZSTDDIR)/SkippableFrame.o $(PZSTDDIR)/Options.o -o $@$(EXT) -lgtest -lgtest_main -lpthread + $(CXX) $(FLAGS) $@.cpp datagen.o $(PZSTDDIR)/Pzstd.o $(PZSTDDIR)/SkippableFrame.o $(PZSTDDIR)/Options.o $(PZSTDDIR)/libzstd.a -o $@$(EXT) -lgtest -lgtest_main -lpthread .PHONY: test clean From 5b8c0247169bdbd19ef2d3d02fafff3319dac54d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 2 Sep 2016 09:44:00 -0700 Subject: [PATCH 21/32] fixed zstd-pgo (#329) reported by @octoploid --- programs/.gitignore | 1 + programs/Makefile | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/programs/.gitignore b/programs/.gitignore index 5875fc792..24f96cf4b 100644 --- a/programs/.gitignore +++ b/programs/.gitignore @@ -7,6 +7,7 @@ zstd-decompress # Object files *.o *.ko +default.profraw # Executables *.exe diff --git a/programs/Makefile b/programs/Makefile index fc634b60e..280c2727d 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -97,6 +97,7 @@ zstd-pgo : clean zstd ./zstd -b7i2 $(PROFILE_WITH) ./zstd -b5 $(PROFILE_WITH) $(RM) zstd + $(RM) $(ZSTDDIR)/decompress/zstd_decompress.o $(MAKE) zstd MOREFLAGS=-fprofile-use zstd-frugal: $(ZSTDDECOMP_O) $(ZSTD_FILES) zstdcli.c fileio.c @@ -116,9 +117,10 @@ zstd-small: clean clean: $(MAKE) -C ../lib clean - @$(RM) ../lib/decompress/*.o + @$(RM) $(ZSTDDIR)/decompress/*.o $(ZSTDDIR)/decompress/zstd_decompress.gcda @$(RM) core *.o tmp* result* *.gcda dictionary *.zst \ - zstd$(EXT) zstd32$(EXT) zstd-compress$(EXT) zstd-decompress$(EXT) + zstd$(EXT) zstd32$(EXT) zstd-compress$(EXT) zstd-decompress$(EXT) \ + *.gcda default.profraw @echo Cleaning completed From 1563bfeabcaba7aacca7d23a7bce4cdefea4ae05 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 2 Sep 2016 11:44:21 -0700 Subject: [PATCH 22/32] fixing FORCE_INLINE for older compilers (#330) --- NEWS | 3 ++- lib/common/fse_decompress.c | 13 ++++++++----- lib/common/xxhash.c | 2 +- lib/compress/fse_compress.c | 13 ++++++++----- lib/compress/huf_compress.c | 16 ---------------- lib/compress/zstd_compress.c | 12 ++++++++---- lib/decompress/huf_decompress.c | 8 -------- lib/decompress/zstd_decompress.c | 12 ++++++++---- lib/legacy/zstd_v01.c | 20 ++++++++----------- lib/legacy/zstd_v02.c | 29 ++++++++-------------------- lib/legacy/zstd_v03.c | 33 +++++++++----------------------- lib/legacy/zstd_v04.c | 29 ++++++++-------------------- lib/legacy/zstd_v05.c | 28 ++++++++------------------- lib/legacy/zstd_v06.c | 27 ++++++++------------------ lib/legacy/zstd_v07.c | 29 ++++++++-------------------- 15 files changed, 92 insertions(+), 182 deletions(-) diff --git a/NEWS b/NEWS index 903350032..726a9e38d 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,8 @@ v1.0.1 -New : contrib/pzstd, parallel version of zstd, by Nick Terrell +New : contrib/pzstd, parallel version of zstd, by Nick Terrell Fixed : CLI -d output to stdout by default when input is stdin (#322) Fixed : CLI correctly detects console on Mac OS-X +Fixed : zstd-pgo, reported by octoploid (#329) v1.0.0 Change Licensing, all project is now BSD, Copyright Facebook diff --git a/lib/common/fse_decompress.c b/lib/common/fse_decompress.c index 032e65771..7492a3832 100644 --- a/lib/common/fse_decompress.c +++ b/lib/common/fse_decompress.c @@ -42,12 +42,15 @@ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif diff --git a/lib/common/xxhash.c b/lib/common/xxhash.c index f462c57f8..29e4fa628 100644 --- a/lib/common/xxhash.c +++ b/lib/common/xxhash.c @@ -115,7 +115,7 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcp # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # define FORCE_INLINE static __forceinline #else -# if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ # ifdef __GNUC__ # define FORCE_INLINE static inline __attribute__((always_inline)) # else diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c index 386b2c010..679dbdb83 100644 --- a/lib/compress/fse_compress.c +++ b/lib/compress/fse_compress.c @@ -41,12 +41,15 @@ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c index c2dd13c87..b7d3d77a2 100644 --- a/lib/compress/huf_compress.c +++ b/lib/compress/huf_compress.c @@ -35,24 +35,8 @@ /* ************************************************************** * Compiler specifics ****************************************************************/ -#if defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) -/* inline is defined */ -#elif defined(_MSC_VER) -# define inline __inline -#else -# define inline /* disable inline */ -#endif - - #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#else -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 0116136c0..723d40559 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -17,11 +17,15 @@ # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #else -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c index a5521bd36..e94fa83cc 100644 --- a/lib/decompress/huf_decompress.c +++ b/lib/decompress/huf_decompress.c @@ -43,16 +43,8 @@ # define inline /* disable inline */ #endif - #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#else -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index fb1ee35a0..762e972ce 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -67,11 +67,15 @@ # pragma warning(disable : 4324) /* disable: C4324: padded structure */ # pragma warning(disable : 4100) /* disable: C4100: unreferenced formal parameter */ #else -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif diff --git a/lib/legacy/zstd_v01.c b/lib/legacy/zstd_v01.c index 94847d5c8..1181cf6e9 100644 --- a/lib/legacy/zstd_v01.c +++ b/lib/legacy/zstd_v01.c @@ -139,11 +139,15 @@ typedef struct # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else # define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif @@ -1300,17 +1304,9 @@ typedef enum { ZSTD_LIST_ERRORS(ZSTD_GENERATE_ENUM) } ZSTD_errorCodes; /* expo #endif #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4324) /* disable: C4324: padded structure */ -#else -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif diff --git a/lib/legacy/zstd_v02.c b/lib/legacy/zstd_v02.c index af1366208..b306db9a2 100644 --- a/lib/legacy/zstd_v02.c +++ b/lib/legacy/zstd_v02.c @@ -1122,12 +1122,15 @@ typedef struct ZSTD_DCtx_s ZSTD_DCtx; # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif @@ -1595,15 +1598,7 @@ static size_t FSE_decompress(void* dst, size_t maxDstSize, const void* cSrc, siz #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif @@ -2790,17 +2785,9 @@ static size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_ #endif #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4324) /* disable: C4324: padded structure */ -#else -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif diff --git a/lib/legacy/zstd_v03.c b/lib/legacy/zstd_v03.c index 637204deb..64cfdf49a 100644 --- a/lib/legacy/zstd_v03.c +++ b/lib/legacy/zstd_v03.c @@ -1122,12 +1122,15 @@ typedef struct ZSTD_DCtx_s ZSTD_DCtx; # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif @@ -1588,25 +1591,13 @@ static size_t FSE_decompress(void* dst, size_t maxDstSize, const void* cSrc, siz #if defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) /* inline is defined */ #elif defined(_MSC_VER) +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # define inline __inline #else # define inline /* disable inline */ #endif -#ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif -#endif - - /**************************************************************** * Includes ****************************************************************/ @@ -2432,17 +2423,11 @@ static size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_ #endif #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4324) /* disable: C4324: padded structure */ #else # define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c index 1239bdb3a..b2c3ab00f 100644 --- a/lib/legacy/zstd_v04.c +++ b/lib/legacy/zstd_v04.c @@ -1380,12 +1380,15 @@ MEM_STATIC unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr) # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif @@ -2021,15 +2024,7 @@ static size_t HUF_decompress4X4_usingDTable(void* dst, size_t maxDstSize, const #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif @@ -2837,17 +2832,9 @@ static size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_ * Compiler specifics *********************************************************/ #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4324) /* disable: C4324: padded structure */ -#else -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c index 96fdf35e9..8434f613c 100644 --- a/lib/legacy/zstd_v05.c +++ b/lib/legacy/zstd_v05.c @@ -1380,12 +1380,15 @@ MEM_STATIC unsigned FSEv05_endOfDState(const FSEv05_DState_t* DStatePtr) # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif @@ -2002,14 +2005,7 @@ size_t HUFv05_decompress1X4_usingDTable(void* dst, size_t maxDstSize, const void #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#else -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif @@ -2858,17 +2854,9 @@ size_t HUFv05_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cS * Compiler specifics *********************************************************/ #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4324) /* disable: C4324: padded structure */ -#else -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c index 6bd463b6e..882361484 100644 --- a/lib/legacy/zstd_v06.c +++ b/lib/legacy/zstd_v06.c @@ -1665,12 +1665,15 @@ size_t FSEv06_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif @@ -2238,14 +2241,7 @@ MEM_STATIC size_t HUFv06_readStats(BYTE* huffWeight, size_t hwSize, U32* rankSta #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#else -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif @@ -3054,16 +3050,9 @@ const char* ZBUFFv06_getErrorName(size_t errorCode) { return ERR_getErrorName(er * Compiler specifics *********************************************************/ #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4324) /* disable: C4324: padded structure */ -#else -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c index f948069e0..b634d6243 100644 --- a/lib/legacy/zstd_v07.c +++ b/lib/legacy/zstd_v07.c @@ -1680,17 +1680,18 @@ size_t HUFv07_readStats(BYTE* huffWeight, size_t hwSize, U32* rankStats, # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4214) /* disable: C4214: non-int bitfields */ #else -# ifdef __GNUC__ -# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FORCE_INLINE static inline __attribute__((always_inline)) +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif # else -# define FORCE_INLINE static inline -# endif +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ #endif - - /* ************************************************************** * Error Management ****************************************************************/ @@ -2006,14 +2007,7 @@ size_t FSEv07_decompress(void* dst, size_t maxDstSize, const void* cSrc, size_t #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -#else -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif @@ -3176,17 +3170,10 @@ static const ZSTDv07_customMem defaultCustomMem = { ZSTDv07_defaultAllocFunction * Compiler specifics *********************************************************/ #ifdef _MSC_VER /* Visual Studio */ -# define FORCE_INLINE static __forceinline # include /* For Visual 2005 */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ # pragma warning(disable : 4324) /* disable: C4324: padded structure */ # pragma warning(disable : 4100) /* disable: C4100: unreferenced formal parameter */ -#else -# ifdef __GNUC__ -# define FORCE_INLINE static inline __attribute__((always_inline)) -# else -# define FORCE_INLINE static inline -# endif #endif From 1e2f6a1f5df5fe7b24ad8df731a80769095910b8 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 2 Sep 2016 12:23:49 -0700 Subject: [PATCH 23/32] Clean up compiler warnings + Build pzstd on travis --- .travis.yml | 2 +- contrib/pzstd/ErrorHolder.h | 5 ++--- contrib/pzstd/Makefile | 4 ++-- contrib/pzstd/SkippableFrame.cpp | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3b77dd94a..d5479a974 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ matrix: packages: - gcc-4.8 - g++-4.8 - env: PLATFORM="Ubuntu 12.04 container" CMD="make -C tests test-zstd_nolegacy && make clean && make zlibwrapper && make clean && make cmaketest && make -C contrib/pzstd googletest && make -C contrib/pzstd test && make -C contrib/pzstd clean" + env: PLATFORM="Ubuntu 12.04 container" CMD="make -C tests test-zstd_nolegacy && make clean && make zlibwrapper && make clean && make cmaketest && make clean && make -C contrib/pzstd pzstd && make -C contrib/pzstd googletest && make -C contrib/pzstd test && make -C contrib/pzstd clean" - os: linux sudo: false env: PLATFORM="Ubuntu 12.04 container" CMD="make usan" diff --git a/contrib/pzstd/ErrorHolder.h b/contrib/pzstd/ErrorHolder.h index 4a81a068c..188badcad 100644 --- a/contrib/pzstd/ErrorHolder.h +++ b/contrib/pzstd/ErrorHolder.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -47,9 +48,7 @@ class ErrorHolder { } ~ErrorHolder() { - if (hasError()) { - throw std::logic_error(message_); - } + assert(!hasError()); } }; } diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile index 6f9231b2a..5338a5a9e 100644 --- a/contrib/pzstd/Makefile +++ b/contrib/pzstd/Makefile @@ -10,7 +10,7 @@ ZSTDDIR = ../../lib PROGDIR = ../../programs -CPPFLAGS = -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/dictBuilder -I$(PROGDIR) -I. +CPPFLAGS = -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(PROGDIR) -I. CXXFLAGS ?= -O3 CXXFLAGS += -std=c++11 CXXFLAGS += $(MOREFLAGS) @@ -55,7 +55,7 @@ main.o: main.cpp *.h utils/*.h $(CXX) $(FLAGS) -c main.cpp -o $@ pzstd: Pzstd.o SkippableFrame.o Options.o main.o libzstd.a - $(CXX) $(FLAGS) $^ -o $@$(EXT) + $(CXX) $(FLAGS) $^ -o $@$(EXT) -lpthread googletest: @git clone https://github.com/google/googletest diff --git a/contrib/pzstd/SkippableFrame.cpp b/contrib/pzstd/SkippableFrame.cpp index 20ad4cc8e..5dc95e5ab 100644 --- a/contrib/pzstd/SkippableFrame.cpp +++ b/contrib/pzstd/SkippableFrame.cpp @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ #include "SkippableFrame.h" -#include "common/mem.h" +#include "mem.h" #include "utils/Range.h" #include From ac14348a283b54603f60353cd3763f9434b73616 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 2 Sep 2016 12:35:36 -0700 Subject: [PATCH 24/32] When reading from stdin, write to stdout by default --- contrib/pzstd/Options.cpp | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/contrib/pzstd/Options.cpp b/contrib/pzstd/Options.cpp index 693e9a515..616130996 100644 --- a/contrib/pzstd/Options.cpp +++ b/contrib/pzstd/Options.cpp @@ -146,23 +146,21 @@ bool Options::parse(int argc, const char** argv) { // Determine output file if not specified if (outputFile.empty()) { if (inputFile == "-") { - std::fprintf( - stderr, - "Invalid arguments: Reading from stdin, but -o not provided.\n"); - return false; - } - // Attempt to add/remove zstd extension from the input file - if (decompress) { - int stemSize = inputFile.size() - zstdExtension.size(); - if (stemSize > 0 && inputFile.substr(stemSize) == zstdExtension) { - outputFile = inputFile.substr(0, stemSize); - } else { - std::fprintf( - stderr, "Invalid argument: Unable to determine output file.\n"); - return false; - } + outputFile = "-"; } else { - outputFile = inputFile + zstdExtension; + // Attempt to add/remove zstd extension from the input file + if (decompress) { + int stemSize = inputFile.size() - zstdExtension.size(); + if (stemSize > 0 && inputFile.substr(stemSize) == zstdExtension) { + outputFile = inputFile.substr(0, stemSize); + } else { + std::fprintf( + stderr, "Invalid argument: Unable to determine output file.\n"); + return false; + } + } else { + outputFile = inputFile + zstdExtension; + } } } // Check compression level From 2fcf8a4b997ae0e6c5ca3b59685ed488bf1d11bb Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 2 Sep 2016 12:59:14 -0700 Subject: [PATCH 25/32] Update tests to reflect new default options --- contrib/pzstd/test/OptionsTest.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/contrib/pzstd/test/OptionsTest.cpp b/contrib/pzstd/test/OptionsTest.cpp index 1479d6cd2..87e79d59e 100644 --- a/contrib/pzstd/test/OptionsTest.cpp +++ b/contrib/pzstd/test/OptionsTest.cpp @@ -106,6 +106,16 @@ TEST(Options, ValidInputs) { Options expected = { 2, 23, 3, false, "silesia.tar", "silesia.tar.pzstd", false, false}; } + { + Options options; + std::array args = {{nullptr, "-n", "1"}}; + EXPECT_TRUE(options.parse(args.size(), args.data())); + } + { + Options options; + std::array args = {{nullptr, "-", "-n", "1"}}; + EXPECT_TRUE(options.parse(args.size(), args.data())); + } } TEST(Options, BadNumThreads) { @@ -153,16 +163,6 @@ TEST(Options, BadOutputFile) { std::array args = {{nullptr, "notzst", "-d", "-n", "1"}}; EXPECT_FALSE(options.parse(args.size(), args.data())); } - { - Options options; - std::array args = {{nullptr, "-n", "1"}}; - EXPECT_FALSE(options.parse(args.size(), args.data())); - } - { - Options options; - std::array args = {{nullptr, "-", "-n", "1"}}; - EXPECT_FALSE(options.parse(args.size(), args.data())); - } } TEST(Options, Extras) { From 64c1c065cc48dfc7bdcbe06f3fd374a37903e934 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 2 Sep 2016 13:53:23 -0700 Subject: [PATCH 26/32] Add optional max size to work queue --- contrib/pzstd/Makefile | 2 +- contrib/pzstd/utils/WorkQueue.h | 59 ++++++++++++++------ contrib/pzstd/utils/test/Makefile | 2 +- contrib/pzstd/utils/test/WorkQueueTest.cpp | 65 ++++++++++++++++++++++ 4 files changed, 108 insertions(+), 20 deletions(-) diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile index 5338a5a9e..c59a6d107 100644 --- a/contrib/pzstd/Makefile +++ b/contrib/pzstd/Makefile @@ -70,5 +70,5 @@ clean: $(MAKE) -C $(ZSTDDIR) clean $(MAKE) -C utils/test clean $(MAKE) -C test clean - @$(RM) -rf googletest/ libzstd.a *.o pzstd$(EXT) + @$(RM) -rf libzstd.a *.o pzstd$(EXT) @echo Cleaning completed diff --git a/contrib/pzstd/utils/WorkQueue.h b/contrib/pzstd/utils/WorkQueue.h index 3d926cc80..2fa417f41 100644 --- a/contrib/pzstd/utils/WorkQueue.h +++ b/contrib/pzstd/utils/WorkQueue.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -25,14 +26,29 @@ template class WorkQueue { // Protects all member variable access std::mutex mutex_; - std::condition_variable cv_; + std::condition_variable readerCv_; + std::condition_variable writerCv_; std::queue queue_; bool done_; + std::size_t maxSize_; + + // Must have lock to call this function + bool full() const { + if (maxSize_ == 0) { + return false; + } + return queue_.size() >= maxSize_; + } public: - /// Constructs an empty work queue. - WorkQueue() : done_(false) {} + /** + * Constructs an empty work queue with an optional max size. + * If `maxSize == 0` the queue size is unbounded. + * + * @param maxSize The maximum allowed size of the work queue. + */ + WorkQueue(std::size_t maxSize = 0) : done_(false), maxSize_(maxSize) {} /** * Push an item onto the work queue. Notify a single thread that work is @@ -44,13 +60,16 @@ class WorkQueue { */ bool push(T item) { { - std::lock_guard lock(mutex_); + std::unique_lock lock(mutex_); + while (full() && !done_) { + writerCv_.wait(lock); + } if (done_) { return false; } queue_.push(std::move(item)); } - cv_.notify_one(); + readerCv_.notify_one(); return true; } @@ -64,16 +83,19 @@ class WorkQueue { * `finish()` has been called. */ bool pop(T& item) { - std::unique_lock lock(mutex_); - while (queue_.empty() && !done_) { - cv_.wait(lock); + { + std::unique_lock lock(mutex_); + while (queue_.empty() && !done_) { + readerCv_.wait(lock); + } + if (queue_.empty()) { + assert(done_); + return false; + } + item = std::move(queue_.front()); + queue_.pop(); } - if (queue_.empty()) { - assert(done_); - return false; - } - item = std::move(queue_.front()); - queue_.pop(); + writerCv_.notify_one(); return true; } @@ -87,18 +109,19 @@ class WorkQueue { assert(!done_); done_ = true; } - cv_.notify_all(); + readerCv_.notify_all(); + writerCv_.notify_all(); } /// Blocks until `finish()` has been called (but the queue may not be empty). void waitUntilFinished() { std::unique_lock lock(mutex_); while (!done_) { - cv_.wait(lock); + readerCv_.wait(lock); // If we were woken by a push, we need to wake a thread waiting on pop(). if (!done_) { lock.unlock(); - cv_.notify_one(); + readerCv_.notify_one(); lock.lock(); } } @@ -111,7 +134,7 @@ class BufferWorkQueue { std::atomic size_; public: - BufferWorkQueue() : size_(0) {} + BufferWorkQueue(std::size_t maxSize = 0) : queue_(maxSize), size_(0) {} void push(Buffer buffer) { size_.fetch_add(buffer.size()); diff --git a/contrib/pzstd/utils/test/Makefile b/contrib/pzstd/utils/test/Makefile index 23f111e55..b9ea73e32 100644 --- a/contrib/pzstd/utils/test/Makefile +++ b/contrib/pzstd/utils/test/Makefile @@ -23,7 +23,7 @@ GTEST_LIB ?= -L $(PZSTDDIR)/googletest/build/googlemock/gtest CPPFLAGS = -I$(PZSTDDIR) $(GTEST_INC) $(GTEST_LIB) CXXFLAGS ?= -O3 CXXFLAGS += -std=c++11 -CFLAGS += $(MOREFLAGS) +CXXFLAGS += $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) %: %.cpp diff --git a/contrib/pzstd/utils/test/WorkQueueTest.cpp b/contrib/pzstd/utils/test/WorkQueueTest.cpp index 1b548d160..074891fda 100644 --- a/contrib/pzstd/utils/test/WorkQueueTest.cpp +++ b/contrib/pzstd/utils/test/WorkQueueTest.cpp @@ -145,6 +145,71 @@ TEST(WorkQueue, MPMC) { } } +TEST(WorkQueue, BoundedSizeWorks) { + WorkQueue queue(1); + int result; + queue.push(5); + queue.pop(result); + queue.push(5); + queue.pop(result); + queue.push(5); + queue.finish(); + queue.pop(result); + EXPECT_EQ(5, result); +} + +TEST(WorkQueue, BoundedSizePushAfterFinish) { + WorkQueue queue(1); + int result; + queue.push(5); + std::thread pusher([&queue] { + queue.push(6); + }); + // Dirtily try and make sure that pusher has run. + std::this_thread::sleep_for(std::chrono::seconds(1)); + queue.finish(); + EXPECT_TRUE(queue.pop(result)); + EXPECT_EQ(5, result); + EXPECT_FALSE(queue.pop(result)); + + pusher.join(); +} + +TEST(WorkQueue, BoundedSizeMPMC) { + WorkQueue queue(100); + std::vector results(10000, -1); + std::mutex mutex; + std::vector popperThreads; + for (int i = 0; i < 10; ++i) { + popperThreads.emplace_back(Popper{&queue, results.data(), &mutex}); + } + + std::vector pusherThreads; + for (int i = 0; i < 100; ++i) { + auto min = i * 100; + auto max = (i + 1) * 100; + pusherThreads.emplace_back( + [ &queue, min, max ] { + for (int i = min; i < max; ++i) { + queue.push(i); + } + }); + } + + for (auto& thread : pusherThreads) { + thread.join(); + } + queue.finish(); + + for (auto& thread : popperThreads) { + thread.join(); + } + + for (int i = 0; i < 10000; ++i) { + EXPECT_EQ(i, results[i]); + } +} + TEST(BufferWorkQueue, SizeCalculatedCorrectly) { { BufferWorkQueue queue; From d725427a3c740c6a952c461e55542e7157c4d9e2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 2 Sep 2016 15:32:39 -0700 Subject: [PATCH 27/32] g_time => local displayTime --- lib/dictBuilder/zdict.c | 14 +++++++------- lib/dictBuilder/zdict.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index adfe55cf7..916a481eb 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -74,13 +74,6 @@ static const size_t g_min_fast_dictContent = 192; #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } static unsigned g_displayLevel = 0; /* 0 : no display; 1: errors; 2: default; 4: full information */ -#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ - if (ZDICT_clockSpan(g_time) > refreshRate) \ - { g_time = clock(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stdout); } } -static const clock_t refreshRate = CLOCKS_PER_SEC * 3 / 10; -static clock_t g_time = 0; - static clock_t ZDICT_clockSpan(clock_t nPrevious) { return clock() - nPrevious; } static void ZDICT_printHex(U32 dlevel, const void* ptr, size_t length) @@ -470,6 +463,12 @@ static U32 ZDICT_dictSize(const dictItem* dictList) } +#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ + if (ZDICT_clockSpan(displayClock) > refreshRate) \ + { displayClock = clock(); DISPLAY(__VA_ARGS__); \ + if (g_displayLevel>=4) fflush(stdout); } } +static const clock_t refreshRate = CLOCKS_PER_SEC * 3 / 10; + static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, const void* const buffer, size_t bufferSize, /* buffer must end with noisy guard band */ const size_t* fileSizes, unsigned nbFiles, @@ -481,6 +480,7 @@ static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, BYTE* doneMarks = (BYTE*)malloc((bufferSize+16)*sizeof(*doneMarks)); /* +16 for overflow security */ U32* filePos = (U32*)malloc(nbFiles * sizeof(*filePos)); size_t result = 0; + clock_t displayClock = 0; /* init */ DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */ diff --git a/lib/dictBuilder/zdict.h b/lib/dictBuilder/zdict.h index c84aedd1f..642a43516 100644 --- a/lib/dictBuilder/zdict.h +++ b/lib/dictBuilder/zdict.h @@ -68,7 +68,7 @@ typedef struct { int compressionLevel; /* 0 means default; target a specific zstd compression level */ unsigned notificationLevel; /* Write to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */ unsigned dictID; /* 0 means auto mode (32-bits random value); other : force dictID value */ - unsigned reserved[2]; /* space for future parameters */ + unsigned reserved[2]; /* reserved space for future parameters */ } ZDICT_params_t; From 2b26ad194736e3c227f31b41c329a10351f746d5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 2 Sep 2016 15:34:41 -0700 Subject: [PATCH 28/32] removed timeb.h (#319) --- tests/fuzzer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 18948b913..b8b7f0c93 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -23,7 +23,6 @@ **************************************/ #include /* free */ #include /* fgets, sscanf */ -#include /* timeb */ #include /* strcmp */ #include /* clock_t */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressContinue, ZSTD_compressBlock */ From 855766d73dac1d19abb82f984015a842e09deef2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 2 Sep 2016 17:04:49 -0700 Subject: [PATCH 29/32] clarified dictionary in format description --- NEWS | 1 + lib/compress/zstd_compress.c | 1 - lib/dictBuilder/zdict.c | 12 ++++++------ zstd_compression_format.md | 23 ++++++++++++----------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/NEWS b/NEWS index 726a9e38d..dcfabbf37 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ v1.0.1 New : contrib/pzstd, parallel version of zstd, by Nick Terrell Fixed : CLI -d output to stdout by default when input is stdin (#322) Fixed : CLI correctly detects console on Mac OS-X +Fixed : compatibility with OpenBSD, reported by Juan Francisco Cantero Hurtado (#319) Fixed : zstd-pgo, reported by octoploid (#329) v1.0.0 diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 723d40559..9e733b8f2 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -8,7 +8,6 @@ */ - /*-******************************************************* * Compiler specifics *********************************************************/ diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 916a481eb..4d9e3a92e 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -463,12 +463,6 @@ static U32 ZDICT_dictSize(const dictItem* dictList) } -#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ - if (ZDICT_clockSpan(displayClock) > refreshRate) \ - { displayClock = clock(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stdout); } } -static const clock_t refreshRate = CLOCKS_PER_SEC * 3 / 10; - static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, const void* const buffer, size_t bufferSize, /* buffer must end with noisy guard band */ const size_t* fileSizes, unsigned nbFiles, @@ -481,6 +475,12 @@ static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, U32* filePos = (U32*)malloc(nbFiles * sizeof(*filePos)); size_t result = 0; clock_t displayClock = 0; + clock_t const refreshRate = CLOCKS_PER_SEC * 3 / 10; + +# define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ + if (ZDICT_clockSpan(displayClock) > refreshRate) \ + { displayClock = clock(); DISPLAY(__VA_ARGS__); \ + if (g_displayLevel>=4) fflush(stdout); } } /* init */ DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */ diff --git a/zstd_compression_format.md b/zstd_compression_format.md index 3facb3210..8a5d7b77e 100644 --- a/zstd_compression_format.md +++ b/zstd_compression_format.md @@ -551,7 +551,7 @@ Let's presume the following Huffman tree must be described : The tree depth is 4, since its smallest element uses 4 bits. Value `5` will not be listed, nor will values above `5`. Values from `0` to `4` will be listed using `Weight` instead of `Number_of_Bits`. -Weight formula is : +Weight formula is : ``` Weight = Number_of_Bits ? (Max_Number_of_Bits + 1 - Number_of_Bits) : 0 ``` @@ -779,7 +779,7 @@ which specifies `Baseline` and `Number_of_Bits` to add. _Codes_ are FSE compressed, and interleaved with raw additional bits in the same bitstream. -##### Literals length codes +##### Literals length codes Literals length codes are values ranging from `0` to `35` included. They define lengths from 0 to 131071 bytes. @@ -1126,10 +1126,10 @@ When `Repeated_Offset2` is used, it's swapped with `Repeated_Offset1`. Dictionary format ----------------- -`zstd` is compatible with "pure content" dictionaries, free of any format restriction. +`zstd` is compatible with "raw content" dictionaries, free of any format restriction. But dictionaries created by `zstd --train` follow a format, described here. -__Pre-requisites__ : a dictionary has a known length, +__Pre-requisites__ : a dictionary has a size, defined either by a buffer limit, or a file size. | `Magic_Number` | `Dictionary_ID` | `Entropy_Tables` | `Content` | @@ -1151,20 +1151,21 @@ _Reserved ranges :_ - high range : >= (2^31) __`Entropy_Tables`__ : following the same format as a [compressed blocks]. - They are stored in following order : - Huffman tables for literals, FSE table for offsets, - FSE table for match lengths, and FSE table for literals lengths. - It's finally followed by 3 offset values, populating recent offsets, - stored in order, 4-bytes little-endian each, for a total of 12 bytes. + They are stored in following order : + Huffman tables for literals, FSE table for offsets, + FSE table for match lengths, and FSE table for literals lengths. + It's finally followed by 3 offset values, populating recent offsets, + stored in order, 4-bytes little-endian each, for a total of 12 bytes. -__`Content`__ : Where the actual dictionary content is. - Content size depends on Dictionary size. +__`Content`__ : The rest of the dictionary is its content. + The content act as a "past" in front of data to compress or decompress. [compressed blocks]: #the-format-of-compressed_block Version changes --------------- +- 0.2.1 : clarify field names, by Przemyslaw Skibinski - 0.2.0 : numerous format adjustments for zstd v0.8 - 0.1.2 : limit Huffman tree depth to 11 bits - 0.1.1 : reserved dictID ranges From d56dbc02d35ad70f1760834a94726f8d109a9500 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 2 Sep 2016 17:28:41 -0700 Subject: [PATCH 30/32] removed g_displayLevel --- lib/dictBuilder/zdict.c | 62 +++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 4d9e3a92e..cfabb20ba 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -71,19 +71,18 @@ static const size_t g_min_fast_dictContent = 192; * Console display ***************************************/ #define DISPLAY(...) { fprintf(stderr, __VA_ARGS__); fflush( stderr ); } -#define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } -static unsigned g_displayLevel = 0; /* 0 : no display; 1: errors; 2: default; 4: full information */ +#define DISPLAYLEVEL(l, ...) if (notificationLevel>=l) { DISPLAY(__VA_ARGS__); } /* 0 : no display; 1: errors; 2: default; 3: details; 4: debug */ static clock_t ZDICT_clockSpan(clock_t nPrevious) { return clock() - nPrevious; } -static void ZDICT_printHex(U32 dlevel, const void* ptr, size_t length) +static void ZDICT_printHex(const void* ptr, size_t length) { const BYTE* const b = (const BYTE*)ptr; size_t u; for (u=0; u126) c = '.'; /* non-printable char */ - DISPLAYLEVEL(dlevel, "%c", c); + DISPLAY("%c", c); } } @@ -204,7 +203,7 @@ static void ZDICT_initDictItem(dictItem* d) static dictItem ZDICT_analyzePos( BYTE* doneMarks, const int* suffix, U32 start, - const void* buffer, U32 minRatio) + const void* buffer, U32 minRatio, U32 notificationLevel) { U32 lengthList[LLIMIT] = {0}; U32 cumulLength[LLIMIT] = {0}; @@ -466,7 +465,7 @@ static U32 ZDICT_dictSize(const dictItem* dictList) static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, const void* const buffer, size_t bufferSize, /* buffer must end with noisy guard band */ const size_t* fileSizes, unsigned nbFiles, - U32 minRatio) + U32 minRatio, U32 notificationLevel) { int* const suffix0 = (int*)malloc((bufferSize+2)*sizeof(*suffix0)); int* const suffix = suffix0+1; @@ -477,10 +476,10 @@ static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, clock_t displayClock = 0; clock_t const refreshRate = CLOCKS_PER_SEC * 3 / 10; -# define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ +# define DISPLAYUPDATE(l, ...) if (notificationLevel>=l) { \ if (ZDICT_clockSpan(displayClock) > refreshRate) \ { displayClock = clock(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stdout); } } + if (notificationLevel>=4) fflush(stdout); } } /* init */ DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */ @@ -518,7 +517,7 @@ static size_t ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, { U32 cursor; for (cursor=0; cursor < bufferSize; ) { dictItem solution; if (doneMarks[cursor]) { cursor++; continue; } - solution = ZDICT_analyzePos(doneMarks, suffix, reverseSuffix[cursor], buffer, minRatio); + solution = ZDICT_analyzePos(doneMarks, suffix, reverseSuffix[cursor], buffer, minRatio, notificationLevel); if (solution.length==0) { cursor++; continue; } ZDICT_insertDictItem(dictList, dictListSize, solution); cursor += solution.length; @@ -558,15 +557,15 @@ typedef struct static void ZDICT_countEStats(EStats_ress_t esr, ZSTD_parameters params, U32* countLit, U32* offsetcodeCount, U32* matchlengthCount, U32* litlengthCount, U32* repOffsets, - const void* src, size_t srcSize) + const void* src, size_t srcSize, U32 notificationLevel) { size_t const blockSizeMax = MIN (ZSTD_BLOCKSIZE_ABSOLUTEMAX, 1 << params.cParams.windowLog); size_t cSize; if (srcSize > blockSizeMax) srcSize = blockSizeMax; /* protection vs large samples */ - { size_t const errorCode = ZSTD_copyCCtx(esr.zc, esr.ref); - if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_copyCCtx failed \n"); return; } - } + { size_t const errorCode = ZSTD_copyCCtx(esr.zc, esr.ref); + if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_copyCCtx failed \n"); return; } + } cSize = ZSTD_compressBlock(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_ABSOLUTEMAX, src, srcSize); if (ZSTD_isError(cSize)) { DISPLAYLEVEL(1, "warning : could not compress sample size %u \n", (U32)srcSize); return; } @@ -647,9 +646,10 @@ static void ZDICT_insertSortCount(offsetCount_t table[ZSTD_REP_NUM+1], U32 val, #define OFFCODE_MAX 30 /* only applicable to first block */ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, - unsigned compressionLevel, - const void* srcBuffer, const size_t* fileSizes, unsigned nbFiles, - const void* dictBuffer, size_t dictBufferSize) + unsigned compressionLevel, + const void* srcBuffer, const size_t* fileSizes, unsigned nbFiles, + const void* dictBuffer, size_t dictBufferSize, + unsigned notificationLevel) { U32 countLit[256]; HUF_CREATE_STATIC_CTABLE(hufTable, 255); @@ -690,18 +690,19 @@ static size_t ZDICT_analyzeEntropy(void* dstBuffer, size_t maxDstSize, memset(bestRepOffset, 0, sizeof(bestRepOffset)); if (compressionLevel==0) compressionLevel=g_compressionLevel_default; params = ZSTD_getParams(compressionLevel, averageSampleSize, dictBufferSize); - { size_t const beginResult = ZSTD_compressBegin_advanced(esr.ref, dictBuffer, dictBufferSize, params, 0); - if (ZSTD_isError(beginResult)) { - eSize = ERROR(GENERIC); - DISPLAYLEVEL(1, "error : ZSTD_compressBegin_advanced failed \n"); - goto _cleanup; - } } + { size_t const beginResult = ZSTD_compressBegin_advanced(esr.ref, dictBuffer, dictBufferSize, params, 0); + if (ZSTD_isError(beginResult)) { + eSize = ERROR(GENERIC); + DISPLAYLEVEL(1, "error : ZSTD_compressBegin_advanced failed \n"); + goto _cleanup; + } } /* collect stats on all files */ for (u=0; u= 3) { + if (params.notificationLevel>= 3) { U32 const nb = MIN(25, dictList[0].pos); U32 const dictContentSize = ZDICT_dictSize(dictList); U32 u; @@ -901,7 +903,7 @@ size_t ZDICT_trainFromBuffer_unsafe( U32 printedLength = MIN(40, length); DISPLAYLEVEL(3, "%3u:%3u bytes at pos %8u, savings %7u bytes |", u, length, pos, dictList[u].savings); - ZDICT_printHex(3, (const char*)samplesBuffer+pos, printedLength); + ZDICT_printHex((const char*)samplesBuffer+pos, printedLength); DISPLAYLEVEL(3, "| \n"); } } From 9622fe499d0ddc867508d3724e10a0bdc6cf2dfa Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 2 Sep 2016 20:11:22 -0700 Subject: [PATCH 31/32] Fix memory usage issues. --- contrib/pzstd/Makefile | 2 ++ contrib/pzstd/Pzstd.cpp | 32 +++++++++++++++++----- contrib/pzstd/utils/WorkQueue.h | 17 ++++++++++++ contrib/pzstd/utils/test/WorkQueueTest.cpp | 21 ++++++++++++++ 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/contrib/pzstd/Makefile b/contrib/pzstd/Makefile index c59a6d107..d71cf5b34 100644 --- a/contrib/pzstd/Makefile +++ b/contrib/pzstd/Makefile @@ -63,7 +63,9 @@ googletest: @cd googletest/build && cmake .. && make test: libzstd.a Pzstd.o Options.o SkippableFrame.o + $(MAKE) -C utils/test clean $(MAKE) -C utils/test test + $(MAKE) -C test clean $(MAKE) -C test test clean: diff --git a/contrib/pzstd/Pzstd.cpp b/contrib/pzstd/Pzstd.cpp index 84f6a2e4c..ddfa59556 100644 --- a/contrib/pzstd/Pzstd.cpp +++ b/contrib/pzstd/Pzstd.cpp @@ -67,11 +67,14 @@ size_t pzstdMain(const Options& options, ErrorHolder& errorHolder) { // WorkQueue outlives ThreadPool so in the case of error we are certain // we don't accidently try to call push() on it after it is destroyed. - WorkQueue> outs; + WorkQueue> outs{2 * options.numThreads}; size_t bytesWritten; { - // Initialize the thread pool with numThreads - ThreadPool executor(options.numThreads); + // Initialize the thread pool with numThreads + 1 + // We add one because the read thread spends most of its time waiting. + // This also sets the minimum number of threads to 2, so the algorithm + // doesn't deadlock. + ThreadPool executor(options.numThreads + 1); if (!options.decompress) { // Add a job that reads the input and starts all the compression jobs executor.add( @@ -229,6 +232,15 @@ calculateStep(size_t size, size_t numThreads, const ZSTD_parameters& params) { namespace { enum class FileStatus { Continue, Done, Error }; +/// Determines the status of the file descriptor `fd`. +FileStatus fileStatus(FILE* fd) { + if (std::feof(fd)) { + return FileStatus::Done; + } else if (std::ferror(fd)) { + return FileStatus::Error; + } + return FileStatus::Continue; +} } // anonymous namespace /** @@ -243,10 +255,9 @@ readData(BufferWorkQueue& queue, size_t chunkSize, size_t size, FILE* fd) { auto bytesRead = std::fread(buffer.data(), 1, std::min(chunkSize, buffer.size()), fd); queue.push(buffer.splitAt(bytesRead)); - if (std::feof(fd)) { - return FileStatus::Done; - } else if (std::ferror(fd) || bytesRead == 0) { - return FileStatus::Error; + auto status = fileStatus(fd); + if (status != FileStatus::Continue) { + return status; } } return FileStatus::Continue; @@ -388,6 +399,7 @@ void asyncDecompressFrames( // frameSize is 0 if the frame info can't be decoded. Buffer buffer(SkippableFrame::kSize); auto bytesRead = std::fread(buffer.data(), 1, buffer.size(), fd); + status = fileStatus(fd); if (bytesRead == 0 && status != FileStatus::Continue) { break; } @@ -395,6 +407,12 @@ void asyncDecompressFrames( frameSize = SkippableFrame::tryRead(buffer.range()); in->push(std::move(buffer)); } + if (frameSize == 0) { + // We hit a non SkippableFrame, so this will be the last job. + // Make sure that we don't use too much memory + in->setMaxSize(64); + out->setMaxSize(64); + } // Start decompression in the thread pool executor.add([&errorHolder, in, out] { return decompress(errorHolder, std::move(in), std::move(out)); diff --git a/contrib/pzstd/utils/WorkQueue.h b/contrib/pzstd/utils/WorkQueue.h index 2fa417f41..538213500 100644 --- a/contrib/pzstd/utils/WorkQueue.h +++ b/contrib/pzstd/utils/WorkQueue.h @@ -99,6 +99,19 @@ class WorkQueue { return true; } + /** + * Sets the maximum queue size. If `maxSize == 0` then it is unbounded. + * + * @param maxSize The new maximum queue size. + */ + void setMaxSize(std::size_t maxSize) { + { + std::lock_guard lock(mutex_); + maxSize_ = maxSize; + } + writerCv_.notify_all(); + } + /** * Promise that `push()` won't be called again, so once the queue is empty * there will never any more work. @@ -149,6 +162,10 @@ class BufferWorkQueue { return result; } + void setMaxSize(std::size_t maxSize) { + queue_.setMaxSize(maxSize); + } + void finish() { queue_.finish(); } diff --git a/contrib/pzstd/utils/test/WorkQueueTest.cpp b/contrib/pzstd/utils/test/WorkQueueTest.cpp index 074891fda..84d8573c3 100644 --- a/contrib/pzstd/utils/test/WorkQueueTest.cpp +++ b/contrib/pzstd/utils/test/WorkQueueTest.cpp @@ -175,6 +175,27 @@ TEST(WorkQueue, BoundedSizePushAfterFinish) { pusher.join(); } +TEST(WorkQueue, SetMaxSize) { + WorkQueue queue(2); + int result; + queue.push(5); + queue.push(6); + queue.setMaxSize(1); + std::thread pusher([&queue] { + queue.push(7); + }); + // Dirtily try and make sure that pusher has run. + std::this_thread::sleep_for(std::chrono::seconds(1)); + queue.finish(); + EXPECT_TRUE(queue.pop(result)); + EXPECT_EQ(5, result); + EXPECT_TRUE(queue.pop(result)); + EXPECT_EQ(6, result); + EXPECT_FALSE(queue.pop(result)); + + pusher.join(); +} + TEST(WorkQueue, BoundedSizeMPMC) { WorkQueue queue(100); std::vector results(10000, -1); From 33a0465a51a1b6d90f1989601cf4ef407dc3446f Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 2 Sep 2016 22:11:49 -0700 Subject: [PATCH 32/32] fixed a few links --- NEWS | 2 +- lib/libzstd.pc.in | 2 +- programs/zstd.1 | 2 +- projects/README.md | 2 +- projects/cmake/CMakeLists.txt | 32 ++++---------------------------- tests/test-zstd-speed.py | 2 +- tests/test-zstd-versions.py | 2 +- zstd.rb | 18 ------------------ 8 files changed, 10 insertions(+), 52 deletions(-) delete mode 100644 zstd.rb diff --git a/NEWS b/NEWS index dcfabbf37..fce31cb2e 100644 --- a/NEWS +++ b/NEWS @@ -189,6 +189,6 @@ frame concatenation support v0.1.1 fix compression bug detects write-flush errors -git@github.com:Cyan4973/zstd.git + v0.1.0 first release diff --git a/lib/libzstd.pc.in b/lib/libzstd.pc.in index 28afc3add..9399363dd 100644 --- a/lib/libzstd.pc.in +++ b/lib/libzstd.pc.in @@ -8,7 +8,7 @@ includedir=@INCLUDEDIR@ Name: zstd Description: lossless compression algorithm library -URL: https://github.com/Cyan4973/zstd +URL: https://github.com/facebook/zstd Version: @VERSION@ Libs: -L@LIBDIR@ -lzstd Cflags: -I@INCLUDEDIR@ diff --git a/programs/zstd.1 b/programs/zstd.1 index 0a0ad629d..a23616529 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -135,7 +135,7 @@ Typical gains range from ~10% (at 64KB) to x5 better (at <1KB). .SH BUGS -Report bugs at:- https://github.com/Cyan4973/zstd/issues +Report bugs at:- https://github.com/facebook/zstd/issues .SH AUTHOR Yann Collet diff --git a/projects/README.md b/projects/README.md index 39742fb1a..dd60b56e8 100644 --- a/projects/README.md +++ b/projects/README.md @@ -14,7 +14,7 @@ The following projects are included with the zstd distribution: #### How to compile zstd with Visual Studio 1. Install Visual Studio e.g. VS 2015 Community Edition (it's free). -2. Download the latest version of zstd from https://github.com/Cyan4973/zstd/releases +2. Download the latest version of zstd from https://github.com/facebook/zstd/releases 3. Decompress ZIP archive. 4. Go to decompressed directory then to `projects` then `VS2010` and open `zstd.sln` 5. Visual Studio will ask about converting VS2010 project to VS2015 and you should agree. diff --git a/projects/cmake/CMakeLists.txt b/projects/cmake/CMakeLists.txt index 86178bf97..b8f5d18e4 100644 --- a/projects/cmake/CMakeLists.txt +++ b/projects/cmake/CMakeLists.txt @@ -1,34 +1,10 @@ # ################################################################ -# zstd - Makefile -# Copyright (C) Yann Collet 2014-2016 +# Copyright (c) 2016-present, Yann Collet, Facebook, Inc. # All rights reserved. -# -# BSD license # -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, this -# list of conditions and the following disclaimer in the documentation and/or -# other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# You can contact the author at : -# - zstd source repository : https://github.com/Cyan4973/zstd -# - Public forum : https://groups.google.com/forum/#!forum/lz4c +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. An additional grant +# of patent rights can be found in the PATENTS file in the same directory. # ################################################################ PROJECT(zstd) diff --git a/tests/test-zstd-speed.py b/tests/test-zstd-speed.py index 45cfe865d..8d4d172b4 100755 --- a/tests/test-zstd-speed.py +++ b/tests/test-zstd-speed.py @@ -18,7 +18,7 @@ import traceback import hashlib script_version = 'v0.8.0 (2016-08-03)' -default_repo_url = 'https://github.com/Cyan4973/zstd.git' +default_repo_url = 'https://github.com/facebook/zstd.git' working_dir_name = 'speedTest' working_path = os.getcwd() + '/' + working_dir_name # /path/to/zstd/tests/speedTest clone_path = working_path + '/' + 'zstd' # /path/to/zstd/tests/speedTest/zstd diff --git a/tests/test-zstd-versions.py b/tests/test-zstd-versions.py index 5a24bc4d7..a5a713009 100755 --- a/tests/test-zstd-versions.py +++ b/tests/test-zstd-versions.py @@ -19,7 +19,7 @@ import sys import subprocess from subprocess import Popen, PIPE -repo_url = 'https://github.com/Cyan4973/zstd.git' +repo_url = 'https://github.com/facebook/zstd.git' tmp_dir_name = 'tests/versionsTest' make_cmd = 'make' git_cmd = 'git' diff --git a/zstd.rb b/zstd.rb deleted file mode 100644 index 0aa72a213..000000000 --- a/zstd.rb +++ /dev/null @@ -1,18 +0,0 @@ -class Zstd < Formula - desc "Zstandard - Fast real-time compression algorithm" - homepage "http://www.zstd.net/" - url "https://github.com/Cyan4973/zstd/archive/v1.0.0.tar.gz" - sha256 "197e6ef74da878cbf72844f38461bb18129d144fd5221b3598e973ecda6f5963" - - def install - system "make", "install", "PREFIX=#{prefix}" - end - - test do - (testpath/"input.txt").write("Hello, world." * 10) - system "#{bin}/zstd", "input.txt", "-o", "compressed.zst" - system "#{bin}/zstd", "--test", "compressed.zst" - system "#{bin}/zstd", "-d", "compressed.zst", "-o", "decompressed.txt" - system "cmp", "input.txt", "decompressed.txt" - end -end