From 7d3602807891e54673867524821a2fa4a9709cc3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 12 Feb 2016 00:07:30 +0100 Subject: [PATCH] Integrate dictBuilder into libzstd --- dictBuilder/Makefile | 7 +- dictBuilder/dibcli.c | 8 +- dictBuilder/dibio.c | 281 ++++ dictBuilder/dibio.h | 64 + dictBuilder/dictBuilder.h | 94 -- dictBuilder/dictionary | Bin 0 -> 112640 bytes dictBuilder/divsufsort.c | 404 ----- dictBuilder/divsufsort.h | 180 -- dictBuilder/divsufsort_private.h | 212 --- dictBuilder/lfs.h | 56 - dictBuilder/sssort.c | 844 ---------- dictBuilder/trsort.c | 615 ------- dictBuilder/utils.c | 381 ----- lib/Makefile | 6 +- {dictBuilder => lib}/dictBuilder.c | 348 ++-- lib/dictBuilder.h | 67 + lib/dictBuilder_static.h | 104 ++ lib/divsufsort.c | 1905 ++++++++++++++++++++++ dictBuilder/config.h => lib/divsufsort.h | 72 +- lib/mem.h | 11 +- lib/zstd.h | 2 +- lib/zstd_compress.c | 78 +- lib/zstd_internal.h | 65 +- lib/zstd_static.h | 7 +- 24 files changed, 2658 insertions(+), 3153 deletions(-) create mode 100644 dictBuilder/dibio.c create mode 100644 dictBuilder/dibio.h delete mode 100644 dictBuilder/dictBuilder.h create mode 100644 dictBuilder/dictionary delete mode 100644 dictBuilder/divsufsort.c delete mode 100644 dictBuilder/divsufsort.h delete mode 100644 dictBuilder/divsufsort_private.h delete mode 100644 dictBuilder/lfs.h delete mode 100644 dictBuilder/sssort.c delete mode 100644 dictBuilder/trsort.c delete mode 100644 dictBuilder/utils.c rename {dictBuilder => lib}/dictBuilder.c (72%) create mode 100644 lib/dictBuilder.h create mode 100644 lib/dictBuilder_static.h create mode 100644 lib/divsufsort.c rename dictBuilder/config.h => lib/divsufsort.h (52%) diff --git a/dictBuilder/Makefile b/dictBuilder/Makefile index e5a4f1aa7..949995253 100644 --- a/dictBuilder/Makefile +++ b/dictBuilder/Makefile @@ -1,6 +1,6 @@ # ########################################################################## # Dict Builder - Makefile -# Copyright (C) Yann Collet 2015 +# Copyright (C) Yann Collet 2016 # # GPL v2 License # @@ -19,8 +19,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # You can contact the author at : -# - ZSTD source repository : http://code.google.com/p/zstd/ -# - Public forum : https://groups.google.com/forum/#!forum/lz4c +# - ZSTD homepage : http://www.zstd.net/ # ########################################################################## CPPFLAGS= -I../lib @@ -47,7 +46,7 @@ default: dictBuilder all: dictBuilder -dictBuilder: dictBuilder.c dibcli.c divsufsort.c sssort.c trsort.c $(ZSTDDIR)/huff0.c $(ZSTDDIR)/fse.c $(ZSTDDIR)/zstd_decompress.c +dictBuilder: dibio.c dibcli.c $(ZSTDDIR)/dictBuilder.c $(ZSTDDIR)/huff0.c $(ZSTDDIR)/fse.c $(ZSTDDIR)/zstd_decompress.c $(ZSTDDIR)/zstd_compress.c $(ZSTDDIR)/divsufsort.c $(CC) $(FLAGS) $^ -o $@$(EXT) clean: diff --git a/dictBuilder/dibcli.c b/dictBuilder/dibcli.c index 8566c6245..a8009ae63 100644 --- a/dictBuilder/dibcli.c +++ b/dictBuilder/dibcli.c @@ -37,8 +37,8 @@ #include /* exit, calloc, free */ #include /* strcmp, strlen */ #include /* fprintf, getchar */ - -#include "dictBuilder.h" +#include "dibio.h" +#include "zstd.h" /* version numbers */ /*-************************************ @@ -48,7 +48,7 @@ #ifndef PROGRAM_VERSION # define QUOTE(str) #str # define EXP_Q(str) QUOTE(str) -# define PROGRAM_VERSION "v" EXP_Q(DiB_VERSION_MAJOR) "." EXP_Q(DiB_VERSION_MINOR) "." EXP_Q(DiB_VERSION_RELEASE) +# define PROGRAM_VERSION "v" EXP_Q(ZSTD_VERSION_MAJOR) "." EXP_Q(ZSTD_VERSION_MINOR) "." EXP_Q(ZSTD_VERSION_RELEASE) #endif #define AUTHOR "Yann Collet" #define WELCOME_MESSAGE "*** %s %s %i-bits, by %s ***\n", PROGRAM_DESCRIPTION, PROGRAM_VERSION, (int)(sizeof(void*)*8), AUTHOR @@ -248,7 +248,7 @@ int main(int argCount, const char** argv) /* building ... */ { - DiB_params_t param; + ZDICT_params_t param; param.selectivityLevel = selectionLevel; param.compressionLevel = cLevel; DiB_setNotificationLevel(g_displayLevel); diff --git a/dictBuilder/dibio.c b/dictBuilder/dibio.c new file mode 100644 index 000000000..679b6504c --- /dev/null +++ b/dictBuilder/dibio.c @@ -0,0 +1,281 @@ +/* + dibio - I/O API for dictionary builder + Copyright (C) Yann Collet 2016 + + GPL v2 License + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + You can contact the author at : + - zstd homepage : http://www.zstd.net/ +*/ + +/*-************************************** +* Compiler Options +****************************************/ +/* Disable some Visual warning messages */ +#ifdef _MSC_VER +# define _CRT_SECURE_NO_WARNINGS /* fopen */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +#endif + +/* Unix Large Files support (>4GB) */ +#define _FILE_OFFSET_BITS 64 +#if (defined(__sun__) && (!defined(__LP64__))) /* Sun Solaris 32-bits requires specific definitions */ +# define _LARGEFILE_SOURCE +#elif ! defined(__LP64__) /* No point defining Large file for 64 bit */ +# define _LARGEFILE64_SOURCE +#endif + + +/*-************************************* +* Includes +***************************************/ +#include /* malloc, free */ +#include /* memset */ +#include /* fprintf, fopen, ftello64 */ +#include /* stat64 */ +#include /* stat64 */ +#include /* clock */ + +#include "mem.h" /* read */ +#include "error_private.h" +#include "dictBuilder_static.h" + +/*-************************************* +* Compiler specifics +***************************************/ +#if !defined(S_ISREG) +# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) +#endif + + +/*-************************************* +* Constants +***************************************/ +#define KB *(1 <<10) +#define MB *(1 <<20) +#define GB *(1U<<30) + +#define DICTLISTSIZE 10000 +#define MEMMULT 11 +static const size_t maxMemory = (sizeof(size_t) == 4) ? (2 GB - 64 MB) : ((size_t)(512 MB) << sizeof(size_t)); + +#define NOISELENGTH 32 +#define PRIME1 2654435761U +#define PRIME2 2246822519U + +#define MINRATIO 4 +static const U32 g_compressionLevel_default = 5; +static const U32 g_selectivity_default = 9; +static const size_t g_provision_entropySize = 200; +static const size_t g_min_fast_dictContent = 192; + + +/*-************************************* +* Console display +***************************************/ +#define DISPLAY(...) fprintf(stderr, __VA_ARGS__) +#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 */ +void DiB_setNotificationLevel(unsigned l) { g_displayLevel=l; ZDICT_setNotificationLevel(l); } + +void DiB_printHex(U32 dlevel, 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); + } +} + + +/*-************************************* +* Exceptions +***************************************/ +#ifndef DEBUG +# define DEBUG 0 +#endif +#define DEBUGOUTPUT(...) if (DEBUG) DISPLAY(__VA_ARGS__); +#define EXM_THROW(error, ...) \ +{ \ + DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \ + DISPLAYLEVEL(1, "Error %i : ", error); \ + DISPLAYLEVEL(1, __VA_ARGS__); \ + DISPLAYLEVEL(1, "\n"); \ + exit(error); \ +} + + +/* ******************************************************** +* Helper functions +**********************************************************/ +unsigned DiB_isError(size_t errorCode) { return ERR_isError(errorCode); } + +const char* DiB_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); } + + +/* ******************************************************** +* File related operations +**********************************************************/ +static unsigned long long DiB_getFileSize(const char* infilename) +{ + int r; +#if defined(_MSC_VER) + struct _stat64 statbuf; + r = _stat64(infilename, &statbuf); +#else + struct stat statbuf; + r = stat(infilename, &statbuf); +#endif + if (r || !S_ISREG(statbuf.st_mode)) return 0; /* No good... */ + return (unsigned long long)statbuf.st_size; +} + + +static unsigned long long DiB_getTotalFileSize(const char** fileNamesTable, unsigned nbFiles) +{ + unsigned long long total = 0; + unsigned n; + for (n=0; n bufferSize-pos) fileSize = 0; /* stop there, not enough memory to load all files */ + readSize = fread(buff+pos, 1, (size_t)fileSize, f); + if (readSize != (size_t)fileSize) EXM_THROW(11, "could not read %s", fileNamesTable[n]); + pos += readSize; + fileSizes[n] = (size_t)fileSize; + fclose(f); + } +} + + +/*-******************************************************** +* Dictionary training functions +**********************************************************/ +static size_t DiB_findMaxMem(unsigned long long requiredMem) +{ + size_t step = 8 MB; + void* testmem = NULL; + + requiredMem = (((requiredMem >> 23) + 1) << 23); + requiredMem += 2 * step; + if (requiredMem > maxMemory) requiredMem = maxMemory; + + while (!testmem) { + requiredMem -= step; + testmem = malloc((size_t)requiredMem); + } + + free(testmem); + return (size_t)(requiredMem - step); +} + + +static void DiB_fillNoise(void* buffer, size_t length) +{ + unsigned acc = PRIME1; + size_t p=0;; + + for (p=0; p> 21); + } +} + + +static void DiB_saveDict(const char* dictFileName, + const void* buff, size_t buffSize) +{ + FILE* f; + size_t n; + + f = fopen(dictFileName, "wb"); + if (f==NULL) EXM_THROW(3, "cannot open %s ", dictFileName); + + n = fwrite(buff, 1, buffSize, f); + if (n!=buffSize) EXM_THROW(4, "%s : write error", dictFileName) + + n = (size_t)fclose(f); + if (n!=0) EXM_THROW(5, "%s : flush error", dictFileName) +} + + +int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, + const char** fileNamesTable, unsigned nbFiles, + ZDICT_params_t params) +{ + void* srcBuffer; + size_t benchedSize; + size_t* fileSizes = (size_t*)malloc(nbFiles * sizeof(size_t)); + unsigned long long totalSizeToLoad = DiB_getTotalFileSize(fileNamesTable, nbFiles); + void* dictBuffer = malloc(maxDictSize); + size_t dictSize; + int result = 0; + + /* init */ + benchedSize = DiB_findMaxMem(totalSizeToLoad * MEMMULT) / MEMMULT; + if ((unsigned long long)benchedSize > totalSizeToLoad) benchedSize = (size_t)totalSizeToLoad; + if (benchedSize < totalSizeToLoad) + DISPLAYLEVEL(1, "Not enough memory; training on %u MB only...\n", (unsigned)(benchedSize >> 20)); + + /* Memory allocation & restrictions */ + srcBuffer = malloc(benchedSize+NOISELENGTH); /* + noise */ + if ((!fileSizes) || (!srcBuffer) || (!dictBuffer)) EXM_THROW(12, "not enough memory for DiB_trainFiles"); /* should not happen */ + + /* Load input buffer */ + DiB_loadFiles(srcBuffer, benchedSize, fileSizes, fileNamesTable, nbFiles); + DiB_fillNoise((char*)srcBuffer + benchedSize, NOISELENGTH); /* guard band, for end of buffer condition */ + + /* call buffer version */ + dictSize = ZDICT_trainFromBuffer_unsafe(dictBuffer, maxDictSize, + srcBuffer, fileSizes, nbFiles, + params); + if (ZDICT_isError(dictSize)) { + DISPLAYLEVEL(1, "dictionary training failed : %s", ZDICT_getErrorName(dictSize)); /* should not happen */ + result = 1; + goto _cleanup; + } + + /* save dict */ + DISPLAYLEVEL(2, "Save dictionary of size %u into file %s \n", (U32)dictSize, dictFileName); + DiB_saveDict(dictFileName, dictBuffer, dictSize); + + /* clean up */ +_cleanup: + free(srcBuffer); + free(dictBuffer); + free(fileSizes); + return result; +} diff --git a/dictBuilder/dibio.h b/dictBuilder/dibio.h new file mode 100644 index 000000000..c9aee1fcd --- /dev/null +++ b/dictBuilder/dibio.h @@ -0,0 +1,64 @@ +/* + dibio.h - I/O API for dictionary builder + Copyright (C) Yann Collet 2016 + + GPL v2 License + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + You can contact the author at : + - zstd source repository : https://github.com/Cyan4973/zstd + - ztsd public forum : https://groups.google.com/forum/#!forum/lz4c +*/ + +/* This library is designed for a single-threaded console application. +* It exit() and printf() into stderr when it encounters an error condition. */ + +#ifndef DIBIO_H_003 +#define DIBIO_H_003 + + +/*-************************************* +* Dependencies +***************************************/ +#include "dictBuilder_static.h" /* ZDICT_params_t */ + + +/*-************************************* +* Public functions +***************************************/ +/*! DiB_trainFromFiles() : + Train a dictionary from a set of files provided by `fileNamesTable`. + Resulting dictionary is written into file `dictFileName`. + `parameters` is optional and can be provided with values set to 0, meaning "default". + @return : 0 == ok. Any other : error. +*/ +int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, + const char** fileNamesTable, unsigned nbFiles, + ZDICT_params_t parameters); + + +/*-************************************* +* Helper functions +***************************************/ +/*! DiB_setNotificationLevel + Set amount of notification to be displayed on the console. + default initial value : 0 = no console notification. + Note : not thread-safe (use a global constant) +*/ +void DiB_setNotificationLevel(unsigned l); + + +#endif diff --git a/dictBuilder/dictBuilder.h b/dictBuilder/dictBuilder.h deleted file mode 100644 index a022583eb..000000000 --- a/dictBuilder/dictBuilder.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - dictBuilder.h - Copyright (C) Yann Collet 2016 - - GPL v2 License - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - You can contact the author at : - - zstd source repository : https://github.com/Cyan4973/zstd - - ztsd public forum : https://groups.google.com/forum/#!forum/lz4c -*/ - -/* This library is designed for a single-threaded console application. -* It exit() and printf() into stderr when it encounters an error condition. */ - -#ifndef DICTBUILDER_H_001 -#define DICTBUILDER_H_001 - -/*-************************************* -* Version -***************************************/ -#define DiB_VERSION_MAJOR 0 /* for breaking interface changes */ -#define DiB_VERSION_MINOR 0 /* for new (non-breaking) interface capabilities */ -#define DiB_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */ -#define DiB_VERSION_NUMBER (DiB_VERSION_MAJOR *100*100 + DiB_VERSION_MINOR *100 + DiB_VERSION_RELEASE) -unsigned DiB_versionNumber (void); - - -/*-************************************* -* Public type -***************************************/ -typedef struct { - unsigned selectivityLevel; /* 0 means default; larger => bigger selection => larger dictionary */ - unsigned compressionLevel; /* 0 means default; target a specific zstd compression level */ -} DiB_params_t; - - -/*-************************************* -* Public functions -***************************************/ -/*! DiB_trainFromBuffer - Train a dictionary from a memory buffer @samplesBuffer - where @nbSamples samples have been stored concatenated. - Each sample size is provided into an orderly table @sampleSizes. - Resulting dictionary will be saved into @dictBuffer. - @parameters is optional and can be provided with 0 values to mean "default". - @result : size of dictionary stored into @dictBuffer (<= @dictBufferSize) - or an error code, which can be tested by DiB_isError(). - note : DiB_trainFromBuffer() will send notifications into stderr if instructed to, using DiB_setNotificationLevel() -*/ -size_t DiB_trainFromBuffer(void* dictBuffer, size_t dictBufferSize, - const void* samplesBuffer, const size_t* sampleSizes, unsigned nbSamples, - DiB_params_t parameters); - - -/*! DiB_trainFromFiles - Train a dictionary from a set of files provided by @fileNamesTable - Resulting dictionary is written into file @dictFileName. - @parameters is optional and can be provided with 0 values. - @result : 0 == ok. Any other : error. -*/ -int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, - const char** fileNamesTable, unsigned nbFiles, - DiB_params_t parameters); - - -/*-************************************* -* Helper functions -***************************************/ -unsigned DiB_isError(size_t errorCode); -const char* DiB_getErrorName(size_t errorCode); - -/*! DiB_setNotificationLevel - Set amount of notification to be displayed on the console. - default initial value : 0 = no console notification. - Note : not thread-safe (use a global constant) -*/ -void DiB_setNotificationLevel(unsigned l); - - -#endif diff --git a/dictBuilder/dictionary b/dictBuilder/dictionary new file mode 100644 index 0000000000000000000000000000000000000000..d6bf575c61f3b366de2c0aadadb0b484deaf377d GIT binary patch literal 112640 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≀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 -#endif - - -/*- Private Functions -*/ - -/* Sorts suffixes of type B*. */ -static -saidx_t -sort_typeBstar(const sauchar_t *T, saidx_t *SA, - saidx_t *bucket_A, saidx_t *bucket_B, - saidx_t n) { - saidx_t *PAb, *ISAb, *buf; -#ifdef _OPENMP - saidx_t *curbuf; - saidx_t l; -#endif - saidx_t i, j, k, t, m, bufsize; - saint_t c0, c1; -#ifdef _OPENMP - saint_t d0, d1; - int tmp; -#endif - - /* Initialize bucket arrays. */ - for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } - for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } - - /* Count the number of occurrences of the first one or two characters of each - type A, B and B* suffix. Moreover, store the beginning position of all - type B* suffixes into the array SA. */ - for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { - /* type A suffix. */ - do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); - if(0 <= i) { - /* type B* suffix. */ - ++BUCKET_BSTAR(c0, c1); - SA[--m] = i; - /* type B suffix. */ - for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { - ++BUCKET_B(c0, c1); - } - } - } - m = n - m; -/* -note: - A type B* suffix is lexicographically smaller than a type B suffix that - begins with the same first two characters. -*/ - - /* Calculate the index of start/end point of each bucket. */ - for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { - t = i + BUCKET_A(c0); - BUCKET_A(c0) = i + j; /* start point */ - i = t + BUCKET_B(c0, c0); - for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { - j += BUCKET_BSTAR(c0, c1); - BUCKET_BSTAR(c0, c1) = j; /* end point */ - i += BUCKET_B(c0, c1); - } - } - - if(0 < m) { - /* Sort the type B* suffixes by their first two characters. */ - PAb = SA + n - m; ISAb = SA + m; - for(i = m - 2; 0 <= i; --i) { - t = PAb[i], c0 = T[t], c1 = T[t + 1]; - SA[--BUCKET_BSTAR(c0, c1)] = i; - } - t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; - SA[--BUCKET_BSTAR(c0, c1)] = m - 1; - - /* Sort the type B* substrings using sssort. */ -#ifdef _OPENMP - tmp = omp_get_max_threads(); - buf = SA + m, bufsize = (n - (2 * m)) / tmp; - c0 = ALPHABET_SIZE - 2, c1 = ALPHABET_SIZE - 1, j = m; -#pragma omp parallel default(shared) private(curbuf, k, l, d0, d1, tmp) - { - tmp = omp_get_thread_num(); - curbuf = buf + tmp * bufsize; - k = 0; - for(;;) { - #pragma omp critical(sssort_lock) - { - if(0 < (l = j)) { - d0 = c0, d1 = c1; - do { - k = BUCKET_BSTAR(d0, d1); - if(--d1 <= d0) { - d1 = ALPHABET_SIZE - 1; - if(--d0 < 0) { break; } - } - } while(((l - k) <= 1) && (0 < (l = k))); - c0 = d0, c1 = d1, j = k; - } - } - if(l == 0) { break; } - sssort(T, PAb, SA + k, SA + l, - curbuf, bufsize, 2, n, *(SA + k) == (m - 1)); - } - } -#else - buf = SA + m, bufsize = n - (2 * m); - for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { - for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { - i = BUCKET_BSTAR(c0, c1); - if(1 < (j - i)) { - sssort(T, PAb, SA + i, SA + j, - buf, bufsize, 2, n, *(SA + i) == (m - 1)); - } - } - } -#endif - - /* Compute ranks of type B* substrings. */ - for(i = m - 1; 0 <= i; --i) { - if(0 <= SA[i]) { - j = i; - do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); - SA[i + 1] = i - j; - if(i <= 0) { break; } - } - j = i; - do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); - ISAb[SA[i]] = j; - } - - /* Construct the inverse suffix array of type B* suffixes using trsort. */ - trsort(ISAb, SA, m, 1); - - /* Set the sorted order of tyoe B* suffixes. */ - for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { - for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { } - if(0 <= i) { - t = i; - for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { } - SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; - } - } - - /* Calculate the index of start/end point of each bucket. */ - BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */ - for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { - i = BUCKET_A(c0 + 1) - 1; - for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { - t = i - BUCKET_B(c0, c1); - BUCKET_B(c0, c1) = i; /* end point */ - - /* Move all type B* suffixes to the correct position. */ - for(i = t, j = BUCKET_BSTAR(c0, c1); - j <= k; - --i, --k) { SA[i] = SA[k]; } - } - BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */ - BUCKET_B(c0, c0) = i; /* end point */ - } - } - - return m; -} - -/* Constructs the suffix array by using the sorted order of type B* suffixes. */ -static -void -construct_SA(const sauchar_t *T, saidx_t *SA, - saidx_t *bucket_A, saidx_t *bucket_B, - saidx_t n, saidx_t m) { - saidx_t *i, *j, *k; - saidx_t s; - saint_t c0, c1, c2; - - if(0 < m) { - /* Construct the sorted order of type B suffixes by using - the sorted order of type B* suffixes. */ - for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { - /* Scan the suffix array from right to left. */ - for(i = SA + BUCKET_BSTAR(c1, c1 + 1), - j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; - i <= j; - --j) { - if(0 < (s = *j)) { - assert(T[s] == c1); - assert(((s + 1) < n) && (T[s] <= T[s + 1])); - assert(T[s - 1] <= T[s]); - *j = ~s; - c0 = T[--s]; - if((0 < s) && (T[s - 1] > c0)) { s = ~s; } - if(c0 != c2) { - if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } - k = SA + BUCKET_B(c2 = c0, c1); - } - assert(k < j); - *k-- = s; - } else { - assert(((s == 0) && (T[s] == c1)) || (s < 0)); - *j = ~s; - } - } - } - } - - /* Construct the suffix array by using - the sorted order of type B suffixes. */ - k = SA + BUCKET_A(c2 = T[n - 1]); - *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); - /* Scan the suffix array from left to right. */ - for(i = SA, j = SA + n; i < j; ++i) { - if(0 < (s = *i)) { - assert(T[s - 1] >= T[s]); - c0 = T[--s]; - if((s == 0) || (T[s - 1] < c0)) { s = ~s; } - if(c0 != c2) { - BUCKET_A(c2) = k - SA; - k = SA + BUCKET_A(c2 = c0); - } - assert(i < k); - *k++ = s; - } else { - assert(s < 0); - *i = ~s; - } - } -} - -/* Constructs the burrows-wheeler transformed string directly - by using the sorted order of type B* suffixes. */ -static -saidx_t -construct_BWT(const sauchar_t *T, saidx_t *SA, - saidx_t *bucket_A, saidx_t *bucket_B, - saidx_t n, saidx_t m) { - saidx_t *i, *j, *k, *orig; - saidx_t s; - saint_t c0, c1, c2; - - if(0 < m) { - /* Construct the sorted order of type B suffixes by using - the sorted order of type B* suffixes. */ - for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { - /* Scan the suffix array from right to left. */ - for(i = SA + BUCKET_BSTAR(c1, c1 + 1), - j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; - i <= j; - --j) { - if(0 < (s = *j)) { - assert(T[s] == c1); - assert(((s + 1) < n) && (T[s] <= T[s + 1])); - assert(T[s - 1] <= T[s]); - c0 = T[--s]; - *j = ~((saidx_t)c0); - if((0 < s) && (T[s - 1] > c0)) { s = ~s; } - if(c0 != c2) { - if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } - k = SA + BUCKET_B(c2 = c0, c1); - } - assert(k < j); - *k-- = s; - } else if(s != 0) { - *j = ~s; -#ifndef NDEBUG - } else { - assert(T[s] == c1); -#endif - } - } - } - } - - /* Construct the BWTed string by using - the sorted order of type B suffixes. */ - k = SA + BUCKET_A(c2 = T[n - 1]); - *k++ = (T[n - 2] < c2) ? ~((saidx_t)T[n - 2]) : (n - 1); - /* Scan the suffix array from left to right. */ - for(i = SA, j = SA + n, orig = SA; i < j; ++i) { - if(0 < (s = *i)) { - assert(T[s - 1] >= T[s]); - c0 = T[--s]; - *i = c0; - if((0 < s) && (T[s - 1] < c0)) { s = ~((saidx_t)T[s - 1]); } - if(c0 != c2) { - BUCKET_A(c2) = k - SA; - k = SA + BUCKET_A(c2 = c0); - } - assert(i < k); - *k++ = s; - } else if(s != 0) { - *i = ~s; - } else { - orig = i; - } - } - - return orig - SA; -} - - -/*---------------------------------------------------------------------------*/ - -/*- Function -*/ - -saint_t -divsufsort(const sauchar_t *T, saidx_t *SA, saidx_t n) { - saidx_t *bucket_A, *bucket_B; - saidx_t m; - saint_t err = 0; - - /* Check arguments. */ - if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; } - else if(n == 0) { return 0; } - else if(n == 1) { SA[0] = 0; return 0; } - else if(n == 2) { m = (T[0] < T[1]); SA[m ^ 1] = 0, SA[m] = 1; return 0; } - - bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); - bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); - - /* Suffixsort. */ - if((bucket_A != NULL) && (bucket_B != NULL)) { - m = sort_typeBstar(T, SA, bucket_A, bucket_B, n); - construct_SA(T, SA, bucket_A, bucket_B, n, m); - } else { - err = -2; - } - - free(bucket_B); - free(bucket_A); - - return err; -} - -saidx_t -divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n) { - saidx_t *B; - saidx_t *bucket_A, *bucket_B; - saidx_t m, pidx, i; - - /* Check arguments. */ - if((T == NULL) || (U == NULL) || (n < 0)) { return -1; } - else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } - - if((B = A) == NULL) { B = (saidx_t *)malloc((size_t)(n + 1) * sizeof(saidx_t)); } - bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); - bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); - - /* Burrows-Wheeler Transform. */ - if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) { - m = sort_typeBstar(T, B, bucket_A, bucket_B, n); - pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m); - - /* Copy to output string. */ - U[0] = T[n - 1]; - for(i = 0; i < pidx; ++i) { U[i + 1] = (sauchar_t)B[i]; } - for(i += 1; i < n; ++i) { U[i] = (sauchar_t)B[i]; } - pidx += 1; - } else { - pidx = -2; - } - - free(bucket_B); - free(bucket_A); - if(A == NULL) { free(B); } - - return pidx; -} - -const char * -divsufsort_version(void) { - return PROJECT_VERSION_FULL; -} diff --git a/dictBuilder/divsufsort.h b/dictBuilder/divsufsort.h deleted file mode 100644 index 6d3e64870..000000000 --- a/dictBuilder/divsufsort.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * divsufsort.h for libdivsufsort - * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _DIVSUFSORT_H -#define _DIVSUFSORT_H 1 - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#include - -#ifndef DIVSUFSORT_API -# ifdef DIVSUFSORT_BUILD_DLL -# define DIVSUFSORT_API -# else -# define DIVSUFSORT_API -# endif -#endif - -/*- Datatypes -*/ -#ifndef SAUCHAR_T -#define SAUCHAR_T -typedef uint8_t sauchar_t; -#endif /* SAUCHAR_T */ -#ifndef SAINT_T -#define SAINT_T -typedef int32_t saint_t; -#endif /* SAINT_T */ -#ifndef SAIDX_T -#define SAIDX_T -typedef int32_t saidx_t; -#endif /* SAIDX_T */ -#ifndef PRIdSAINT_T -#define PRIdSAINT_T PRId32 -#endif /* PRIdSAINT_T */ -#ifndef PRIdSAIDX_T -#define PRIdSAIDX_T PRId32 -#endif /* PRIdSAIDX_T */ - - -/*- Prototypes -*/ - -/** - * Constructs the suffix array of a given string. - * @param T[0..n-1] The input string. - * @param SA[0..n-1] The output array of suffixes. - * @param n The length of the given string. - * @return 0 if no error occurred, -1 or -2 otherwise. - */ -DIVSUFSORT_API -saint_t -divsufsort(const sauchar_t *T, saidx_t *SA, saidx_t n); - -/** - * Constructs the burrows-wheeler transformed string of a given string. - * @param T[0..n-1] The input string. - * @param U[0..n-1] The output string. (can be T) - * @param A[0..n-1] The temporary array. (can be NULL) - * @param n The length of the given string. - * @return The primary index if no error occurred, -1 or -2 otherwise. - */ -DIVSUFSORT_API -saidx_t -divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n); - -/** - * Returns the version of the divsufsort library. - * @return The version number string. - */ -DIVSUFSORT_API -const char * -divsufsort_version(void); - - -/** - * Constructs the burrows-wheeler transformed string of a given string and suffix array. - * @param T[0..n-1] The input string. - * @param U[0..n-1] The output string. (can be T) - * @param SA[0..n-1] The suffix array. (can be NULL) - * @param n The length of the given string. - * @param idx The output primary index. - * @return 0 if no error occurred, -1 or -2 otherwise. - */ -DIVSUFSORT_API -saint_t -bw_transform(const sauchar_t *T, sauchar_t *U, - saidx_t *SA /* can NULL */, - saidx_t n, saidx_t *idx); - -/** - * Inverse BW-transforms a given BWTed string. - * @param T[0..n-1] The input string. - * @param U[0..n-1] The output string. (can be T) - * @param A[0..n-1] The temporary array. (can be NULL) - * @param n The length of the given string. - * @param idx The primary index. - * @return 0 if no error occurred, -1 or -2 otherwise. - */ -DIVSUFSORT_API -saint_t -inverse_bw_transform(const sauchar_t *T, sauchar_t *U, - saidx_t *A /* can NULL */, - saidx_t n, saidx_t idx); - -/** - * Checks the correctness of a given suffix array. - * @param T[0..n-1] The input string. - * @param SA[0..n-1] The input suffix array. - * @param n The length of the given string. - * @param verbose The verbose mode. - * @return 0 if no error occurred. - */ -DIVSUFSORT_API -saint_t -sufcheck(const sauchar_t *T, const saidx_t *SA, saidx_t n, saint_t verbose); - -/** - * Search for the pattern P in the string T. - * @param T[0..Tsize-1] The input string. - * @param Tsize The length of the given string. - * @param P[0..Psize-1] The input pattern string. - * @param Psize The length of the given pattern string. - * @param SA[0..SAsize-1] The input suffix array. - * @param SAsize The length of the given suffix array. - * @param idx The output index. - * @return The count of matches if no error occurred, -1 otherwise. - */ -DIVSUFSORT_API -saidx_t -sa_search(const sauchar_t *T, saidx_t Tsize, - const sauchar_t *P, saidx_t Psize, - const saidx_t *SA, saidx_t SAsize, - saidx_t *left); - -/** - * Search for the character c in the string T. - * @param T[0..Tsize-1] The input string. - * @param Tsize The length of the given string. - * @param SA[0..SAsize-1] The input suffix array. - * @param SAsize The length of the given suffix array. - * @param c The input character. - * @param idx The output index. - * @return The count of matches if no error occurred, -1 otherwise. - */ -DIVSUFSORT_API -saidx_t -sa_simplesearch(const sauchar_t *T, saidx_t Tsize, - const saidx_t *SA, saidx_t SAsize, - saint_t c, saidx_t *left); - - -#ifdef __cplusplus -} /* extern "C" */ -#endif /* __cplusplus */ - -#endif /* _DIVSUFSORT_H */ diff --git a/dictBuilder/divsufsort_private.h b/dictBuilder/divsufsort_private.h deleted file mode 100644 index 0a18f6d28..000000000 --- a/dictBuilder/divsufsort_private.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * divsufsort_private.h for libdivsufsort - * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _DIVSUFSORT_PRIVATE_H -#define _DIVSUFSORT_PRIVATE_H 1 - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* ************************* -* Includes -***************************/ -#include -#include /* unconditional */ -#include -#include "config.h" /* unconditional */ - - -#if HAVE_STRING_H -# include -#endif -#if HAVE_MEMORY_H -# include -#endif -#if HAVE_STDDEF_H -# include -#endif -#if HAVE_STRINGS_H -# ifdef _WIN32 -# include -# else -# include -# endif -#endif -#if HAVE_INTTYPES_H -# include -#else -# if HAVE_STDINT_H -# include -# endif -#endif -#if defined(BUILD_DIVSUFSORT64) -# include "divsufsort64.h" -# ifndef SAIDX_T -# define SAIDX_T -# define saidx_t saidx64_t -# endif /* SAIDX_T */ -# ifndef PRIdSAIDX_T -# define PRIdSAIDX_T PRIdSAIDX64_T -# endif /* PRIdSAIDX_T */ -# define divsufsort divsufsort64 -# define divbwt divbwt64 -# define divsufsort_version divsufsort64_version -# define bw_transform bw_transform64 -# define inverse_bw_transform inverse_bw_transform64 -# define sufcheck sufcheck64 -# define sa_search sa_search64 -# define sa_simplesearch sa_simplesearch64 -# define sssort sssort64 -# define trsort trsort64 -#else -# include "divsufsort.h" -#endif - - -/*- Constants -*/ -#if !defined(UINT8_MAX) -# define UINT8_MAX (255) -#endif /* UINT8_MAX */ -#if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1) -# undef ALPHABET_SIZE -#endif -#if !defined(ALPHABET_SIZE) -# define ALPHABET_SIZE (UINT8_MAX + 1) -#endif -/* for divsufsort.c */ -#define BUCKET_A_SIZE (ALPHABET_SIZE) -#define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) -/* for sssort.c */ -#if defined(SS_INSERTIONSORT_THRESHOLD) -# if SS_INSERTIONSORT_THRESHOLD < 1 -# undef SS_INSERTIONSORT_THRESHOLD -# define SS_INSERTIONSORT_THRESHOLD (1) -# endif -#else -# define SS_INSERTIONSORT_THRESHOLD (8) -#endif -#if defined(SS_BLOCKSIZE) -# if SS_BLOCKSIZE < 0 -# undef SS_BLOCKSIZE -# define SS_BLOCKSIZE (0) -# elif 32768 <= SS_BLOCKSIZE -# undef SS_BLOCKSIZE -# define SS_BLOCKSIZE (32767) -# endif -#else -# define SS_BLOCKSIZE (1024) -#endif -/* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */ -#if SS_BLOCKSIZE == 0 -# if defined(BUILD_DIVSUFSORT64) -# define SS_MISORT_STACKSIZE (96) -# else -# define SS_MISORT_STACKSIZE (64) -# endif -#elif SS_BLOCKSIZE <= 4096 -# define SS_MISORT_STACKSIZE (16) -#else -# define SS_MISORT_STACKSIZE (24) -#endif -#if defined(BUILD_DIVSUFSORT64) -# define SS_SMERGE_STACKSIZE (64) -#else -# define SS_SMERGE_STACKSIZE (32) -#endif -/* for trsort.c */ -#define TR_INSERTIONSORT_THRESHOLD (8) -#if defined(BUILD_DIVSUFSORT64) -# define TR_STACKSIZE (96) -#else -# define TR_STACKSIZE (64) -#endif - - -/*- Macros -*/ -#ifndef SWAP -# define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0) -#endif /* SWAP */ -#ifndef MIN -# define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) -#endif /* MIN */ -#ifndef MAX -# define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) -#endif /* MAX */ -#define STACK_PUSH(_a, _b, _c, _d)\ - do {\ - assert(ssize < STACK_SIZE);\ - stack[ssize].a = (_a), stack[ssize].b = (_b),\ - stack[ssize].c = (_c), stack[ssize++].d = (_d);\ - } while(0) -#define STACK_PUSH5(_a, _b, _c, _d, _e)\ - do {\ - assert(ssize < STACK_SIZE);\ - stack[ssize].a = (_a), stack[ssize].b = (_b),\ - stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\ - } while(0) -#define STACK_POP(_a, _b, _c, _d)\ - do {\ - assert(0 <= ssize);\ - if(ssize == 0) { return; }\ - (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ - (_c) = stack[ssize].c, (_d) = stack[ssize].d;\ - } while(0) -#define STACK_POP5(_a, _b, _c, _d, _e)\ - do {\ - assert(0 <= ssize);\ - if(ssize == 0) { return; }\ - (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ - (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\ - } while(0) -/* for divsufsort.c */ -#define BUCKET_A(_c0) bucket_A[(_c0)] -#if ALPHABET_SIZE == 256 -#define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) -#define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) -#else -#define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)]) -#define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)]) -#endif - - -/*- Private Prototypes -*/ -/* sssort.c */ -void -sssort(const sauchar_t *Td, const saidx_t *PA, - saidx_t *first, saidx_t *last, - saidx_t *buf, saidx_t bufsize, - saidx_t depth, saidx_t n, saint_t lastsuffix); -/* trsort.c */ -void -trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth); - - -#ifdef __cplusplus -} /* extern "C" */ -#endif /* __cplusplus */ - -#endif /* _DIVSUFSORT_PRIVATE_H */ diff --git a/dictBuilder/lfs.h b/dictBuilder/lfs.h deleted file mode 100644 index 7ef88f0b4..000000000 --- a/dictBuilder/lfs.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * lfs.h for libdivsufsort - * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _LFS_H -#define _LFS_H 1 - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef __STRICT_ANSI__ -# define LFS_OFF_T off_t -# define LFS_FOPEN fopen -# define LFS_FTELL ftello -# define LFS_FSEEK fseeko -# define LFS_PRId PRIdMAX -#else -# define LFS_OFF_T long -# define LFS_FOPEN fopen -# define LFS_FTELL ftell -# define LFS_FSEEK fseek -# define LFS_PRId "ld" -#endif -#ifndef PRIdOFF_T -# define PRIdOFF_T LFS_PRId -#endif - - -#ifdef __cplusplus -} /* extern "C" */ -#endif /* __cplusplus */ - -#endif /* _LFS_H */ diff --git a/dictBuilder/sssort.c b/dictBuilder/sssort.c deleted file mode 100644 index 07cb5bd1b..000000000 --- a/dictBuilder/sssort.c +++ /dev/null @@ -1,844 +0,0 @@ -/* - * sssort.c for libdivsufsort - * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/*- Compiler specifics -*/ -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wshorten-64-to-32" -#endif - -#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 */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# define FORCE_INLINE static __forceinline -#else -# if 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 -# endif /* __STDC_VERSION__ */ -#endif - -/*- Dependencies -*/ -#include "divsufsort_private.h" - - -/*- Private Functions -*/ - -static const saint_t lg_table[256]= { - -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 -}; - -#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) - -static INLINE -saint_t -ss_ilg(saidx_t n) { -#if SS_BLOCKSIZE == 0 -# if defined(BUILD_DIVSUFSORT64) - return (n >> 32) ? - ((n >> 48) ? - ((n >> 56) ? - 56 + lg_table[(n >> 56) & 0xff] : - 48 + lg_table[(n >> 48) & 0xff]) : - ((n >> 40) ? - 40 + lg_table[(n >> 40) & 0xff] : - 32 + lg_table[(n >> 32) & 0xff])) : - ((n & 0xffff0000) ? - ((n & 0xff000000) ? - 24 + lg_table[(n >> 24) & 0xff] : - 16 + lg_table[(n >> 16) & 0xff]) : - ((n & 0x0000ff00) ? - 8 + lg_table[(n >> 8) & 0xff] : - 0 + lg_table[(n >> 0) & 0xff])); -# else - return (n & 0xffff0000) ? - ((n & 0xff000000) ? - 24 + lg_table[(n >> 24) & 0xff] : - 16 + lg_table[(n >> 16) & 0xff]) : - ((n & 0x0000ff00) ? - 8 + lg_table[(n >> 8) & 0xff] : - 0 + lg_table[(n >> 0) & 0xff]); -# endif -#elif SS_BLOCKSIZE < 256 - return lg_table[n]; -#else - return (n & 0xff00) ? - 8 + lg_table[(n >> 8) & 0xff] : - 0 + lg_table[(n >> 0) & 0xff]; -#endif -} - -#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ - -#if SS_BLOCKSIZE != 0 - -static const saint_t sqq_table[256] = { - 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, - 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, - 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, -110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, -128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, -143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, -156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, -169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, -181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, -192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, -202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, -212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, -221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, -230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, -239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, -247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 -}; - -static INLINE -saidx_t -ss_isqrt(saidx_t x) { - saidx_t y, e; - - if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } - e = (x & 0xffff0000) ? - ((x & 0xff000000) ? - 24 + lg_table[(x >> 24) & 0xff] : - 16 + lg_table[(x >> 16) & 0xff]) : - ((x & 0x0000ff00) ? - 8 + lg_table[(x >> 8) & 0xff] : - 0 + lg_table[(x >> 0) & 0xff]); - - if(e >= 16) { - y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); - if(e >= 24) { y = (y + 1 + x / y) >> 1; } - y = (y + 1 + x / y) >> 1; - } else if(e >= 8) { - y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; - } else { - return sqq_table[x] >> 4; - } - - return (x < (y * y)) ? y - 1 : y; -} - -#endif /* SS_BLOCKSIZE != 0 */ - - -/*---------------------------------------------------------------------------*/ - -/* Compares two suffixes. */ -static INLINE -saint_t -ss_compare(const sauchar_t *T, - const saidx_t *p1, const saidx_t *p2, - saidx_t depth) { - const sauchar_t *U1, *U2, *U1n, *U2n; - - for(U1 = T + depth + *p1, - U2 = T + depth + *p2, - U1n = T + *(p1 + 1) + 2, - U2n = T + *(p2 + 1) + 2; - (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); - ++U1, ++U2) { - } - - return U1 < U1n ? - (U2 < U2n ? *U1 - *U2 : 1) : - (U2 < U2n ? -1 : 0); -} - - -/*---------------------------------------------------------------------------*/ - -#if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) - -/* Insertionsort for small size groups */ -static -void -ss_insertionsort(const sauchar_t *T, const saidx_t *PA, - saidx_t *first, saidx_t *last, saidx_t depth) { - saidx_t *i, *j; - saidx_t t; - saint_t r; - - for(i = last - 2; first <= i; --i) { - for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { - do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); - if(last <= j) { break; } - } - if(r == 0) { *j = ~*j; } - *(j - 1) = t; - } -} - -#endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */ - - -/*---------------------------------------------------------------------------*/ - -#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) - -static INLINE -void -ss_fixdown(const sauchar_t *Td, const saidx_t *PA, - saidx_t *SA, saidx_t i, saidx_t size) { - saidx_t j, k; - saidx_t v; - saint_t c, d, e; - - for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { - d = Td[PA[SA[k = j++]]]; - if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } - if(d <= c) { break; } - } - SA[i] = v; -} - -/* Simple top-down heapsort. */ -static -void -ss_heapsort(const sauchar_t *Td, const saidx_t *PA, saidx_t *SA, saidx_t size) { - saidx_t i, m; - saidx_t t; - - m = size; - if((size % 2) == 0) { - m--; - if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); } - } - - for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); } - if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } - for(i = m - 1; 0 < i; --i) { - t = SA[0], SA[0] = SA[i]; - ss_fixdown(Td, PA, SA, 0, i); - SA[i] = t; - } -} - - -/*---------------------------------------------------------------------------*/ - -/* Returns the median of three elements. */ -static INLINE -saidx_t * -ss_median3(const sauchar_t *Td, const saidx_t *PA, - saidx_t *v1, saidx_t *v2, saidx_t *v3) { - saidx_t *t; - if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); } - if(Td[PA[*v2]] > Td[PA[*v3]]) { - if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; } - else { return v3; } - } - return v2; -} - -/* Returns the median of five elements. */ -static INLINE -saidx_t * -ss_median5(const sauchar_t *Td, const saidx_t *PA, - saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { - saidx_t *t; - if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); } - if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); } - if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); } - if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); } - if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); } - if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; } - return v3; -} - -/* Returns the pivot element. */ -static INLINE -saidx_t * -ss_pivot(const sauchar_t *Td, const saidx_t *PA, saidx_t *first, saidx_t *last) { - saidx_t *middle; - saidx_t t; - - t = last - first; - middle = first + t / 2; - - if(t <= 512) { - if(t <= 32) { - return ss_median3(Td, PA, first, middle, last - 1); - } else { - t >>= 2; - return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); - } - } - t >>= 3; - first = ss_median3(Td, PA, first, first + t, first + (t << 1)); - middle = ss_median3(Td, PA, middle - t, middle, middle + t); - last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); - return ss_median3(Td, PA, first, middle, last); -} - - -/*---------------------------------------------------------------------------*/ - -/* Binary partition for substrings. */ -static INLINE -saidx_t * -ss_partition(const saidx_t *PA, - saidx_t *first, saidx_t *last, saidx_t depth) { - saidx_t *a, *b; - saidx_t t; - for(a = first - 1, b = last;;) { - for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; } - for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { } - if(b <= a) { break; } - t = ~*b; - *b = *a; - *a = t; - } - if(first < a) { *first = ~*first; } - return a; -} - -/* Multikey introsort for medium size groups. */ -static -void -ss_mintrosort(const sauchar_t *T, const saidx_t *PA, - saidx_t *first, saidx_t *last, - saidx_t depth) { -#define STACK_SIZE SS_MISORT_STACKSIZE - struct { saidx_t *a, *b, c; saint_t d; } stack[STACK_SIZE]; - const sauchar_t *Td; - saidx_t *a, *b, *c, *d, *e, *f; - saidx_t s, t; - saint_t ssize; - saint_t limit; - saint_t v, x = 0; - - for(ssize = 0, limit = ss_ilg(last - first);;) { - - if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { -#if 1 < SS_INSERTIONSORT_THRESHOLD - if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); } -#endif - STACK_POP(first, last, depth, limit); - continue; - } - - Td = T + depth; - if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); } - if(limit < 0) { - for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { - if((x = Td[PA[*a]]) != v) { - if(1 < (a - first)) { break; } - v = x; - first = a; - } - } - if(Td[PA[*first] - 1] < v) { - first = ss_partition(PA, first, a, depth); - } - if((a - first) <= (last - a)) { - if(1 < (a - first)) { - STACK_PUSH(a, last, depth, -1); - last = a, depth += 1, limit = ss_ilg(a - first); - } else { - first = a, limit = -1; - } - } else { - if(1 < (last - a)) { - STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); - first = a, limit = -1; - } else { - last = a, depth += 1, limit = ss_ilg(a - first); - } - } - continue; - } - - /* choose pivot */ - a = ss_pivot(Td, PA, first, last); - v = Td[PA[*a]]; - SWAP(*first, *a); - - /* partition */ - for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { } - if(((a = b) < last) && (x < v)) { - for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { - if(x == v) { SWAP(*b, *a); ++a; } - } - } - for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { } - if((b < (d = c)) && (x > v)) { - for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { - if(x == v) { SWAP(*c, *d); --d; } - } - } - for(; b < c;) { - SWAP(*b, *c); - for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { - if(x == v) { SWAP(*b, *a); ++a; } - } - for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { - if(x == v) { SWAP(*c, *d); --d; } - } - } - - if(a <= d) { - c = b - 1; - - if((s = a - first) > (t = b - a)) { s = t; } - for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } - if((s = d - c) > (t = last - d - 1)) { s = t; } - for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } - - a = first + (b - a), c = last - (d - c); - b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); - - if((a - first) <= (last - c)) { - if((last - c) <= (c - b)) { - STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); - STACK_PUSH(c, last, depth, limit); - last = a; - } else if((a - first) <= (c - b)) { - STACK_PUSH(c, last, depth, limit); - STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); - last = a; - } else { - STACK_PUSH(c, last, depth, limit); - STACK_PUSH(first, a, depth, limit); - first = b, last = c, depth += 1, limit = ss_ilg(c - b); - } - } else { - if((a - first) <= (c - b)) { - STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); - STACK_PUSH(first, a, depth, limit); - first = c; - } else if((last - c) <= (c - b)) { - STACK_PUSH(first, a, depth, limit); - STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); - first = c; - } else { - STACK_PUSH(first, a, depth, limit); - STACK_PUSH(c, last, depth, limit); - first = b, last = c, depth += 1, limit = ss_ilg(c - b); - } - } - } else { - limit += 1; - if(Td[PA[*first] - 1] < v) { - first = ss_partition(PA, first, last, depth); - limit = ss_ilg(last - first); - } - depth += 1; - } - } -#undef STACK_SIZE -} - -#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ - - -/*---------------------------------------------------------------------------*/ - -#if SS_BLOCKSIZE != 0 - -static INLINE -void -ss_blockswap(saidx_t *a, saidx_t *b, saidx_t n) { - saidx_t t; - for(; 0 < n; --n, ++a, ++b) { - t = *a, *a = *b, *b = t; - } -} - -static INLINE -void -ss_rotate(saidx_t *first, saidx_t *middle, saidx_t *last) { - saidx_t *a, *b, t; - saidx_t l, r; - l = middle - first, r = last - middle; - for(; (0 < l) && (0 < r);) { - if(l == r) { ss_blockswap(first, middle, l); break; } - if(l < r) { - a = last - 1, b = middle - 1; - t = *a; - do { - *a-- = *b, *b-- = *a; - if(b < first) { - *a = t; - last = a; - if((r -= l + 1) <= l) { break; } - a -= 1, b = middle - 1; - t = *a; - } - } while(1); - } else { - a = first, b = middle; - t = *a; - do { - *a++ = *b, *b++ = *a; - if(last <= b) { - *a = t; - first = a + 1; - if((l -= r + 1) <= r) { break; } - a += 1, b = middle; - t = *a; - } - } while(1); - } - } -} - - -/*---------------------------------------------------------------------------*/ - -static -void -ss_inplacemerge(const sauchar_t *T, const saidx_t *PA, - saidx_t *first, saidx_t *middle, saidx_t *last, - saidx_t depth) { - const saidx_t *p; - saidx_t *a, *b; - saidx_t len, half; - saint_t q, r; - saint_t x; - - for(;;) { - if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } - else { x = 0; p = PA + *(last - 1); } - for(a = first, len = middle - first, half = len >> 1, r = -1; - 0 < len; - len = half, half >>= 1) { - b = a + half; - q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); - if(q < 0) { - a = b + 1; - half -= (len & 1) ^ 1; - } else { - r = q; - } - } - if(a < middle) { - if(r == 0) { *a = ~*a; } - ss_rotate(a, middle, last); - last -= middle - a; - middle = a; - if(first == middle) { break; } - } - --last; - if(x != 0) { while(*--last < 0) { } } - if(middle == last) { break; } - } -} - - -/*---------------------------------------------------------------------------*/ - -/* Merge-forward with internal buffer. */ -static -void -ss_mergeforward(const sauchar_t *T, const saidx_t *PA, - saidx_t *first, saidx_t *middle, saidx_t *last, - saidx_t *buf, saidx_t depth) { - saidx_t *a, *b, *c, *bufend; - saidx_t t; - saint_t r; - - bufend = buf + (middle - first) - 1; - ss_blockswap(buf, first, middle - first); - - for(t = *(a = first), b = buf, c = middle;;) { - r = ss_compare(T, PA + *b, PA + *c, depth); - if(r < 0) { - do { - *a++ = *b; - if(bufend <= b) { *bufend = t; return; } - *b++ = *a; - } while(*b < 0); - } else if(r > 0) { - do { - *a++ = *c, *c++ = *a; - if(last <= c) { - while(b < bufend) { *a++ = *b, *b++ = *a; } - *a = *b, *b = t; - return; - } - } while(*c < 0); - } else { - *c = ~*c; - do { - *a++ = *b; - if(bufend <= b) { *bufend = t; return; } - *b++ = *a; - } while(*b < 0); - - do { - *a++ = *c, *c++ = *a; - if(last <= c) { - while(b < bufend) { *a++ = *b, *b++ = *a; } - *a = *b, *b = t; - return; - } - } while(*c < 0); - } - } -} - -/* Merge-backward with internal buffer. */ -static -void -ss_mergebackward(const sauchar_t *T, const saidx_t *PA, - saidx_t *first, saidx_t *middle, saidx_t *last, - saidx_t *buf, saidx_t depth) { - const saidx_t *p1, *p2; - saidx_t *a, *b, *c, *bufend; - saidx_t t; - saint_t r; - saint_t x; - - bufend = buf + (last - middle) - 1; - ss_blockswap(buf, middle, last - middle); - - x = 0; - if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } - else { p1 = PA + *bufend; } - if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } - else { p2 = PA + *(middle - 1); } - for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { - r = ss_compare(T, p1, p2, depth); - if(0 < r) { - if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } - *a-- = *b; - if(b <= buf) { *buf = t; break; } - *b-- = *a; - if(*b < 0) { p1 = PA + ~*b; x |= 1; } - else { p1 = PA + *b; } - } else if(r < 0) { - if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } - *a-- = *c, *c-- = *a; - if(c < first) { - while(buf < b) { *a-- = *b, *b-- = *a; } - *a = *b, *b = t; - break; - } - if(*c < 0) { p2 = PA + ~*c; x |= 2; } - else { p2 = PA + *c; } - } else { - if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } - *a-- = ~*b; - if(b <= buf) { *buf = t; break; } - *b-- = *a; - if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } - *a-- = *c, *c-- = *a; - if(c < first) { - while(buf < b) { *a-- = *b, *b-- = *a; } - *a = *b, *b = t; - break; - } - if(*b < 0) { p1 = PA + ~*b; x |= 1; } - else { p1 = PA + *b; } - if(*c < 0) { p2 = PA + ~*c; x |= 2; } - else { p2 = PA + *c; } - } - } -} - -/* D&C based merge. */ -static -void -ss_swapmerge(const sauchar_t *T, const saidx_t *PA, - saidx_t *first, saidx_t *middle, saidx_t *last, - saidx_t *buf, saidx_t bufsize, saidx_t depth) { -#define STACK_SIZE SS_SMERGE_STACKSIZE -#define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) -#define MERGE_CHECK(a, b, c)\ - do {\ - if(((c) & 1) ||\ - (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\ - *(a) = ~*(a);\ - }\ - if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\ - *(b) = ~*(b);\ - }\ - } while(0) - struct { saidx_t *a, *b, *c; saint_t d; } stack[STACK_SIZE]; - saidx_t *l, *r, *lm, *rm; - saidx_t m, len, half; - saint_t ssize; - saint_t check, next; - - for(check = 0, ssize = 0;;) { - if((last - middle) <= bufsize) { - if((first < middle) && (middle < last)) { - ss_mergebackward(T, PA, first, middle, last, buf, depth); - } - MERGE_CHECK(first, last, check); - STACK_POP(first, middle, last, check); - continue; - } - - if((middle - first) <= bufsize) { - if(first < middle) { - ss_mergeforward(T, PA, first, middle, last, buf, depth); - } - MERGE_CHECK(first, last, check); - STACK_POP(first, middle, last, check); - continue; - } - - for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1; - 0 < len; - len = half, half >>= 1) { - if(ss_compare(T, PA + GETIDX(*(middle + m + half)), - PA + GETIDX(*(middle - m - half - 1)), depth) < 0) { - m += half + 1; - half -= (len & 1) ^ 1; - } - } - - if(0 < m) { - lm = middle - m, rm = middle + m; - ss_blockswap(lm, middle, m); - l = r = middle, next = 0; - if(rm < last) { - if(*rm < 0) { - *rm = ~*rm; - if(first < lm) { for(; *--l < 0;) { } next |= 4; } - next |= 1; - } else if(first < lm) { - for(; *r < 0; ++r) { } - next |= 2; - } - } - - if((l - first) <= (last - r)) { - STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); - middle = lm, last = l, check = (check & 3) | (next & 4); - } else { - if((next & 2) && (r == middle)) { next ^= 6; } - STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); - first = r, middle = rm, check = (next & 3) | (check & 4); - } - } else { - if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { - *middle = ~*middle; - } - MERGE_CHECK(first, last, check); - STACK_POP(first, middle, last, check); - } - } -#undef STACK_SIZE -} - -#endif /* SS_BLOCKSIZE != 0 */ - - -/*---------------------------------------------------------------------------*/ - -/*- Function -*/ - -/* Substring sort */ -void -sssort(const sauchar_t *T, const saidx_t *PA, - saidx_t *first, saidx_t *last, - saidx_t *buf, saidx_t bufsize, - saidx_t depth, saidx_t n, saint_t lastsuffix) { - saidx_t *a; -#if SS_BLOCKSIZE != 0 - saidx_t *b, *middle, *curbuf; - saidx_t j, k, curbufsize, limit; -#endif - saidx_t i; - - if(lastsuffix != 0) { ++first; } - -#if SS_BLOCKSIZE == 0 - ss_mintrosort(T, PA, first, last, depth); -#else - if((bufsize < SS_BLOCKSIZE) && - (bufsize < (last - first)) && - (bufsize < (limit = ss_isqrt(last - first)))) { - if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; } - buf = middle = last - limit, bufsize = limit; - } else { - middle = last, limit = 0; - } - for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { -#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE - ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); -#elif 1 < SS_BLOCKSIZE - ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); -#endif - curbufsize = last - (a + SS_BLOCKSIZE); - curbuf = a + SS_BLOCKSIZE; - if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; } - for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { - ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); - } - } -#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE - ss_mintrosort(T, PA, a, middle, depth); -#elif 1 < SS_BLOCKSIZE - ss_insertionsort(T, PA, a, middle, depth); -#endif - for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { - if(i & 1) { - ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); - a -= k; - } - } - if(limit != 0) { -#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE - ss_mintrosort(T, PA, middle, last, depth); -#elif 1 < SS_BLOCKSIZE - ss_insertionsort(T, PA, middle, last, depth); -#endif - ss_inplacemerge(T, PA, first, middle, last, depth); - } -#endif - - if(lastsuffix != 0) { - /* Insert last type B* suffix. */ - saidx_t PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2; - for(a = first, i = *(first - 1); - (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); - ++a) { - *(a - 1) = *a; - } - *(a - 1) = i; - } -} diff --git a/dictBuilder/trsort.c b/dictBuilder/trsort.c deleted file mode 100644 index 9e21e68ec..000000000 --- a/dictBuilder/trsort.c +++ /dev/null @@ -1,615 +0,0 @@ -/* - * trsort.c for libdivsufsort - * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/*- Compiler specifics -*/ -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wshorten-64-to-32" -#endif - -#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 */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ -# define FORCE_INLINE static __forceinline -#else -# if 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 -# endif /* __STDC_VERSION__ */ -#endif - -/*- Dependencies -*/ -#include "divsufsort_private.h" - - -/*- Private Functions -*/ - -static const saint_t lg_table[256]= { - -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 -}; - -static INLINE -saint_t -tr_ilg(saidx_t n) { -#if defined(BUILD_DIVSUFSORT64) - return (n >> 32) ? - ((n >> 48) ? - ((n >> 56) ? - 56 + lg_table[(n >> 56) & 0xff] : - 48 + lg_table[(n >> 48) & 0xff]) : - ((n >> 40) ? - 40 + lg_table[(n >> 40) & 0xff] : - 32 + lg_table[(n >> 32) & 0xff])) : - ((n & 0xffff0000) ? - ((n & 0xff000000) ? - 24 + lg_table[(n >> 24) & 0xff] : - 16 + lg_table[(n >> 16) & 0xff]) : - ((n & 0x0000ff00) ? - 8 + lg_table[(n >> 8) & 0xff] : - 0 + lg_table[(n >> 0) & 0xff])); -#else - return (n & 0xffff0000) ? - ((n & 0xff000000) ? - 24 + lg_table[(n >> 24) & 0xff] : - 16 + lg_table[(n >> 16) & 0xff]) : - ((n & 0x0000ff00) ? - 8 + lg_table[(n >> 8) & 0xff] : - 0 + lg_table[(n >> 0) & 0xff]); -#endif -} - - -/*---------------------------------------------------------------------------*/ - -/* Simple insertionsort for small size groups. */ -static -void -tr_insertionsort(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { - saidx_t *a, *b; - saidx_t t, r; - - for(a = first + 1; a < last; ++a) { - for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { - do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); - if(b < first) { break; } - } - if(r == 0) { *b = ~*b; } - *(b + 1) = t; - } -} - - -/*---------------------------------------------------------------------------*/ - -static INLINE -void -tr_fixdown(const saidx_t *ISAd, saidx_t *SA, saidx_t i, saidx_t size) { - saidx_t j, k; - saidx_t v; - saidx_t c, d, e; - - for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { - d = ISAd[SA[k = j++]]; - if(d < (e = ISAd[SA[j]])) { k = j; d = e; } - if(d <= c) { break; } - } - SA[i] = v; -} - -/* Simple top-down heapsort. */ -static -void -tr_heapsort(const saidx_t *ISAd, saidx_t *SA, saidx_t size) { - saidx_t i, m; - saidx_t t; - - m = size; - if((size % 2) == 0) { - m--; - if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); } - } - - for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } - if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } - for(i = m - 1; 0 < i; --i) { - t = SA[0], SA[0] = SA[i]; - tr_fixdown(ISAd, SA, 0, i); - SA[i] = t; - } -} - - -/*---------------------------------------------------------------------------*/ - -/* Returns the median of three elements. */ -static INLINE -saidx_t * -tr_median3(const saidx_t *ISAd, saidx_t *v1, saidx_t *v2, saidx_t *v3) { - saidx_t *t; - if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); } - if(ISAd[*v2] > ISAd[*v3]) { - if(ISAd[*v1] > ISAd[*v3]) { return v1; } - else { return v3; } - } - return v2; -} - -/* Returns the median of five elements. */ -static INLINE -saidx_t * -tr_median5(const saidx_t *ISAd, - saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { - saidx_t *t; - if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); } - if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); } - if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); } - if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); } - if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); } - if(ISAd[*v3] > ISAd[*v4]) { return v4; } - return v3; -} - -/* Returns the pivot element. */ -static INLINE -saidx_t * -tr_pivot(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { - saidx_t *middle; - saidx_t t; - - t = last - first; - middle = first + t / 2; - - if(t <= 512) { - if(t <= 32) { - return tr_median3(ISAd, first, middle, last - 1); - } else { - t >>= 2; - return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); - } - } - t >>= 3; - first = tr_median3(ISAd, first, first + t, first + (t << 1)); - middle = tr_median3(ISAd, middle - t, middle, middle + t); - last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); - return tr_median3(ISAd, first, middle, last); -} - - -/*---------------------------------------------------------------------------*/ - -typedef struct _trbudget_t trbudget_t; -struct _trbudget_t { - saidx_t chance; - saidx_t remain; - saidx_t incval; - saidx_t count; -}; - -static INLINE -void -trbudget_init(trbudget_t *budget, saidx_t chance, saidx_t incval) { - budget->chance = chance; - budget->remain = budget->incval = incval; -} - -static INLINE -saint_t -trbudget_check(trbudget_t *budget, saidx_t size) { - if(size <= budget->remain) { budget->remain -= size; return 1; } - if(budget->chance == 0) { budget->count += size; return 0; } - budget->remain += budget->incval - size; - budget->chance -= 1; - return 1; -} - - -/*---------------------------------------------------------------------------*/ - -static INLINE -void -tr_partition(const saidx_t *ISAd, - saidx_t *first, saidx_t *middle, saidx_t *last, - saidx_t **pa, saidx_t **pb, saidx_t v) { - saidx_t *a, *b, *c, *d, *e, *f; - saidx_t t, s; - saidx_t x = 0; - - for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { } - if(((a = b) < last) && (x < v)) { - for(; (++b < last) && ((x = ISAd[*b]) <= v);) { - if(x == v) { SWAP(*b, *a); ++a; } - } - } - for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { } - if((b < (d = c)) && (x > v)) { - for(; (b < --c) && ((x = ISAd[*c]) >= v);) { - if(x == v) { SWAP(*c, *d); --d; } - } - } - for(; b < c;) { - SWAP(*b, *c); - for(; (++b < c) && ((x = ISAd[*b]) <= v);) { - if(x == v) { SWAP(*b, *a); ++a; } - } - for(; (b < --c) && ((x = ISAd[*c]) >= v);) { - if(x == v) { SWAP(*c, *d); --d; } - } - } - - if(a <= d) { - c = b - 1; - if((s = a - first) > (t = b - a)) { s = t; } - for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } - if((s = d - c) > (t = last - d - 1)) { s = t; } - for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } - first += (b - a), last -= (d - c); - } - *pa = first, *pb = last; -} - -static -void -tr_copy(saidx_t *ISA, const saidx_t *SA, - saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, - saidx_t depth) { - /* sort suffixes of middle partition - by using sorted order of suffixes of left and right partition. */ - saidx_t *c, *d, *e; - saidx_t s, v; - - v = b - SA - 1; - for(c = first, d = a - 1; c <= d; ++c) { - if((0 <= (s = *c - depth)) && (ISA[s] == v)) { - *++d = s; - ISA[s] = d - SA; - } - } - for(c = last - 1, e = d + 1, d = b; e < d; --c) { - if((0 <= (s = *c - depth)) && (ISA[s] == v)) { - *--d = s; - ISA[s] = d - SA; - } - } -} - -static -void -tr_partialcopy(saidx_t *ISA, const saidx_t *SA, - saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, - saidx_t depth) { - saidx_t *c, *d, *e; - saidx_t s, v; - saidx_t rank, lastrank, newrank = -1; - - v = b - SA - 1; - lastrank = -1; - for(c = first, d = a - 1; c <= d; ++c) { - if((0 <= (s = *c - depth)) && (ISA[s] == v)) { - *++d = s; - rank = ISA[s + depth]; - if(lastrank != rank) { lastrank = rank; newrank = d - SA; } - ISA[s] = newrank; - } - } - - lastrank = -1; - for(e = d; first <= e; --e) { - rank = ISA[*e]; - if(lastrank != rank) { lastrank = rank; newrank = e - SA; } - if(newrank != rank) { ISA[*e] = newrank; } - } - - lastrank = -1; - for(c = last - 1, e = d + 1, d = b; e < d; --c) { - if((0 <= (s = *c - depth)) && (ISA[s] == v)) { - *--d = s; - rank = ISA[s + depth]; - if(lastrank != rank) { lastrank = rank; newrank = d - SA; } - ISA[s] = newrank; - } - } -} - -static -void -tr_introsort(saidx_t *ISA, const saidx_t *ISAd, - saidx_t *SA, saidx_t *first, saidx_t *last, - trbudget_t *budget) { -#define STACK_SIZE TR_STACKSIZE - struct { const saidx_t *a; saidx_t *b, *c; saint_t d, e; }stack[STACK_SIZE]; - saidx_t *a, *b, *c; - saidx_t t; - saidx_t v, x = 0; - saidx_t incr = ISAd - ISA; - saint_t limit, next; - saint_t ssize, trlink = -1; - - for(ssize = 0, limit = tr_ilg(last - first);;) { - - if(limit < 0) { - if(limit == -1) { - /* tandem repeat partition */ - tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); - - /* update ranks */ - if(a < last) { - for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } - } - if(b < last) { - for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } - } - - /* push */ - if(1 < (b - a)) { - STACK_PUSH5(NULL, a, b, 0, 0); - STACK_PUSH5(ISAd - incr, first, last, -2, trlink); - trlink = ssize - 2; - } - if((a - first) <= (last - b)) { - if(1 < (a - first)) { - STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); - last = a, limit = tr_ilg(a - first); - } else if(1 < (last - b)) { - first = b, limit = tr_ilg(last - b); - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } else { - if(1 < (last - b)) { - STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); - first = b, limit = tr_ilg(last - b); - } else if(1 < (a - first)) { - last = a, limit = tr_ilg(a - first); - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } - } else if(limit == -2) { - /* tandem repeat copy */ - a = stack[--ssize].b, b = stack[ssize].c; - if(stack[ssize].d == 0) { - tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); - } else { - if(0 <= trlink) { stack[trlink].d = -1; } - tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); - } - STACK_POP5(ISAd, first, last, limit, trlink); - } else { - /* sorted partition */ - if(0 <= *first) { - a = first; - do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); - first = a; - } - if(first < last) { - a = first; do { *a = ~*a; } while(*++a < 0); - next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; - if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } - - /* push */ - if(trbudget_check(budget, a - first)) { - if((a - first) <= (last - a)) { - STACK_PUSH5(ISAd, a, last, -3, trlink); - ISAd += incr, last = a, limit = next; - } else { - if(1 < (last - a)) { - STACK_PUSH5(ISAd + incr, first, a, next, trlink); - first = a, limit = -3; - } else { - ISAd += incr, last = a, limit = next; - } - } - } else { - if(0 <= trlink) { stack[trlink].d = -1; } - if(1 < (last - a)) { - first = a, limit = -3; - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } - continue; - } - - if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { - tr_insertionsort(ISAd, first, last); - limit = -3; - continue; - } - - if(limit-- == 0) { - tr_heapsort(ISAd, first, last - first); - for(a = last - 1; first < a; a = b) { - for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } - } - limit = -3; - continue; - } - - /* choose pivot */ - a = tr_pivot(ISAd, first, last); - SWAP(*first, *a); - v = ISAd[*first]; - - /* partition */ - tr_partition(ISAd, first, first + 1, last, &a, &b, v); - if((last - first) != (b - a)) { - next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; - - /* update ranks */ - for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } - if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } - - /* push */ - if((1 < (b - a)) && (trbudget_check(budget, b - a))) { - if((a - first) <= (last - b)) { - if((last - b) <= (b - a)) { - if(1 < (a - first)) { - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - STACK_PUSH5(ISAd, b, last, limit, trlink); - last = a; - } else if(1 < (last - b)) { - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - first = b; - } else { - ISAd += incr, first = a, last = b, limit = next; - } - } else if((a - first) <= (b - a)) { - if(1 < (a - first)) { - STACK_PUSH5(ISAd, b, last, limit, trlink); - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - last = a; - } else { - STACK_PUSH5(ISAd, b, last, limit, trlink); - ISAd += incr, first = a, last = b, limit = next; - } - } else { - STACK_PUSH5(ISAd, b, last, limit, trlink); - STACK_PUSH5(ISAd, first, a, limit, trlink); - ISAd += incr, first = a, last = b, limit = next; - } - } else { - if((a - first) <= (b - a)) { - if(1 < (last - b)) { - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - STACK_PUSH5(ISAd, first, a, limit, trlink); - first = b; - } else if(1 < (a - first)) { - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - last = a; - } else { - ISAd += incr, first = a, last = b, limit = next; - } - } else if((last - b) <= (b - a)) { - if(1 < (last - b)) { - STACK_PUSH5(ISAd, first, a, limit, trlink); - STACK_PUSH5(ISAd + incr, a, b, next, trlink); - first = b; - } else { - STACK_PUSH5(ISAd, first, a, limit, trlink); - ISAd += incr, first = a, last = b, limit = next; - } - } else { - STACK_PUSH5(ISAd, first, a, limit, trlink); - STACK_PUSH5(ISAd, b, last, limit, trlink); - ISAd += incr, first = a, last = b, limit = next; - } - } - } else { - if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } - if((a - first) <= (last - b)) { - if(1 < (a - first)) { - STACK_PUSH5(ISAd, b, last, limit, trlink); - last = a; - } else if(1 < (last - b)) { - first = b; - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } else { - if(1 < (last - b)) { - STACK_PUSH5(ISAd, first, a, limit, trlink); - first = b; - } else if(1 < (a - first)) { - last = a; - } else { - STACK_POP5(ISAd, first, last, limit, trlink); - } - } - } - } else { - if(trbudget_check(budget, last - first)) { - limit = tr_ilg(last - first), ISAd += incr; - } else { - if(0 <= trlink) { stack[trlink].d = -1; } - STACK_POP5(ISAd, first, last, limit, trlink); - } - } - } -#undef STACK_SIZE -} - - - -/*---------------------------------------------------------------------------*/ - -/*- Function -*/ - -/* Tandem repeat sort */ -void -trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth) { - saidx_t *ISAd; - saidx_t *first, *last; - trbudget_t budget; - saidx_t t, skip, unsorted; - - trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); -/* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */ - for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { - first = SA; - skip = 0; - unsorted = 0; - do { - if((t = *first) < 0) { first -= t; skip += t; } - else { - if(skip != 0) { *(first + skip) = skip; skip = 0; } - last = SA + ISA[t] + 1; - if(1 < (last - first)) { - budget.count = 0; - tr_introsort(ISA, ISAd, SA, first, last, &budget); - if(budget.count != 0) { unsorted += budget.count; } - else { skip = first - last; } - } else if((last - first) == 1) { - skip = -1; - } - first = last; - } - } while(first < (SA + n)); - if(skip != 0) { *(first + skip) = skip; } - if(unsorted == 0) { break; } - } -} diff --git a/dictBuilder/utils.c b/dictBuilder/utils.c deleted file mode 100644 index 90fb23efa..000000000 --- a/dictBuilder/utils.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * utils.c for libdivsufsort - * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "divsufsort_private.h" - - -/*- Private Function -*/ - -/* Binary search for inverse bwt. */ -static -saidx_t -binarysearch_lower(const saidx_t *A, saidx_t size, saidx_t value) { - saidx_t half, i; - for(i = 0, half = size >> 1; - 0 < size; - size = half, half >>= 1) { - if(A[i + half] < value) { - i += half + 1; - half -= (size & 1) ^ 1; - } - } - return i; -} - - -/*- Functions -*/ - -/* Burrows-Wheeler transform. */ -saint_t -bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *SA, - saidx_t n, saidx_t *idx) { - saidx_t *A, i, j, p, t; - saint_t c; - - /* Check arguments. */ - if((T == NULL) || (U == NULL) || (n < 0) || (idx == NULL)) { return -1; } - if(n <= 1) { - if(n == 1) { U[0] = T[0]; } - *idx = n; - return 0; - } - - if((A = SA) == NULL) { - i = divbwt(T, U, NULL, n); - if(0 <= i) { *idx = i; i = 0; } - return (saint_t)i; - } - - /* BW transform. */ - if(T == U) { - t = n; - for(i = 0, j = 0; i < n; ++i) { - p = t - 1; - t = A[i]; - if(0 <= p) { - c = T[j]; - U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; - A[j] = c; - j++; - } else { - *idx = i; - } - } - p = t - 1; - if(0 <= p) { - c = T[j]; - U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; - A[j] = c; - } else { - *idx = i; - } - } else { - U[0] = T[n - 1]; - for(i = 0; A[i] != 0; ++i) { U[i + 1] = T[A[i] - 1]; } - *idx = i + 1; - for(++i; i < n; ++i) { U[i] = T[A[i] - 1]; } - } - - if(SA == NULL) { - /* Deallocate memory. */ - free(A); - } - - return 0; -} - -/* Inverse Burrows-Wheeler transform. */ -saint_t -inverse_bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *A, - saidx_t n, saidx_t idx) { - saidx_t C[ALPHABET_SIZE]; - sauchar_t D[ALPHABET_SIZE]; - saidx_t *B; - saidx_t i, p; - saint_t c, d; - - /* Check arguments. */ - if((T == NULL) || (U == NULL) || (n < 0) || (idx < 0) || - (n < idx) || ((0 < n) && (idx == 0))) { - return -1; - } - if(n <= 1) { return 0; } - - if((B = A) == NULL) { - /* Allocate n*sizeof(saidx_t) bytes of memory. */ - if((B = (saidx_t *)malloc((size_t)n * sizeof(saidx_t))) == NULL) { return -2; } - } - - /* Inverse BW transform. */ - for(c = 0; c < ALPHABET_SIZE; ++c) { C[c] = 0; } - for(i = 0; i < n; ++i) { ++C[T[i]]; } - for(c = 0, d = 0, i = 0; c < ALPHABET_SIZE; ++c) { - p = C[c]; - if(0 < p) { - C[c] = i; - D[d++] = (sauchar_t)c; - i += p; - } - } - for(i = 0; i < idx; ++i) { B[C[T[i]]++] = i; } - for( ; i < n; ++i) { B[C[T[i]]++] = i + 1; } - for(c = 0; c < d; ++c) { C[c] = C[D[c]]; } - for(i = 0, p = idx; i < n; ++i) { - U[i] = D[binarysearch_lower(C, d, p)]; - p = B[p - 1]; - } - - if(A == NULL) { - /* Deallocate memory. */ - free(B); - } - - return 0; -} - -/* Checks the suffix array SA of the string T. */ -saint_t -sufcheck(const sauchar_t *T, const saidx_t *SA, - saidx_t n, saint_t verbose) { - saidx_t C[ALPHABET_SIZE]; - saidx_t i, p, q, t; - saint_t c; - - if(verbose) { fprintf(stderr, "sufcheck: "); } - - /* Check arguments. */ - if((T == NULL) || (SA == NULL) || (n < 0)) { - if(verbose) { fprintf(stderr, "Invalid arguments.\n"); } - return -1; - } - if(n == 0) { - if(verbose) { fprintf(stderr, "Done.\n"); } - return 0; - } - - /* check range: [0..n-1] */ - for(i = 0; i < n; ++i) { - if((SA[i] < 0) || (n <= SA[i])) { - if(verbose) { - fprintf(stderr, "Out of the range [0,%" PRIdSAIDX_T "].\n" - " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", - n - 1, i, SA[i]); - } - return -2; - } - } - - /* check first characters. */ - for(i = 1; i < n; ++i) { - if(T[SA[i - 1]] > T[SA[i]]) { - if(verbose) { - fprintf(stderr, "Suffixes in wrong order.\n" - " T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d" - " > T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d\n", - i - 1, SA[i - 1], T[SA[i - 1]], i, SA[i], T[SA[i]]); - } - return -3; - } - } - - /* check suffixes. */ - for(i = 0; i < ALPHABET_SIZE; ++i) { C[i] = 0; } - for(i = 0; i < n; ++i) { ++C[T[i]]; } - for(i = 0, p = 0; i < ALPHABET_SIZE; ++i) { - t = C[i]; - C[i] = p; - p += t; - } - - q = C[T[n - 1]]; - C[T[n - 1]] += 1; - for(i = 0; i < n; ++i) { - p = SA[i]; - if(0 < p) { - c = T[--p]; - t = C[c]; - } else { - c = T[p = n - 1]; - t = q; - } - if((t < 0) || (p != SA[t])) { - if(verbose) { - fprintf(stderr, "Suffix in wrong position.\n" - " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T " or\n" - " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", - t, (0 <= t) ? SA[t] : -1, i, SA[i]); - } - return -4; - } - if(t != q) { - ++C[c]; - if((n <= C[c]) || (T[SA[C[c]]] != c)) { C[c] = -1; } - } - } - - if(1 <= verbose) { fprintf(stderr, "Done.\n"); } - return 0; -} - - -static -int -_compare(const sauchar_t *T, saidx_t Tsize, - const sauchar_t *P, saidx_t Psize, - saidx_t suf, saidx_t *match) { - saidx_t i, j; - saint_t r; - for(i = suf + *match, j = *match, r = 0; - (i < Tsize) && (j < Psize) && ((r = T[i] - P[j]) == 0); ++i, ++j) { } - *match = j; - return (r == 0) ? -(j != Psize) : r; -} - -/* Search for the pattern P in the string T. */ -saidx_t -sa_search(const sauchar_t *T, saidx_t Tsize, - const sauchar_t *P, saidx_t Psize, - const saidx_t *SA, saidx_t SAsize, - saidx_t *idx) { - saidx_t size, lsize, rsize, half; - saidx_t match, lmatch, rmatch; - saidx_t llmatch, lrmatch, rlmatch, rrmatch; - saidx_t i, j, k; - saint_t r; - - if(idx != NULL) { *idx = -1; } - if((T == NULL) || (P == NULL) || (SA == NULL) || - (Tsize < 0) || (Psize < 0) || (SAsize < 0)) { return -1; } - if((Tsize == 0) || (SAsize == 0)) { return 0; } - if(Psize == 0) { if(idx != NULL) { *idx = 0; } return SAsize; } - - for(i = j = k = 0, lmatch = rmatch = 0, size = SAsize, half = size >> 1; - 0 < size; - size = half, half >>= 1) { - match = MIN(lmatch, rmatch); - r = _compare(T, Tsize, P, Psize, SA[i + half], &match); - if(r < 0) { - i += half + 1; - half -= (size & 1) ^ 1; - lmatch = match; - } else if(r > 0) { - rmatch = match; - } else { - lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; - - /* left part */ - for(llmatch = lmatch, lrmatch = match, half = lsize >> 1; - 0 < lsize; - lsize = half, half >>= 1) { - lmatch = MIN(llmatch, lrmatch); - r = _compare(T, Tsize, P, Psize, SA[j + half], &lmatch); - if(r < 0) { - j += half + 1; - half -= (lsize & 1) ^ 1; - llmatch = lmatch; - } else { - lrmatch = lmatch; - } - } - - /* right part */ - for(rlmatch = match, rrmatch = rmatch, half = rsize >> 1; - 0 < rsize; - rsize = half, half >>= 1) { - rmatch = MIN(rlmatch, rrmatch); - r = _compare(T, Tsize, P, Psize, SA[k + half], &rmatch); - if(r <= 0) { - k += half + 1; - half -= (rsize & 1) ^ 1; - rlmatch = rmatch; - } else { - rrmatch = rmatch; - } - } - - break; - } - } - - if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } - return k - j; -} - -/* Search for the character c in the string T. */ -saidx_t -sa_simplesearch(const sauchar_t *T, saidx_t Tsize, - const saidx_t *SA, saidx_t SAsize, - saint_t c, saidx_t *idx) { - saidx_t size, lsize, rsize, half; - saidx_t i, j, k, p; - saint_t r; - - if(idx != NULL) { *idx = -1; } - if((T == NULL) || (SA == NULL) || (Tsize < 0) || (SAsize < 0)) { return -1; } - if((Tsize == 0) || (SAsize == 0)) { return 0; } - - for(i = j = k = 0, size = SAsize, half = size >> 1; - 0 < size; - size = half, half >>= 1) { - p = SA[i + half]; - r = (p < Tsize) ? T[p] - c : -1; - if(r < 0) { - i += half + 1; - half -= (size & 1) ^ 1; - } else if(r == 0) { - lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; - - /* left part */ - for(half = lsize >> 1; - 0 < lsize; - lsize = half, half >>= 1) { - p = SA[j + half]; - r = (p < Tsize) ? T[p] - c : -1; - if(r < 0) { - j += half + 1; - half -= (lsize & 1) ^ 1; - } - } - - /* right part */ - for(half = rsize >> 1; - 0 < rsize; - rsize = half, half >>= 1) { - p = SA[k + half]; - r = (p < Tsize) ? T[p] - c : -1; - if(r <= 0) { - k += half + 1; - half -= (rsize & 1) ^ 1; - } - } - - break; - } - } - - if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } - return k - j; -} diff --git a/lib/Makefile b/lib/Makefile index e0ca0c626..5ce8c18b7 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,6 +1,6 @@ # ################################################################ # ZSTD library - Makefile -# Copyright (C) Yann Collet 2015 +# Copyright (C) Yann Collet 2015-2016 # All rights reserved. # # BSD license @@ -28,7 +28,6 @@ # # You can contact the author at : # - ZSTD homepage : http://www.zstd.net -# - ZSTD source repository : https://github.com/Cyan4973/zstd # ################################################################ # Version numbers @@ -52,7 +51,7 @@ FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(MOREFLAGS) LIBDIR ?= $(PREFIX)/lib INCLUDEDIR=$(PREFIX)/include -ZSTD_FILES := zstd_compress.c zstd_decompress.c fse.c huff0.c +ZSTD_FILES := zstd_compress.c zstd_decompress.c fse.c huff0.c dictBuilder.c divsufsort.c ZSTD_LEGACY:= legacy/zstd_v01.c legacy/zstd_v02.c legacy/zstd_v03.c legacy/zstd_v04.c ifeq ($(ZSTD_LEGACY_SUPPORT), 0) @@ -119,6 +118,7 @@ install: libzstd libzstd.pc @cp -a libzstd.pc $(DESTDIR)$(LIBDIR)/pkgconfig/ @install -m 644 libzstd.a $(DESTDIR)$(LIBDIR)/libzstd.a @install -m 644 zstd.h $(DESTDIR)$(INCLUDEDIR)/zstd.h + @install -m 644 zstd.h $(DESTDIR)$(INCLUDEDIR)/zstd_buffered.h @echo zstd static and shared library installed uninstall: diff --git a/dictBuilder/dictBuilder.c b/lib/dictBuilder.c similarity index 72% rename from dictBuilder/dictBuilder.c rename to lib/dictBuilder.c index d8b2bdb30..ede3b3d2a 100644 --- a/dictBuilder/dictBuilder.c +++ b/lib/dictBuilder.c @@ -1,28 +1,37 @@ /* - dictBuilder - dictionary builder for LZ algorithms + dictBuilder - dictionary builder for zstd Copyright (C) Yann Collet 2016 - GPL v2 License + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + * 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. - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + 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 + OWNER 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 + - Zstd source repository : https://www.zstd.net */ -/* ************************************** +/*-************************************** * Compiler Options ****************************************/ /* Disable some Visual warning messages */ @@ -41,7 +50,7 @@ /*-************************************* -* Includes +* Dependencies ***************************************/ #include /* malloc, free */ #include /* memset */ @@ -53,9 +62,10 @@ #include "mem.h" /* read */ #include "error_private.h" #include "divsufsort.h" -#include "dictBuilder.h" -#include "zstd_compress.c" +#include "dictBuilder_static.h" +#include "fse.h" #include "huff0_static.h" +#include "zstd_internal.h" /*-************************************* @@ -94,16 +104,16 @@ static const size_t g_min_fast_dictContent = 192; #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #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 */ -void DiB_setNotificationLevel(unsigned l) { g_displayLevel=l; } +void ZDICT_setNotificationLevel(unsigned l) { g_displayLevel=l; } #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ - if (DiB_GetMilliSpan(g_time) > refreshRate) \ + if (ZDICT_GetMilliSpan(g_time) > refreshRate) \ { g_time = clock(); DISPLAY(__VA_ARGS__); \ if (g_displayLevel>=4) fflush(stdout); } } static const unsigned refreshRate = 300; static clock_t g_time = 0; -void DiB_printHex(U32 dlevel, const void* ptr, size_t length) +void ZDICT_printHex(U32 dlevel, const void* ptr, size_t length) { const BYTE* const b = (const BYTE*)ptr; size_t u; @@ -133,81 +143,25 @@ void DiB_printHex(U32 dlevel, const void* ptr, size_t length) } -/* ******************************************************** +/*-******************************************************** * Helper functions **********************************************************/ -unsigned DiB_versionNumber (void) { return DiB_VERSION_NUMBER; } - -static unsigned DiB_GetMilliSpan(clock_t nPrevious) +static unsigned ZDICT_GetMilliSpan(clock_t nPrevious) { clock_t nCurrent = clock(); unsigned nSpan = (unsigned)(((nCurrent - nPrevious) * 1000) / CLOCKS_PER_SEC); return nSpan; } -unsigned DiB_isError(size_t errorCode) { return ERR_isError(errorCode); } +unsigned ZDICT_isError(size_t errorCode) { return ERR_isError(errorCode); } -const char* DiB_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); } - - -/* ******************************************************** -* File related operations -**********************************************************/ -static unsigned long long DiB_getFileSize(const char* infilename) -{ - int r; -#if defined(_MSC_VER) - struct _stat64 statbuf; - r = _stat64(infilename, &statbuf); -#else - struct stat statbuf; - r = stat(infilename, &statbuf); -#endif - if (r || !S_ISREG(statbuf.st_mode)) return 0; /* No good... */ - return (unsigned long long)statbuf.st_size; -} - - -static unsigned long long DiB_getTotalFileSize(const char** fileNamesTable, unsigned nbFiles) -{ - unsigned long long total = 0; - unsigned n; - for (n=0; n bufferSize-pos) fileSize = 0; /* stop there, not enough memory to load all files */ - readSize = fread(buff+pos, 1, (size_t)fileSize, f); - if (readSize != (size_t)fileSize) EXM_THROW(11, "could not read %s", fileNamesTable[n]); - pos += readSize; - fileSizes[n] = (size_t)fileSize; - fclose(f); - } -} +const char* ZDICT_getErrorName(size_t errorCode) { return ERR_getErrorName(errorCode); } /*-******************************************************** * Dictionary training functions **********************************************************/ -static size_t DiB_read_ARCH(const void* p) { size_t r; memcpy(&r, p, sizeof(r)); return r; } - -static unsigned DiB_NbCommonBytes (register size_t val) +static unsigned ZDICT_NbCommonBytes (register size_t val) { if (MEM_isLittleEndian()) { if (MEM_64bits()) { @@ -266,17 +220,17 @@ static unsigned DiB_NbCommonBytes (register size_t val) } -/*! DiB_count() : +/*! ZDICT_count() : Count the nb of common bytes between 2 pointers. Note : this function presumes end of buffer followed by noisy guard band. */ -static size_t DiB_count(const void* pIn, const void* pMatch) +static size_t ZDICT_count(const void* pIn, const void* pMatch) { const char* const pStart = (const char*)pIn; for (;;) { - size_t diff = DiB_read_ARCH(pMatch) ^ DiB_read_ARCH(pIn); + size_t diff = MEM_readST(pMatch) ^ MEM_readST(pIn); if (!diff) { pIn = (const char*)pIn+sizeof(size_t); pMatch = (const char*)pMatch+sizeof(size_t); continue; } - pIn = (const char*)pIn+DiB_NbCommonBytes(diff); + pIn = (const char*)pIn+ZDICT_NbCommonBytes(diff); return (size_t)((const char*)pIn - pStart); } } @@ -288,7 +242,7 @@ typedef struct { U32 savings; } dictItem; -void DiB_initDictItem(dictItem* d) +static void ZDICT_initDictItem(dictItem* d) { d->pos = 1; d->length = 0; @@ -298,9 +252,9 @@ void DiB_initDictItem(dictItem* d) #define LLIMIT 64 /* heuristic determined experimentally */ #define MINMATCHLENGTH 7 /* heuristic determined experimentally */ -static dictItem DiB_analyzePos( +static dictItem ZDICT_analyzePos( BYTE* doneMarks, - const saidx_t* suffix, U32 start, + const int* suffix, U32 start, const void* buffer, U32 minRatio) { U32 lengthList[LLIMIT] = {0}; @@ -334,12 +288,12 @@ static dictItem DiB_analyzePos( /* look forward */ do { end++; - length = DiB_count(b + pos, b + suffix[end]); + length = ZDICT_count(b + pos, b + suffix[end]); } while (length >=MINMATCHLENGTH); /* look backward */ do { - length = DiB_count(b + pos, b + *(suffix+start-1)); + length = ZDICT_count(b + pos, b + *(suffix+start-1)); if (length >=MINMATCHLENGTH) start--; } while(length >= MINMATCHLENGTH); @@ -400,14 +354,14 @@ static dictItem DiB_analyzePos( /* look forward */ do { end++; - length = DiB_count(b + pos, b + suffix[end]); + length = ZDICT_count(b + pos, b + suffix[end]); if (length >= LLIMIT) length = LLIMIT-1; lengthList[length]++; } while (length >=MINMATCHLENGTH); /* look backward */ do { - length = DiB_count(b + pos, b + suffix[start-1]); + length = ZDICT_count(b + pos, b + suffix[start-1]); if (length >= LLIMIT) length = LLIMIT-1; lengthList[length]++; if (length >=MINMATCHLENGTH) start--; @@ -453,7 +407,7 @@ static dictItem DiB_analyzePos( if (testedPos == pos) length = solution.length; else { - length = DiB_count(b+pos, b+testedPos); + length = ZDICT_count(b+pos, b+testedPos); if (length > solution.length) length = solution.length; } pEnd = (U32)(testedPos + length); @@ -465,11 +419,11 @@ static dictItem DiB_analyzePos( } -/*! DiB_checkMerge +/*! ZDICT_checkMerge check if dictItem can be merged, do it if possible @return : id of destination elt, 0 if not merged */ -static U32 DiB_checkMerge(dictItem* table, dictItem elt, U32 eltNbToSkip) +static U32 ZDICT_checkMerge(dictItem* table, dictItem elt, U32 eltNbToSkip) { const U32 tableSize = table->pos; const U32 max = elt.pos + (elt.length-1); @@ -513,7 +467,7 @@ static U32 DiB_checkMerge(dictItem* table, dictItem elt, U32 eltNbToSkip) } -static void DiB_removeDictItem(dictItem* table, U32 id) +static void ZDICT_removeDictItem(dictItem* table, U32 id) { /* convention : first element is nb of elts */ U32 max = table->pos; @@ -525,15 +479,15 @@ static void DiB_removeDictItem(dictItem* table, U32 id) } -static void DiB_insertDictItem(dictItem* table, U32 maxSize, dictItem elt) +static void ZDICT_insertDictItem(dictItem* table, U32 maxSize, dictItem elt) { /* merge if possible */ - U32 mergeId = DiB_checkMerge(table, elt, 0); + U32 mergeId = ZDICT_checkMerge(table, elt, 0); if (mergeId) { U32 newMerge = 1; while (newMerge) { - newMerge = DiB_checkMerge(table, table[mergeId], mergeId); - if (newMerge) DiB_removeDictItem(table, mergeId); + newMerge = ZDICT_checkMerge(table, table[mergeId], mergeId); + if (newMerge) ZDICT_removeDictItem(table, mergeId); mergeId = newMerge; } return; @@ -555,7 +509,7 @@ static void DiB_insertDictItem(dictItem* table, U32 maxSize, dictItem elt) } -static U32 DiB_dictSize(const dictItem* dictList) +static U32 ZDICT_dictSize(const dictItem* dictList) { U32 u, dictSize = 0; for (u=1; u> shiftRatio; - saint_t errorCode; + int divSuftSortResult; /* init */ DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */ if (!suffix0 || !reverseSuffix || !doneMarks || !filePos) - EXM_THROW(1, "not enough memory for DiB_trainBuffer"); + EXM_THROW(1, "not enough memory for ZDICT_trainBuffer"); if (minRatio < MINRATIO) minRatio = MINRATIO; memset(doneMarks, 0, bufferSize+16); /* sort */ DISPLAYLEVEL(2, "sorting %u files of total size %u MB ...\n", nbFiles, (U32)(bufferSize>>20)); - errorCode = divsufsort((const sauchar_t*)buffer, suffix, (saidx_t)bufferSize); - if (errorCode != 0) EXM_THROW(2, "sort failed"); - suffix[bufferSize] = (saidx_t)bufferSize; /* leads into noise */ - suffix0[0] = (saidx_t)bufferSize; /* leads into noise */ + divSuftSortResult = divsufsort((const unsigned char*)buffer, suffix, (int)bufferSize, 0); + if (divSuftSortResult != 0) EXM_THROW(2, "sort failed"); + suffix[bufferSize] = (int)bufferSize; /* leads into noise */ + suffix0[0] = (int)bufferSize; /* leads into noise */ { /* build reverse suffix sort */ size_t pos; @@ -608,9 +562,9 @@ static void DiB_trainBuffer(dictItem* dictList, U32 dictListSize, U32 cursor; for (cursor=0; cursor < bufferSize; ) { dictItem solution; if (doneMarks[cursor]) { cursor++; continue; } - solution = DiB_analyzePos(doneMarks, suffix, reverseSuffix[cursor], buffer, minRatio); + solution = ZDICT_analyzePos(doneMarks, suffix, reverseSuffix[cursor], buffer, minRatio); if (solution.length==0) { cursor++; continue; } - DiB_insertDictItem(dictList, dictListSize, solution); + ZDICT_insertDictItem(dictList, dictListSize, solution); cursor += solution.length; DISPLAYUPDATE(2, "\r%4.2f %% \r", (double)cursor / bufferSize * 100); } } @@ -633,26 +587,7 @@ static void DiB_trainBuffer(dictItem* dictList, U32 dictListSize, } -static size_t DiB_findMaxMem(unsigned long long requiredMem) -{ - size_t step = 8 MB; - void* testmem = NULL; - - requiredMem = (((requiredMem >> 23) + 1) << 23); - requiredMem += 2 * step; - if (requiredMem > maxMemory) requiredMem = maxMemory; - - while (!testmem) { - requiredMem -= step; - testmem = malloc((size_t)requiredMem); - } - - free(testmem); - return (size_t)(requiredMem - step); -} - - -static void DiB_fillNoise(void* buffer, size_t length) +static void ZDICT_fillNoise(void* buffer, size_t length) { unsigned acc = PRIME1; size_t p=0;; @@ -672,34 +607,36 @@ typedef struct } EStats_ress_t; -static void DiB_countEStats(EStats_ress_t esr, +static void ZDICT_countEStats(EStats_ress_t esr, U32* countLit, U32* offsetcodeCount, U32* matchlengthCount, U32* litlengthCount, const void* src, size_t srcSize) { const BYTE* bytePtr; const U32* u32Ptr; + seqStore_t seqStore; if (srcSize > BLOCKSIZE) srcSize = BLOCKSIZE; /* protection vs large samples */ ZSTD_copyCCtx(esr.zc, esr.ref); ZSTD_compressBlock(esr.zc, esr.workPlace, BLOCKSIZE, src, srcSize); + seqStore = ZSTD_copySeqStore(esr.zc); /* count stats */ - for(bytePtr = esr.zc->seqStore.litStart; bytePtr < esr.zc->seqStore.lit; bytePtr++) + for(bytePtr = seqStore.litStart; bytePtr < seqStore.lit; bytePtr++) countLit[*bytePtr]++; - for(u32Ptr = esr.zc->seqStore.offsetStart; u32Ptr < esr.zc->seqStore.offset; u32Ptr++) { + for(u32Ptr = seqStore.offsetStart; u32Ptr < seqStore.offset; u32Ptr++) { BYTE offcode = (BYTE)ZSTD_highbit(*u32Ptr) + 1; if (*u32Ptr==0) offcode=0; offsetcodeCount[offcode]++; } - for(bytePtr = esr.zc->seqStore.matchLengthStart; bytePtr < esr.zc->seqStore.matchLength; bytePtr++) + for(bytePtr = seqStore.matchLengthStart; bytePtr < seqStore.matchLength; bytePtr++) matchlengthCount[*bytePtr]++; - for(bytePtr = esr.zc->seqStore.litLengthStart; bytePtr < esr.zc->seqStore.litLength; bytePtr++) + for(bytePtr = seqStore.litLengthStart; bytePtr < seqStore.litLength; bytePtr++) litlengthCount[*bytePtr]++; } -#define OFFCODE_MAX 18 -static size_t DiB_analyzeEntropy(void* dstBuffer, size_t maxDstSize, +#define OFFCODE_MAX 18 /* 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) @@ -734,7 +671,7 @@ static size_t DiB_analyzeEntropy(void* dstBuffer, size_t maxDstSize, /* collect stats on all files */ for (u=0; u1) { - DiB_trainBuffer(dictList, dictListSize, + /* build dictionary */ + if (selectivity>1) { /* selectivity == 1 => fast mode */ + ZDICT_trainBuffer(dictList, dictListSize, samplesBuffer, sBuffSize, sampleSizes, nbSamples, selectivity, (U32)targetDictSize); @@ -885,7 +805,7 @@ static size_t DiB_trainFromBuffer_internal( if (g_displayLevel>= 3) { const U32 nb = 25; U32 u; - U32 dictContentSize = DiB_dictSize(dictList); + U32 dictContentSize = ZDICT_dictSize(dictList); DISPLAYLEVEL(3, "\n %u segments found, of total size %u \n", dictList[0].pos, dictContentSize); DISPLAYLEVEL(3, "list %u best segments \n", nb); for (u=1; u<=nb; u++) { @@ -894,13 +814,13 @@ static size_t DiB_trainFromBuffer_internal( U32 d = MIN(40, l); DISPLAYLEVEL(3, "%3u:%3u bytes at pos %8u, savings %7u bytes |", u, l, p, dictList[u].savings); - DiB_printHex(3, (const char*)samplesBuffer+p, d); + ZDICT_printHex(3, (const char*)samplesBuffer+p, d); DISPLAYLEVEL(3, "| \n"); } } } /* create dictionary */ { - U32 dictContentSize = DiB_dictSize(dictList); + U32 dictContentSize = ZDICT_dictSize(dictList); size_t hSize; BYTE* ptr; U32 u; @@ -918,7 +838,7 @@ static size_t DiB_trainFromBuffer_internal( if (selectivity==1) { /* note could also be used to complete a dictionary, but not necessarily better */ DISPLAYLEVEL(3, "\r%70s\r", ""); /* clean display line */ DISPLAYLEVEL(3, "Adding %u KB with fast sampling \n", (U32)(targetDictSize>>10)); - dictContentSize = (U32)DiB_fastSampling((char*)dictBuffer + g_provision_entropySize, + dictContentSize = (U32)ZDICT_fastSampling((char*)dictBuffer + g_provision_entropySize, targetDictSize, samplesBuffer, sBuffSize); } @@ -929,7 +849,7 @@ static size_t DiB_trainFromBuffer_internal( /* entropic tables */ DISPLAYLEVEL(2, "\r%70s\r", ""); /* clean display line */ DISPLAYLEVEL(2, "statistics ... \n"); - hSize += DiB_analyzeEntropy((char*)dictBuffer+4, maxDictSize-4, + hSize += ZDICT_analyzeEntropy((char*)dictBuffer+4, maxDictSize-4, compressionLevel, samplesBuffer, sampleSizes, nbSamples, (char*)dictBuffer + maxDictSize - dictContentSize, dictContentSize); @@ -945,76 +865,38 @@ static size_t DiB_trainFromBuffer_internal( } -/* issue : samplesBuffer need to be followed by a noisy guard band. -* work around : duplicate the buffer, and add the noise ? */ -size_t DiB_trainFromBuffer(void* dictBuffer, size_t maxDictSize, - const void* samplesBuffer, const size_t* sampleSizes, unsigned nbSamples, - DiB_params_t params) +size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dictBufferCapacity, + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, + ZDICT_params_t params) { size_t sBuffSize; void* newBuff; size_t result; - { unsigned u; for (u=0, sBuffSize=0; u totalSizeToLoad) benchedSize = (size_t)totalSizeToLoad; - if (benchedSize < totalSizeToLoad) - DISPLAYLEVEL(1, "Not enough memory; training on %u MB only...\n", (unsigned)(benchedSize >> 20)); - - /* Memory allocation & restrictions */ - srcBuffer = malloc(benchedSize+NOISELENGTH); /* + noise */ - if ((!fileSizes) || (!srcBuffer) || (!dictBuffer)) EXM_THROW(12, "not enough memory for DiB_trainFiles"); /* should not happen */ - - /* Load input buffer */ - DiB_loadFiles(srcBuffer, benchedSize, fileSizes, fileNamesTable, nbFiles); - DiB_fillNoise((char*)srcBuffer + benchedSize, NOISELENGTH); /* guard band, for end of buffer condition */ - - /* call buffer version */ - dictSize = DiB_trainFromBuffer_internal(dictBuffer, maxDictSize, - srcBuffer, fileSizes, nbFiles, - params); - if (DiB_isError(dictSize)) - { - DISPLAYLEVEL(1, "dictionary training failed : %s", DiB_getErrorName(dictSize)); /* should not happen */ - result = 1; - goto _cleanup; - } - - /* save dict */ - DISPLAYLEVEL(2, "Save dictionary of size %u into file %s \n", (U32)dictSize, dictFileName); - DiB_saveDict(dictFileName, dictBuffer, dictSize); - - /* clean up */ -_cleanup: - free(srcBuffer); - free(dictBuffer); - free(fileSizes); - return result; + ZDICT_params_t params; + memset(¶ms, 0, sizeof(params)); + return ZDICT_trainFromBuffer_advanced(dictBuffer, dictBufferCapacity, + samplesBuffer, samplesSizes, nbSamples, + params); } + diff --git a/lib/dictBuilder.h b/lib/dictBuilder.h new file mode 100644 index 000000000..2ca190ce3 --- /dev/null +++ b/lib/dictBuilder.h @@ -0,0 +1,67 @@ +/* + dictBuilder header file + Copyright (C) Yann Collet 2016 + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + 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 + OWNER 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://www.zstd.net +*/ + +#ifndef DICTBUILDER_H_001 +#define DICTBUILDER_H_001 + +#if defined (__cplusplus) +extern "C" { +#endif + +/*-************************************* +* Public functions +***************************************/ +/*! ZDICT_trainFromBuffer() : + Train a dictionary from a memory buffer `samplesBuffer`, + where `nbSamples` samples have been stored concatenated. + Each sample size is provided into an orderly table `samplesSizes`. + Resulting dictionary will be saved into `dictBuffer`. + @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) + or an error code, which can be tested by ZDICT_isError(). +*/ +size_t ZDICT_trainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples); + + +/*-************************************* +* Helper functions +***************************************/ +unsigned ZDICT_isError(size_t errorCode); +const char* ZDICT_getErrorName(size_t errorCode); + + +#if defined (__cplusplus) +} +#endif + +#endif diff --git a/lib/dictBuilder_static.h b/lib/dictBuilder_static.h new file mode 100644 index 000000000..0bddb30df --- /dev/null +++ b/lib/dictBuilder_static.h @@ -0,0 +1,104 @@ +/* + dictBuilder header file + for static linking only + Copyright (C) Yann Collet 2016 + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + 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 + OWNER 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://www.zstd.net +*/ + +/* This library is EXPERIMENTAL, below API is not yet stable */ + +#ifndef DICTBUILDER_STATIC_H_002 +#define DICTBUILDER_STATIC_H_002 + +#if defined (__cplusplus) +extern "C" { +#endif + +/*-************************************* +* Dependencies +***************************************/ +#include "dictBuilder.h" + + +/*-************************************* +* Public type +***************************************/ +typedef struct { + unsigned selectivityLevel; /* 0 means default; larger => bigger selection => larger dictionary */ + unsigned compressionLevel; /* 0 means default; target a specific zstd compression level */ +} ZDICT_params_t; + + +/*-************************************* +* Public functions +***************************************/ +/*! ZDICT_trainFromBuffer_advanced() : + Same as ZDICT_trainFromBuffer() with control over more parameters. + `parameters` is optional and can be provided with values set to 0 to mean "default". + @return : size of dictionary stored into `dictBuffer` (<= `dictBufferSize`) + or an error code, which can be tested by DiB_isError(). + note : ZDICT_trainFromBuffer_advanced() will send notifications into stderr if instructed to, using ZDICT_setNotificationLevel() +*/ +size_t ZDICT_trainFromBuffer_advanced(void* dictBuffer, size_t dictBufferCapacity, + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, + ZDICT_params_t parameters); + + +/*-************************************* +* Helper functions +***************************************/ +/*! ZDICT_setNotificationLevel() : + Set amount of notification to be displayed on the console. + default : 0 = no console notification. + 1 = errors; 2 = notifications; 3 = details; 4 = debug; + Note : not thread-safe (uses a global constant) +*/ +void ZDICT_setNotificationLevel(unsigned l); + + +/*-************************************* +* Private functions +***************************************/ +/*! ZDICT_trainFromBuffer_unsafe() : + Same as ZDICT_trainFromBuffer_advanced(), but does not control `samplesBuffer`. + note : `samplesBuffer` must be followed by noisy guard band to avoid out-of-buffer reads. + @return : size of dictionary stored into `dictBuffer` (<= `dictBufferCapacity`) + or an error code. +*/ +size_t ZDICT_trainFromBuffer_unsafe(void* dictBuffer, size_t dictBufferCapacity, + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples, + ZDICT_params_t parameters); + + +#if defined (__cplusplus) +} +#endif + +#endif /* DICTBUILDER_STATIC_H_002 */ diff --git a/lib/divsufsort.c b/lib/divsufsort.c new file mode 100644 index 000000000..ca790574a --- /dev/null +++ b/lib/divsufsort.c @@ -0,0 +1,1905 @@ +/* + * divsufsort.c for libdivsufsort-lite + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if defined(_MSC_VER) + #pragma warning(disable : 4244) +#endif + +#include +#include +#include + +#include "divsufsort.h" + +/*- Constants -*/ +#if defined(INLINE) +# undef INLINE +#endif +#if !defined(INLINE) +# define INLINE __inline +#endif +#if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1) +# undef ALPHABET_SIZE +#endif +#if !defined(ALPHABET_SIZE) +# define ALPHABET_SIZE (256) +#endif +#define BUCKET_A_SIZE (ALPHABET_SIZE) +#define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) +#if defined(SS_INSERTIONSORT_THRESHOLD) +# if SS_INSERTIONSORT_THRESHOLD < 1 +# undef SS_INSERTIONSORT_THRESHOLD +# define SS_INSERTIONSORT_THRESHOLD (1) +# endif +#else +# define SS_INSERTIONSORT_THRESHOLD (8) +#endif +#if defined(SS_BLOCKSIZE) +# if SS_BLOCKSIZE < 0 +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (0) +# elif 32768 <= SS_BLOCKSIZE +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (32767) +# endif +#else +# define SS_BLOCKSIZE (1024) +#endif +/* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */ +#if SS_BLOCKSIZE == 0 +# define SS_MISORT_STACKSIZE (96) +#elif SS_BLOCKSIZE <= 4096 +# define SS_MISORT_STACKSIZE (16) +#else +# define SS_MISORT_STACKSIZE (24) +#endif +#define SS_SMERGE_STACKSIZE (32) +#define TR_INSERTIONSORT_THRESHOLD (8) +#define TR_STACKSIZE (64) + + +/*- Macros -*/ +#ifndef SWAP +# define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0) +#endif /* SWAP */ +#ifndef MIN +# define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif /* MIN */ +#ifndef MAX +# define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif /* MAX */ +#define STACK_PUSH(_a, _b, _c, _d)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize++].d = (_d);\ + } while(0) +#define STACK_PUSH5(_a, _b, _c, _d, _e)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\ + } while(0) +#define STACK_POP(_a, _b, _c, _d)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d;\ + } while(0) +#define STACK_POP5(_a, _b, _c, _d, _e)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\ + } while(0) +#define BUCKET_A(_c0) bucket_A[(_c0)] +#if ALPHABET_SIZE == 256 +#define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) +#else +#define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)]) +#endif + + +/*- Private Functions -*/ + +static const int lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +int +ss_ilg(int n) { +#if SS_BLOCKSIZE == 0 + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +#elif SS_BLOCKSIZE < 256 + return lg_table[n]; +#else + return (n & 0xff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]; +#endif +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + +#if SS_BLOCKSIZE != 0 + +static const int sqq_table[256] = { + 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, + 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, + 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, +110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, +128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, +143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, +156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, +169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, +181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, +192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, +202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, +212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, +221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, +230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, +239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, +247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 +}; + +static INLINE +int +ss_isqrt(int x) { + int y, e; + + if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } + e = (x & 0xffff0000) ? + ((x & 0xff000000) ? + 24 + lg_table[(x >> 24) & 0xff] : + 16 + lg_table[(x >> 16) & 0xff]) : + ((x & 0x0000ff00) ? + 8 + lg_table[(x >> 8) & 0xff] : + 0 + lg_table[(x >> 0) & 0xff]); + + if(e >= 16) { + y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); + if(e >= 24) { y = (y + 1 + x / y) >> 1; } + y = (y + 1 + x / y) >> 1; + } else if(e >= 8) { + y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; + } else { + return sqq_table[x] >> 4; + } + + return (x < (y * y)) ? y - 1 : y; +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/* Compares two suffixes. */ +static INLINE +int +ss_compare(const unsigned char *T, + const int *p1, const int *p2, + int depth) { + const unsigned char *U1, *U2, *U1n, *U2n; + + for(U1 = T + depth + *p1, + U2 = T + depth + *p2, + U1n = T + *(p1 + 1) + 2, + U2n = T + *(p2 + 1) + 2; + (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); + ++U1, ++U2) { + } + + return U1 < U1n ? + (U2 < U2n ? *U1 - *U2 : 1) : + (U2 < U2n ? -1 : 0); +} + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) + +/* Insertionsort for small size groups */ +static +void +ss_insertionsort(const unsigned char *T, const int *PA, + int *first, int *last, int depth) { + int *i, *j; + int t; + int r; + + for(i = last - 2; first <= i; --i) { + for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { + do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); + if(last <= j) { break; } + } + if(r == 0) { *j = ~*j; } + *(j - 1) = t; + } +} + +#endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */ + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +void +ss_fixdown(const unsigned char *Td, const int *PA, + int *SA, int i, int size) { + int j, k; + int v; + int c, d, e; + + for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = Td[PA[SA[k = j++]]]; + if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +ss_heapsort(const unsigned char *Td, const int *PA, int *SA, int size) { + int i, m; + int t; + + m = size; + if((size % 2) == 0) { + m--; + if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + ss_fixdown(Td, PA, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +int * +ss_median3(const unsigned char *Td, const int *PA, + int *v1, int *v2, int *v3) { + int *t; + if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); } + if(Td[PA[*v2]] > Td[PA[*v3]]) { + if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +int * +ss_median5(const unsigned char *Td, const int *PA, + int *v1, int *v2, int *v3, int *v4, int *v5) { + int *t; + if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); } + if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); } + if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); } + if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); } + if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); } + if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +int * +ss_pivot(const unsigned char *Td, const int *PA, int *first, int *last) { + int *middle; + int t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return ss_median3(Td, PA, first, middle, last - 1); + } else { + t >>= 2; + return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = ss_median3(Td, PA, first, first + t, first + (t << 1)); + middle = ss_median3(Td, PA, middle - t, middle, middle + t); + last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); + return ss_median3(Td, PA, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +/* Binary partition for substrings. */ +static INLINE +int * +ss_partition(const int *PA, + int *first, int *last, int depth) { + int *a, *b; + int t; + for(a = first - 1, b = last;;) { + for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; } + for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { } + if(b <= a) { break; } + t = ~*b; + *b = *a; + *a = t; + } + if(first < a) { *first = ~*first; } + return a; +} + +/* Multikey introsort for medium size groups. */ +static +void +ss_mintrosort(const unsigned char *T, const int *PA, + int *first, int *last, + int depth) { +#define STACK_SIZE SS_MISORT_STACKSIZE + struct { int *a, *b, c; int d; } stack[STACK_SIZE]; + const unsigned char *Td; + int *a, *b, *c, *d, *e, *f; + int s, t; + int ssize; + int limit; + int v, x = 0; + + for(ssize = 0, limit = ss_ilg(last - first);;) { + + if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { +#if 1 < SS_INSERTIONSORT_THRESHOLD + if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); } +#endif + STACK_POP(first, last, depth, limit); + continue; + } + + Td = T + depth; + if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); } + if(limit < 0) { + for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { + if((x = Td[PA[*a]]) != v) { + if(1 < (a - first)) { break; } + v = x; + first = a; + } + } + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, a, depth); + } + if((a - first) <= (last - a)) { + if(1 < (a - first)) { + STACK_PUSH(a, last, depth, -1); + last = a, depth += 1, limit = ss_ilg(a - first); + } else { + first = a, limit = -1; + } + } else { + if(1 < (last - a)) { + STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); + first = a, limit = -1; + } else { + last = a, depth += 1, limit = ss_ilg(a - first); + } + } + continue; + } + + /* choose pivot */ + a = ss_pivot(Td, PA, first, last); + v = Td[PA[*a]]; + SWAP(*first, *a); + + /* partition */ + for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + + a = first + (b - a), c = last - (d - c); + b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); + + if((a - first) <= (last - c)) { + if((last - c) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(c, last, depth, limit); + last = a; + } else if((a - first) <= (c - b)) { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + last = a; + } else { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(first, a, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } else { + if((a - first) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(first, a, depth, limit); + first = c; + } else if((last - c) <= (c - b)) { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + first = c; + } else { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(c, last, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } + } else { + limit += 1; + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, last, depth); + limit = ss_ilg(last - first); + } + depth += 1; + } + } +#undef STACK_SIZE +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + + +/*---------------------------------------------------------------------------*/ + +#if SS_BLOCKSIZE != 0 + +static INLINE +void +ss_blockswap(int *a, int *b, int n) { + int t; + for(; 0 < n; --n, ++a, ++b) { + t = *a, *a = *b, *b = t; + } +} + +static INLINE +void +ss_rotate(int *first, int *middle, int *last) { + int *a, *b, t; + int l, r; + l = middle - first, r = last - middle; + for(; (0 < l) && (0 < r);) { + if(l == r) { ss_blockswap(first, middle, l); break; } + if(l < r) { + a = last - 1, b = middle - 1; + t = *a; + do { + *a-- = *b, *b-- = *a; + if(b < first) { + *a = t; + last = a; + if((r -= l + 1) <= l) { break; } + a -= 1, b = middle - 1; + t = *a; + } + } while(1); + } else { + a = first, b = middle; + t = *a; + do { + *a++ = *b, *b++ = *a; + if(last <= b) { + *a = t; + first = a + 1; + if((l -= r + 1) <= r) { break; } + a += 1, b = middle; + t = *a; + } + } while(1); + } + } +} + + +/*---------------------------------------------------------------------------*/ + +static +void +ss_inplacemerge(const unsigned char *T, const int *PA, + int *first, int *middle, int *last, + int depth) { + const int *p; + int *a, *b; + int len, half; + int q, r; + int x; + + for(;;) { + if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } + else { x = 0; p = PA + *(last - 1); } + for(a = first, len = middle - first, half = len >> 1, r = -1; + 0 < len; + len = half, half >>= 1) { + b = a + half; + q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); + if(q < 0) { + a = b + 1; + half -= (len & 1) ^ 1; + } else { + r = q; + } + } + if(a < middle) { + if(r == 0) { *a = ~*a; } + ss_rotate(a, middle, last); + last -= middle - a; + middle = a; + if(first == middle) { break; } + } + --last; + if(x != 0) { while(*--last < 0) { } } + if(middle == last) { break; } + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Merge-forward with internal buffer. */ +static +void +ss_mergeforward(const unsigned char *T, const int *PA, + int *first, int *middle, int *last, + int *buf, int depth) { + int *a, *b, *c, *bufend; + int t; + int r; + + bufend = buf + (middle - first) - 1; + ss_blockswap(buf, first, middle - first); + + for(t = *(a = first), b = buf, c = middle;;) { + r = ss_compare(T, PA + *b, PA + *c, depth); + if(r < 0) { + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + } else if(r > 0) { + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } else { + *c = ~*c; + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } + } +} + +/* Merge-backward with internal buffer. */ +static +void +ss_mergebackward(const unsigned char *T, const int *PA, + int *first, int *middle, int *last, + int *buf, int depth) { + const int *p1, *p2; + int *a, *b, *c, *bufend; + int t; + int r; + int x; + + bufend = buf + (last - middle) - 1; + ss_blockswap(buf, middle, last - middle); + + x = 0; + if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } + else { p1 = PA + *bufend; } + if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } + else { p2 = PA + *(middle - 1); } + for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { + r = ss_compare(T, p1, p2, depth); + if(0 < r) { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = *b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + } else if(r < 0) { + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } else { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = ~*b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } + } +} + +/* D&C based merge. */ +static +void +ss_swapmerge(const unsigned char *T, const int *PA, + int *first, int *middle, int *last, + int *buf, int bufsize, int depth) { +#define STACK_SIZE SS_SMERGE_STACKSIZE +#define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) +#define MERGE_CHECK(a, b, c)\ + do {\ + if(((c) & 1) ||\ + (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\ + *(a) = ~*(a);\ + }\ + if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\ + *(b) = ~*(b);\ + }\ + } while(0) + struct { int *a, *b, *c; int d; } stack[STACK_SIZE]; + int *l, *r, *lm, *rm; + int m, len, half; + int ssize; + int check, next; + + for(check = 0, ssize = 0;;) { + if((last - middle) <= bufsize) { + if((first < middle) && (middle < last)) { + ss_mergebackward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + if((middle - first) <= bufsize) { + if(first < middle) { + ss_mergeforward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1; + 0 < len; + len = half, half >>= 1) { + if(ss_compare(T, PA + GETIDX(*(middle + m + half)), + PA + GETIDX(*(middle - m - half - 1)), depth) < 0) { + m += half + 1; + half -= (len & 1) ^ 1; + } + } + + if(0 < m) { + lm = middle - m, rm = middle + m; + ss_blockswap(lm, middle, m); + l = r = middle, next = 0; + if(rm < last) { + if(*rm < 0) { + *rm = ~*rm; + if(first < lm) { for(; *--l < 0;) { } next |= 4; } + next |= 1; + } else if(first < lm) { + for(; *r < 0; ++r) { } + next |= 2; + } + } + + if((l - first) <= (last - r)) { + STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); + middle = lm, last = l, check = (check & 3) | (next & 4); + } else { + if((next & 2) && (r == middle)) { next ^= 6; } + STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); + first = r, middle = rm, check = (next & 3) | (check & 4); + } + } else { + if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { + *middle = ~*middle; + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + } + } +#undef STACK_SIZE +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/* Substring sort */ +static +void +sssort(const unsigned char *T, const int *PA, + int *first, int *last, + int *buf, int bufsize, + int depth, int n, int lastsuffix) { + int *a; +#if SS_BLOCKSIZE != 0 + int *b, *middle, *curbuf; + int j, k, curbufsize, limit; +#endif + int i; + + if(lastsuffix != 0) { ++first; } + +#if SS_BLOCKSIZE == 0 + ss_mintrosort(T, PA, first, last, depth); +#else + if((bufsize < SS_BLOCKSIZE) && + (bufsize < (last - first)) && + (bufsize < (limit = ss_isqrt(last - first)))) { + if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; } + buf = middle = last - limit, bufsize = limit; + } else { + middle = last, limit = 0; + } + for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); +#endif + curbufsize = last - (a + SS_BLOCKSIZE); + curbuf = a + SS_BLOCKSIZE; + if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; } + for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { + ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); + } + } +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, middle, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, middle, depth); +#endif + for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { + if(i & 1) { + ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); + a -= k; + } + } + if(limit != 0) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, middle, last, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, middle, last, depth); +#endif + ss_inplacemerge(T, PA, first, middle, last, depth); + } +#endif + + if(lastsuffix != 0) { + /* Insert last type B* suffix. */ + int PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2; + for(a = first, i = *(first - 1); + (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); + ++a) { + *(a - 1) = *a; + } + *(a - 1) = i; + } +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +int +tr_ilg(int n) { + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +} + + +/*---------------------------------------------------------------------------*/ + +/* Simple insertionsort for small size groups. */ +static +void +tr_insertionsort(const int *ISAd, int *first, int *last) { + int *a, *b; + int t, r; + + for(a = first + 1; a < last; ++a) { + for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { + do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); + if(b < first) { break; } + } + if(r == 0) { *b = ~*b; } + *(b + 1) = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_fixdown(const int *ISAd, int *SA, int i, int size) { + int j, k; + int v; + int c, d, e; + + for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = ISAd[SA[k = j++]]; + if(d < (e = ISAd[SA[j]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +tr_heapsort(const int *ISAd, int *SA, int size) { + int i, m; + int t; + + m = size; + if((size % 2) == 0) { + m--; + if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + tr_fixdown(ISAd, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +int * +tr_median3(const int *ISAd, int *v1, int *v2, int *v3) { + int *t; + if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); } + if(ISAd[*v2] > ISAd[*v3]) { + if(ISAd[*v1] > ISAd[*v3]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +int * +tr_median5(const int *ISAd, + int *v1, int *v2, int *v3, int *v4, int *v5) { + int *t; + if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); } + if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); } + if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); } + if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); } + if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); } + if(ISAd[*v3] > ISAd[*v4]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +int * +tr_pivot(const int *ISAd, int *first, int *last) { + int *middle; + int t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return tr_median3(ISAd, first, middle, last - 1); + } else { + t >>= 2; + return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = tr_median3(ISAd, first, first + t, first + (t << 1)); + middle = tr_median3(ISAd, middle - t, middle, middle + t); + last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); + return tr_median3(ISAd, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +typedef struct _trbudget_t trbudget_t; +struct _trbudget_t { + int chance; + int remain; + int incval; + int count; +}; + +static INLINE +void +trbudget_init(trbudget_t *budget, int chance, int incval) { + budget->chance = chance; + budget->remain = budget->incval = incval; +} + +static INLINE +int +trbudget_check(trbudget_t *budget, int size) { + if(size <= budget->remain) { budget->remain -= size; return 1; } + if(budget->chance == 0) { budget->count += size; return 0; } + budget->remain += budget->incval - size; + budget->chance -= 1; + return 1; +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_partition(const int *ISAd, + int *first, int *middle, int *last, + int **pa, int **pb, int v) { + int *a, *b, *c, *d, *e, *f; + int t, s; + int x = 0; + + for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + first += (b - a), last -= (d - c); + } + *pa = first, *pb = last; +} + +static +void +tr_copy(int *ISA, const int *SA, + int *first, int *a, int *b, int *last, + int depth) { + /* sort suffixes of middle partition + by using sorted order of suffixes of left and right partition. */ + int *c, *d, *e; + int s, v; + + v = b - SA - 1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + ISA[s] = d - SA; + } + } + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + ISA[s] = d - SA; + } + } +} + +static +void +tr_partialcopy(int *ISA, const int *SA, + int *first, int *a, int *b, int *last, + int depth) { + int *c, *d, *e; + int s, v; + int rank, lastrank, newrank = -1; + + v = b - SA - 1; + lastrank = -1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } + + lastrank = -1; + for(e = d; first <= e; --e) { + rank = ISA[*e]; + if(lastrank != rank) { lastrank = rank; newrank = e - SA; } + if(newrank != rank) { ISA[*e] = newrank; } + } + + lastrank = -1; + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } +} + +static +void +tr_introsort(int *ISA, const int *ISAd, + int *SA, int *first, int *last, + trbudget_t *budget) { +#define STACK_SIZE TR_STACKSIZE + struct { const int *a; int *b, *c; int d, e; }stack[STACK_SIZE]; + int *a, *b, *c; + int t; + int v, x = 0; + int incr = ISAd - ISA; + int limit, next; + int ssize, trlink = -1; + + for(ssize = 0, limit = tr_ilg(last - first);;) { + + if(limit < 0) { + if(limit == -1) { + /* tandem repeat partition */ + tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); + + /* update ranks */ + if(a < last) { + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + } + if(b < last) { + for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } + } + + /* push */ + if(1 < (b - a)) { + STACK_PUSH5(NULL, a, b, 0, 0); + STACK_PUSH5(ISAd - incr, first, last, -2, trlink); + trlink = ssize - 2; + } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); + last = a, limit = tr_ilg(a - first); + } else if(1 < (last - b)) { + first = b, limit = tr_ilg(last - b); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); + first = b, limit = tr_ilg(last - b); + } else if(1 < (a - first)) { + last = a, limit = tr_ilg(a - first); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else if(limit == -2) { + /* tandem repeat copy */ + a = stack[--ssize].b, b = stack[ssize].c; + if(stack[ssize].d == 0) { + tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); + } + STACK_POP5(ISAd, first, last, limit, trlink); + } else { + /* sorted partition */ + if(0 <= *first) { + a = first; + do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); + first = a; + } + if(first < last) { + a = first; do { *a = ~*a; } while(*++a < 0); + next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; + if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } + + /* push */ + if(trbudget_check(budget, a - first)) { + if((a - first) <= (last - a)) { + STACK_PUSH5(ISAd, a, last, -3, trlink); + ISAd += incr, last = a, limit = next; + } else { + if(1 < (last - a)) { + STACK_PUSH5(ISAd + incr, first, a, next, trlink); + first = a, limit = -3; + } else { + ISAd += incr, last = a, limit = next; + } + } + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + if(1 < (last - a)) { + first = a, limit = -3; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + continue; + } + + if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { + tr_insertionsort(ISAd, first, last); + limit = -3; + continue; + } + + if(limit-- == 0) { + tr_heapsort(ISAd, first, last - first); + for(a = last - 1; first < a; a = b) { + for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } + } + limit = -3; + continue; + } + + /* choose pivot */ + a = tr_pivot(ISAd, first, last); + SWAP(*first, *a); + v = ISAd[*first]; + + /* partition */ + tr_partition(ISAd, first, first + 1, last, &a, &b, v); + if((last - first) != (b - a)) { + next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; + + /* update ranks */ + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } + + /* push */ + if((1 < (b - a)) && (trbudget_check(budget, b - a))) { + if((a - first) <= (last - b)) { + if((last - b) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((a - first) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + if((a - first) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((last - b) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } + } else { + if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + first = b; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + last = a; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } + } else { + if(trbudget_check(budget, last - first)) { + limit = tr_ilg(last - first), ISAd += incr; + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } +#undef STACK_SIZE +} + + + +/*---------------------------------------------------------------------------*/ + +/* Tandem repeat sort */ +static +void +trsort(int *ISA, int *SA, int n, int depth) { + int *ISAd; + int *first, *last; + trbudget_t budget; + int t, skip, unsorted; + + trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); +/* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */ + for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { + first = SA; + skip = 0; + unsorted = 0; + do { + if((t = *first) < 0) { first -= t; skip += t; } + else { + if(skip != 0) { *(first + skip) = skip; skip = 0; } + last = SA + ISA[t] + 1; + if(1 < (last - first)) { + budget.count = 0; + tr_introsort(ISA, ISAd, SA, first, last, &budget); + if(budget.count != 0) { unsorted += budget.count; } + else { skip = first - last; } + } else if((last - first) == 1) { + skip = -1; + } + first = last; + } + } while(first < (SA + n)); + if(skip != 0) { *(first + skip) = skip; } + if(unsorted == 0) { break; } + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Sorts suffixes of type B*. */ +static +int +sort_typeBstar(const unsigned char *T, int *SA, + int *bucket_A, int *bucket_B, + int n, int openMP) { + int *PAb, *ISAb, *buf; +#ifdef LIBBSC_OPENMP + int *curbuf; + int l; +#endif + int i, j, k, t, m, bufsize; + int c0, c1; +#ifdef LIBBSC_OPENMP + int d0, d1; +#endif + (void)openMP; + + /* Initialize bucket arrays. */ + for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } + for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } + + /* Count the number of occurrences of the first one or two characters of each + type A, B and B* suffix. Moreover, store the beginning position of all + type B* suffixes into the array SA. */ + for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { + /* type A suffix. */ + do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); + if(0 <= i) { + /* type B* suffix. */ + ++BUCKET_BSTAR(c0, c1); + SA[--m] = i; + /* type B suffix. */ + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { + ++BUCKET_B(c0, c1); + } + } + } + m = n - m; +/* +note: + A type B* suffix is lexicographically smaller than a type B suffix that + begins with the same first two characters. +*/ + + /* Calculate the index of start/end point of each bucket. */ + for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { + t = i + BUCKET_A(c0); + BUCKET_A(c0) = i + j; /* start point */ + i = t + BUCKET_B(c0, c0); + for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { + j += BUCKET_BSTAR(c0, c1); + BUCKET_BSTAR(c0, c1) = j; /* end point */ + i += BUCKET_B(c0, c1); + } + } + + if(0 < m) { + /* Sort the type B* suffixes by their first two characters. */ + PAb = SA + n - m; ISAb = SA + m; + for(i = m - 2; 0 <= i; --i) { + t = PAb[i], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = i; + } + t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = m - 1; + + /* Sort the type B* substrings using sssort. */ +#ifdef LIBBSC_OPENMP + if (openMP) + { + buf = SA + m; + c0 = ALPHABET_SIZE - 2, c1 = ALPHABET_SIZE - 1, j = m; +#pragma omp parallel default(shared) private(bufsize, curbuf, k, l, d0, d1) + { + bufsize = (n - (2 * m)) / omp_get_num_threads(); + curbuf = buf + omp_get_thread_num() * bufsize; + k = 0; + for(;;) { + #pragma omp critical(sssort_lock) + { + if(0 < (l = j)) { + d0 = c0, d1 = c1; + do { + k = BUCKET_BSTAR(d0, d1); + if(--d1 <= d0) { + d1 = ALPHABET_SIZE - 1; + if(--d0 < 0) { break; } + } + } while(((l - k) <= 1) && (0 < (l = k))); + c0 = d0, c1 = d1, j = k; + } + } + if(l == 0) { break; } + sssort(T, PAb, SA + k, SA + l, + curbuf, bufsize, 2, n, *(SA + k) == (m - 1)); + } + } + } + else + { + buf = SA + m, bufsize = n - (2 * m); + for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { + for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { + i = BUCKET_BSTAR(c0, c1); + if(1 < (j - i)) { + sssort(T, PAb, SA + i, SA + j, + buf, bufsize, 2, n, *(SA + i) == (m - 1)); + } + } + } + } +#else + buf = SA + m, bufsize = n - (2 * m); + for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { + for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { + i = BUCKET_BSTAR(c0, c1); + if(1 < (j - i)) { + sssort(T, PAb, SA + i, SA + j, + buf, bufsize, 2, n, *(SA + i) == (m - 1)); + } + } + } +#endif + + /* Compute ranks of type B* substrings. */ + for(i = m - 1; 0 <= i; --i) { + if(0 <= SA[i]) { + j = i; + do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); + SA[i + 1] = i - j; + if(i <= 0) { break; } + } + j = i; + do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); + ISAb[SA[i]] = j; + } + + /* Construct the inverse suffix array of type B* suffixes using trsort. */ + trsort(ISAb, SA, m, 1); + + /* Set the sorted order of tyoe B* suffixes. */ + for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { } + if(0 <= i) { + t = i; + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { } + SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; + } + } + + /* Calculate the index of start/end point of each bucket. */ + BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */ + for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { + i = BUCKET_A(c0 + 1) - 1; + for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { + t = i - BUCKET_B(c0, c1); + BUCKET_B(c0, c1) = i; /* end point */ + + /* Move all type B* suffixes to the correct position. */ + for(i = t, j = BUCKET_BSTAR(c0, c1); + j <= k; + --i, --k) { SA[i] = SA[k]; } + } + BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */ + BUCKET_B(c0, c0) = i; /* end point */ + } + } + + return m; +} + +/* Constructs the suffix array by using the sorted order of type B* suffixes. */ +static +void +construct_SA(const unsigned char *T, int *SA, + int *bucket_A, int *bucket_B, + int n, int m) { + int *i, *j, *k; + int s; + int c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + *j = ~s; + c0 = T[--s]; + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else { + assert(((s == 0) && (T[s] == c1)) || (s < 0)); + *j = ~s; + } + } + } + } + + /* Construct the suffix array by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + if((s == 0) || (T[s - 1] < c0)) { s = ~s; } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else { + assert(s < 0); + *i = ~s; + } + } +} + +/* Constructs the burrows-wheeler transformed string directly + by using the sorted order of type B* suffixes. */ +static +int +construct_BWT(const unsigned char *T, int *SA, + int *bucket_A, int *bucket_B, + int n, int m) { + int *i, *j, *k, *orig; + int s; + int c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + c0 = T[--s]; + *j = ~((int)c0); + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else if(s != 0) { + *j = ~s; +#ifndef NDEBUG + } else { + assert(T[s] == c1); +#endif + } + } + } + } + + /* Construct the BWTed string by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~((int)T[n - 2]) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n, orig = SA; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + *i = c0; + if((0 < s) && (T[s - 1] < c0)) { s = ~((int)T[s - 1]); } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else if(s != 0) { + *i = ~s; + } else { + orig = i; + } + } + + return orig - SA; +} + +/* Constructs the burrows-wheeler transformed string directly + by using the sorted order of type B* suffixes. */ +static +int +construct_BWT_indexes(const unsigned char *T, int *SA, + int *bucket_A, int *bucket_B, + int n, int m, + unsigned char * num_indexes, int * indexes) { + int *i, *j, *k, *orig; + int s; + int c0, c1, c2; + + int mod = n / 8; + { + mod |= mod >> 1; mod |= mod >> 2; + mod |= mod >> 4; mod |= mod >> 8; + mod |= mod >> 16; mod >>= 1; + + *num_indexes = (unsigned char)((n - 1) / (mod + 1)); + } + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + + if ((s & mod) == 0) indexes[s / (mod + 1) - 1] = j - SA; + + c0 = T[--s]; + *j = ~((int)c0); + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else if(s != 0) { + *j = ~s; +#ifndef NDEBUG + } else { + assert(T[s] == c1); +#endif + } + } + } + } + + /* Construct the BWTed string by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + if (T[n - 2] < c2) { + if (((n - 1) & mod) == 0) indexes[(n - 1) / (mod + 1) - 1] = k - SA; + *k++ = ~((int)T[n - 2]); + } + else { + *k++ = n - 1; + } + + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n, orig = SA; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + + if ((s & mod) == 0) indexes[s / (mod + 1) - 1] = i - SA; + + c0 = T[--s]; + *i = c0; + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + if((0 < s) && (T[s - 1] < c0)) { + if ((s & mod) == 0) indexes[s / (mod + 1) - 1] = k - SA; + *k++ = ~((int)T[s - 1]); + } else + *k++ = s; + } else if(s != 0) { + *i = ~s; + } else { + orig = i; + } + } + + return orig - SA; +} + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +int +divsufsort(const unsigned char *T, int *SA, int n, int openMP) { + int *bucket_A, *bucket_B; + int m; + int err = 0; + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; } + else if(n == 0) { return 0; } + else if(n == 1) { SA[0] = 0; return 0; } + else if(n == 2) { m = (T[0] < T[1]); SA[m ^ 1] = 0, SA[m] = 1; return 0; } + + bucket_A = (int *)malloc(BUCKET_A_SIZE * sizeof(int)); + bucket_B = (int *)malloc(BUCKET_B_SIZE * sizeof(int)); + + /* Suffixsort. */ + if((bucket_A != NULL) && (bucket_B != NULL)) { + m = sort_typeBstar(T, SA, bucket_A, bucket_B, n, openMP); + construct_SA(T, SA, bucket_A, bucket_B, n, m); + } else { + err = -2; + } + + free(bucket_B); + free(bucket_A); + + return err; +} + +int +divbwt(const unsigned char *T, unsigned char *U, int *A, int n, unsigned char * num_indexes, int * indexes, int openMP) { + int *B; + int *bucket_A, *bucket_B; + int m, pidx, i; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0)) { return -1; } + else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } + + if((B = A) == NULL) { B = (int *)malloc((size_t)(n + 1) * sizeof(int)); } + bucket_A = (int *)malloc(BUCKET_A_SIZE * sizeof(int)); + bucket_B = (int *)malloc(BUCKET_B_SIZE * sizeof(int)); + + /* Burrows-Wheeler Transform. */ + if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) { + m = sort_typeBstar(T, B, bucket_A, bucket_B, n, openMP); + + if (num_indexes == NULL || indexes == NULL) { + pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m); + } else { + pidx = construct_BWT_indexes(T, B, bucket_A, bucket_B, n, m, num_indexes, indexes); + } + + /* Copy to output string. */ + U[0] = T[n - 1]; + for(i = 0; i < pidx; ++i) { U[i + 1] = (unsigned char)B[i]; } + for(i += 1; i < n; ++i) { U[i] = (unsigned char)B[i]; } + pidx += 1; + } else { + pidx = -2; + } + + free(bucket_B); + free(bucket_A); + if(A == NULL) { free(B); } + + return pidx; +} diff --git a/dictBuilder/config.h b/lib/divsufsort.h similarity index 52% rename from dictBuilder/config.h rename to lib/divsufsort.h index c2925d335..dac093669 100644 --- a/dictBuilder/config.h +++ b/lib/divsufsort.h @@ -1,5 +1,5 @@ /* - * config.h for libdivsufsort + * divsufsort.h for libdivsufsort-lite * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. * * Permission is hereby granted, free of charge, to any person @@ -24,60 +24,44 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef _CONFIG_H -#define _CONFIG_H 1 +#ifndef _DIVSUFSORT_H +#define _DIVSUFSORT_H 1 #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -/** Define to the version of this package. **/ -#define PROJECT_VERSION_FULL "2.0.1" -/** Define to 1 if you have the header files. **/ -#define HAVE_INTTYPES_H 1 -#define HAVE_STDDEF_H 1 -#define HAVE_STDINT_H 1 -#define HAVE_STDLIB_H 1 -#define HAVE_STRING_H 1 -#define HAVE_STRINGS_H 1 -#define HAVE_MEMORY_H 1 -#define HAVE_SYS_TYPES_H 1 +/*- Prototypes -*/ -/** for WinIO **/ -/* #undef HAVE_IO_H */ -/* #undef HAVE_FCNTL_H */ -/* #undef HAVE__SETMODE */ -/* #undef HAVE_SETMODE */ -/* #undef HAVE__FILENO */ -/* #undef HAVE_FOPEN_S */ -/* #undef HAVE__O_BINARY */ -/* -#ifndef HAVE__SETMODE -# if HAVE_SETMODE -# define _setmode setmode -# define HAVE__SETMODE 1 -# endif -# if HAVE__SETMODE && !HAVE__O_BINARY -# define _O_BINARY 0 -# define HAVE__O_BINARY 1 -# endif -#endif -*/ +/** + * Constructs the suffix array of a given string. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array of suffixes. + * @param n The length of the given string. + * @param openMP enables OpenMP optimization. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +int +divsufsort(const unsigned char *T, int *SA, int n, int openMP); -/** for inline **/ -#ifndef INLINE -# define INLINE inline -#endif - -/** for VC++ warning **/ -#ifdef _MSC_VER -#pragma warning(disable: 4127) -#endif +/** + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @param num_indexes The length of secondary indexes array. (can be NULL) + * @param indexes The secondary indexes array. (can be NULL) + * @param openMP enables OpenMP optimization. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +int +divbwt(const unsigned char *T, unsigned char *U, int *A, int n, unsigned char * num_indexes, int * indexes, int openMP); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ -#endif /* _CONFIG_H */ +#endif /* _DIVSUFSORT_H */ diff --git a/lib/mem.h b/lib/mem.h index a6606e51d..0e357e530 100644 --- a/lib/mem.h +++ b/lib/mem.h @@ -119,11 +119,12 @@ MEM_STATIC unsigned MEM_isLittleEndian(void) #if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2) -/* violates C standard on structure alignment. +/* violates C standard, by lying on structure alignment. Only use if no other choice to achieve best performance on target platform */ MEM_STATIC U16 MEM_read16(const void* memPtr) { return *(const U16*) memPtr; } MEM_STATIC U32 MEM_read32(const void* memPtr) { return *(const U32*) memPtr; } MEM_STATIC U64 MEM_read64(const void* memPtr) { return *(const U64*) memPtr; } +MEM_STATIC U64 MEM_readST(const void* memPtr) { return *(const size_t*) memPtr; } MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } MEM_STATIC void MEM_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } @@ -133,11 +134,12 @@ MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(U64*)memPtr = value; } /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; U64 u64; } __attribute__((packed)) unalign; +typedef union { U16 u16; U32 u32; U64 u64; size_t st; } __attribute__((packed)) unalign; MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign*)ptr)->u64; } +MEM_STATIC U64 MEM_readST(const void* ptr) { return ((const unalign*)ptr)->st; } MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } MEM_STATIC void MEM_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; } @@ -163,6 +165,11 @@ MEM_STATIC U64 MEM_read64(const void* memPtr) U64 val; memcpy(&val, memPtr, sizeof(val)); return val; } +MEM_STATIC size_t MEM_readST(const void* memPtr) +{ + size_t val; memcpy(&val, memPtr, sizeof(val)); return val; +} + MEM_STATIC void MEM_write16(void* memPtr, U16 value) { memcpy(memPtr, &value, sizeof(value)); diff --git a/lib/zstd.h b/lib/zstd.h index ce56c6351..53ed69739 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -61,7 +61,7 @@ extern "C" { ***************************************/ #define ZSTD_VERSION_MAJOR 0 /* for breaking interface changes */ #define ZSTD_VERSION_MINOR 5 /* for new (non-breaking) interface capabilities */ -#define ZSTD_VERSION_RELEASE 0 /* for tweaks, bug-fixes, or development */ +#define ZSTD_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */ #define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE) ZSTDLIB_API unsigned ZSTD_versionNumber (void); diff --git a/lib/zstd_compress.c b/lib/zstd_compress.c index 39e996c2d..83337b901 100644 --- a/lib/zstd_compress.c +++ b/lib/zstd_compress.c @@ -48,7 +48,7 @@ #endif -/* ************************************* +/*-************************************* * Dependencies ***************************************/ #include /* malloc */ @@ -59,46 +59,21 @@ #include "zstd_internal.h" -/* ************************************* +/*-************************************* * Constants ***************************************/ static const U32 g_searchStrength = 8; -/* ************************************* +/*-************************************* * Helper functions ***************************************/ size_t ZSTD_compressBound(size_t srcSize) { return FSE_compressBound(srcSize) + 12; } -/* ************************************* +/*-************************************* * Sequence storage ***************************************/ -typedef struct { - void* buffer; - U32* offsetStart; - U32* offset; - BYTE* offCodeStart; - BYTE* offCode; - BYTE* litStart; - BYTE* lit; - BYTE* litLengthStart; - BYTE* litLength; - BYTE* matchLengthStart; - BYTE* matchLength; - BYTE* dumpsStart; - BYTE* dumps; - /* opt */ - U32* matchLengthFreq; - U32* litLengthFreq; - U32* litFreq; - U32* offCodeFreq; - U32 matchLengthSum; - U32 litLengthSum; - U32 litSum; - U32 offCodeSum; -} seqStore_t; - static void ZSTD_resetFreqs(seqStore_t* ssPtr) { unsigned u; @@ -129,7 +104,7 @@ static void ZSTD_resetSeqStore(seqStore_t* ssPtr) } -/* ************************************* +/*-************************************* * Context memory management ***************************************/ struct ZSTD_CCtx_s @@ -159,7 +134,6 @@ struct ZSTD_CCtx_s FSE_CTable litlengthCTable [FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)]; }; - ZSTD_CCtx* ZSTD_createCCtx(void) { return (ZSTD_CCtx*) calloc(1, sizeof(ZSTD_CCtx)); @@ -172,14 +146,19 @@ size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx) return 0; /* reserved as a potential error code in the future */ } +seqStore_t ZSTD_copySeqStore(const ZSTD_CCtx* ctx) +{ + return ctx->seqStore; +} + static unsigned ZSTD_highbit(U32 val); #define CLAMP(val,min,max) { if (valmax) val=max; } -/** ZSTD_validateParams() - correct params value to remain within authorized range - optimize for srcSize if srcSize > 0 */ +/** ZSTD_validateParams() : + correct params value to remain within authorized range, + optimize for `srcSize` if srcSize > 0 */ void ZSTD_validateParams(ZSTD_parameters* params) { const U32 btPlus = (params->strategy == ZSTD_btlazy2) || (params->strategy == ZSTD_opt_bt); @@ -800,33 +779,9 @@ MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const B } -/* ************************************* +/*-************************************* * Match length counter ***************************************/ -static size_t ZSTD_read_ARCH(const void* p) { size_t r; memcpy(&r, p, sizeof(r)); return r; } - -static unsigned ZSTD_highbit(U32 val) -{ -# if defined(_MSC_VER) /* Visual */ - unsigned long r=0; - _BitScanReverse(&r, val); - return (unsigned)r; -# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ - return 31 - __builtin_clz(val); -# else /* Software version */ - static const int DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; - U32 v = val; - int r; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - r = DeBruijnClz[(U32)(v * 0x07C4ACDDU) >> 27]; - return r; -# endif -} - static unsigned ZSTD_NbCommonBytes (register size_t val) { if (MEM_isLittleEndian()) { @@ -891,12 +846,11 @@ static size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLim const BYTE* const pStart = pIn; while ((pIn(b) ? (a) : (b)) -/* ************************************* +/*-************************************* * Common constants ***************************************/ #define ZSTD_MAGICNUMBER 0xFD2FB525 /* v0.5 */ @@ -130,9 +126,58 @@ MEM_STATIC void ZSTD_wildcopy(void* dst, const void* src, size_t length) while (op < oend); } - -#if defined (__cplusplus) +MEM_STATIC unsigned ZSTD_highbit(U32 val) +{ +# if defined(_MSC_VER) /* Visual */ + unsigned long r=0; + _BitScanReverse(&r, val); + return (unsigned)r; +# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ + return 31 - __builtin_clz(val); +# else /* Software version */ + static const int DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; + U32 v = val; + int r; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + r = DeBruijnClz[(U32)(v * 0x07C4ACDDU) >> 27]; + return r; +# endif } -#endif + + +/*-******************************************* +* Private interfaces +*********************************************/ +typedef struct { + void* buffer; + U32* offsetStart; + U32* offset; + BYTE* offCodeStart; + BYTE* offCode; + BYTE* litStart; + BYTE* lit; + BYTE* litLengthStart; + BYTE* litLength; + BYTE* matchLengthStart; + BYTE* matchLength; + BYTE* dumpsStart; + BYTE* dumps; + /* opt */ + U32* matchLengthFreq; + U32* litLengthFreq; + U32* litFreq; + U32* offCodeFreq; + U32 matchLengthSum; + U32 litLengthSum; + U32 litSum; + U32 offCodeSum; +} seqStore_t; + +seqStore_t ZSTD_copySeqStore(const ZSTD_CCtx* ctx); + #endif /* ZSTD_CCOMMON_H_MODULE */ diff --git a/lib/zstd_static.h b/lib/zstd_static.h index f31e63a28..f695ccc06 100644 --- a/lib/zstd_static.h +++ b/lib/zstd_static.h @@ -205,7 +205,7 @@ ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t ds * Block functions ****************************************/ /*! Block functions produce and decode raw zstd blocks, without frame metadata. - User will have to save and regenerate necessary information to regenerate data, such as block sizes. + User will have to take in charge required information to regenerate data, such as block sizes. A few rules to respect : - Uncompressed block size must be <= 128 KB @@ -222,18 +222,17 @@ ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t ds + ZSTD_decompressBlock() doesn't accept uncompressed data as input !! */ - size_t ZSTD_compressBlock (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); -/* ************************************* +/*-************************************* * Error management ***************************************/ #include "error_public.h" /*! ZSTD_getErrorCode() : convert a `size_t` function result into a `ZSTD_error_code` enum type, - which can be used to compare directly with enum list within "error_public.h" */ + which can be used to compare directly with enum list published into "error_public.h" */ ZSTD_ErrorCode ZSTD_getError(size_t code);