From 7d3602807891e54673867524821a2fa4a9709cc3 Mon Sep 17 00:00:00 2001 From: Yann Collet <yann.collet.73@gmail.com> 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 | 354 ++-- 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, 2661 insertions(+), 3156 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 <stdlib.h> /* exit, calloc, free */ #include <string.h> /* strcmp, strlen */ #include <stdio.h> /* 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 <stdlib.h> /* malloc, free */ +#include <string.h> /* memset */ +#include <stdio.h> /* fprintf, fopen, ftello64 */ +#include <sys/types.h> /* stat64 */ +#include <sys/stat.h> /* stat64 */ +#include <time.h> /* 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; u<length; u++) { + BYTE c = b[u]; + if (c<32 || c>126) 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<nbFiles; n++) + total += DiB_getFileSize(fileNamesTable[n]); + return total; +} + + +static void DiB_loadFiles(void* buffer, size_t bufferSize, + size_t* fileSizes, + const char** fileNamesTable, unsigned nbFiles) +{ + char* buff = (char*)buffer; + size_t pos = 0; + unsigned n; + + for (n=0; n<nbFiles; n++) { + size_t readSize; + unsigned long long fileSize = DiB_getFileSize(fileNamesTable[n]); + FILE* f = fopen(fileNamesTable[n], "rb"); + if (f==NULL) EXM_THROW(10, "impossible to open file %s", fileNamesTable[n]); + DISPLAYLEVEL(2, "Loading %s... \r", fileNamesTable[n]); + if (fileSize > 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<length; p++) { + acc *= PRIME2; + ((unsigned char*)buffer)[p] = (unsigned char)(acc >> 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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 Xnn*h->!a-xKTVJ2qh8TvnyCK^?B0R| literal 0 HcmV?d00001 diff --git a/dictBuilder/divsufsort.c b/dictBuilder/divsufsort.c deleted file mode 100644 index 312813597..000000000 --- a/dictBuilder/divsufsort.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * divsufsort.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 - -/*- Dependencies -*/ -#include "divsufsort_private.h" -#ifdef _OPENMP -# include <omp.h> -#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 <inttypes.h> - -#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 <assert.h> -#include <stdlib.h> /* unconditional */ -#include <stdio.h> -#include "config.h" /* unconditional */ - - -#if HAVE_STRING_H -# include <string.h> -#endif -#if HAVE_MEMORY_H -# include <memory.h> -#endif -#if HAVE_STDDEF_H -# include <stddef.h> -#endif -#if HAVE_STRINGS_H -# ifdef _WIN32 -# include <string.h> -# else -# include <strings.h> -# endif -#endif -#if HAVE_INTTYPES_H -# include <inttypes.h> -#else -# if HAVE_STDINT_H -# include <stdint.h> -# 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 - - 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. + 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://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 <stdlib.h> /* malloc, free */ #include <string.h> /* 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<nbFiles; n++) - total += DiB_getFileSize(fileNamesTable[n]); - return total; -} - - -static void DiB_loadFiles(void* buffer, size_t bufferSize, - size_t* fileSizes, - const char** fileNamesTable, unsigned nbFiles) -{ - char* buff = (char*)buffer; - size_t pos = 0; - unsigned n; - - for (n=0; n<nbFiles; n++) { - size_t readSize; - unsigned long long fileSize = DiB_getFileSize(fileNamesTable[n]); - FILE* f = fopen(fileNamesTable[n], "rb"); - if (f==NULL) EXM_THROW(10, "impossible to open file %s", fileNamesTable[n]); - DISPLAYLEVEL(2, "Loading %s... \r", fileNamesTable[n]); - if (fileSize > 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<dictList[0].pos; u++) @@ -564,32 +518,32 @@ static U32 DiB_dictSize(const dictItem* dictList) } -static void DiB_trainBuffer(dictItem* dictList, U32 dictListSize, +static void ZDICT_trainBuffer(dictItem* dictList, U32 dictListSize, const void* const buffer, const size_t bufferSize, /* buffer must end with noisy guard band */ const size_t* fileSizes, unsigned nbFiles, U32 shiftRatio, unsigned maxDictSize) { - saidx_t* const suffix0 = (saidx_t*)malloc((bufferSize+2)*sizeof(*suffix0)); - saidx_t* const suffix = suffix0+1; + int* const suffix0 = (int*)malloc((bufferSize+2)*sizeof(*suffix0)); + int* const suffix = suffix0+1; U32* reverseSuffix = (U32*)malloc((bufferSize)*sizeof(*reverseSuffix)); BYTE* doneMarks = (BYTE*)malloc((bufferSize+16)*sizeof(*doneMarks)); /* +16 for overflow security */ U32* filePos = (U32*)malloc(nbFiles * sizeof(*filePos)); U32 minRatio = nbFiles >> 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; u<nbFiles; u++) { - DiB_countEStats(esr, + ZDICT_countEStats(esr, countLit, offcodeCount, matchLengthCount, litlengthCount, (const char*)srcBuffer + pos, fileSizes[u]); pos += fileSizes[u]; @@ -794,33 +731,16 @@ static size_t DiB_analyzeEntropy(void* dstBuffer, size_t maxDstSize, } -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) -} - - #define DIB_FASTSEGMENTSIZE 64 -/*! DiB_fastSampling (based on an idea by Giuseppe Ottaviano) - Fill @dictBuffer with stripes of size DIB_FASTSEGMENTSIZE from @samplesBuffer - up to @dictSize. - Filling starts from the end of @dictBuffer, down to maximum possible. - if @dictSize is not a multiply of DIB_FASTSEGMENTSIZE, some bytes at beginning of @dictBuffer won't be used. - @return : amount of data written into @dictBuffer - or an error Code (if @dictSize or @samplesSize too small) +/*! ZDICT_fastSampling (based on an idea by Giuseppe Ottaviano) + Fill `dictBuffer` with stripes of size DIB_FASTSEGMENTSIZE from `samplesBuffer` + up to `dictSize`. + Filling starts from the end of `dictBuffer`, down to maximum possible. + if `dictSize` is not a multiply of DIB_FASTSEGMENTSIZE, some bytes at beginning of `dictBuffer` won't be used. + @return : amount of data written into `dictBuffer` + or an error code */ -static size_t DiB_fastSampling(void* dictBuffer, size_t dictSize, +static size_t ZDICT_fastSampling(void* dictBuffer, size_t dictSize, const void* samplesBuffer, size_t samplesSize) { char* dstPtr = (char*)dictBuffer + dictSize; @@ -851,10 +771,10 @@ static size_t DiB_fastSampling(void* dictBuffer, size_t dictSize, } -static size_t DiB_trainFromBuffer_internal( +size_t ZDICT_trainFromBuffer_unsafe( void* dictBuffer, size_t maxDictSize, const void* samplesBuffer, const size_t* sampleSizes, unsigned nbSamples, - DiB_params_t params) + ZDICT_params_t params) { const U32 dictListSize = MAX( MAX(DICTLISTSIZE, nbSamples), (U32)(maxDictSize/16)); dictItem* dictList = (dictItem*)malloc(dictListSize * sizeof(*dictList)); @@ -869,14 +789,14 @@ static size_t DiB_trainFromBuffer_internal( /* init */ { unsigned u; for (u=0, sBuffSize=0; u<nbSamples; u++) sBuffSize += sampleSizes[u]; } - if (!dictList) { DISPLAYLEVEL(1, "not enough memory for DiB_trainFromBuffer"); return ERROR(memory_allocation); } - DiB_initDictItem(dictList); + if (!dictList) return ERROR(memory_allocation); + ZDICT_initDictItem(dictList); if (selectivity==0) selectivity = g_selectivity_default; if (compressionLevel==0) compressionLevel = g_compressionLevel_default; - /* select stripes */ - if (selectivity>1) { - 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<nbSamples; u++) sBuffSize += sampleSizes[u]; } + { unsigned u; for (u=0, sBuffSize=0; u<nbSamples; u++) sBuffSize += samplesSizes[u]; } newBuff = malloc(sBuffSize + NOISELENGTH); if (!newBuff) return ERROR(memory_allocation); memcpy(newBuff, samplesBuffer, sBuffSize); - DiB_fillNoise((char*)newBuff + sBuffSize, NOISELENGTH); /* guard band, for end of buffer condition */ + ZDICT_fillNoise((char*)newBuff + sBuffSize, NOISELENGTH); /* guard band, for end of buffer condition */ - result = DiB_trainFromBuffer_internal(dictBuffer, maxDictSize, - newBuff, sampleSizes, nbSamples, + result = ZDICT_trainFromBuffer_unsafe(dictBuffer, dictBufferCapacity, + newBuff, samplesSizes, nbSamples, params); free(newBuff); return result; } -int DiB_trainFromFiles(const char* dictFileName, unsigned maxDictSize, - const char** fileNamesTable, unsigned nbFiles, - DiB_params_t params) +/* issue : samplesBuffer need to be followed by a noisy guard band. +* work around : duplicate the buffer, and add the noise ? */ +size_t ZDICT_trainFromBuffer(void* dictBuffer, size_t dictBufferCapacity, + const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples) { - 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 = 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 <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#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 -*/ - -/** for inline **/ -#ifndef INLINE -# define INLINE inline -#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 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 <stdlib.h> /* 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 (val<min) val=min; else if (val>max) 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<pInLimit-(sizeof(size_t)-1))) { - size_t diff = ZSTD_read_ARCH(pMatch) ^ ZSTD_read_ARCH(pIn); + size_t diff = MEM_readST(pMatch) ^ MEM_readST(pIn); if (!diff) { pIn+=sizeof(size_t); pMatch+=sizeof(size_t); continue; } pIn += ZSTD_NbCommonBytes(diff); return (size_t)(pIn - pStart); } - if (MEM_64bits()) if ((pIn<(pInLimit-3)) && (MEM_read32(pMatch) == MEM_read32(pIn))) { pIn+=4; pMatch+=4; } if ((pIn<(pInLimit-1)) && (MEM_read16(pMatch) == MEM_read16(pIn))) { pIn+=2; pMatch+=2; } if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++; @@ -904,7 +858,7 @@ static size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLim } /** ZSTD_count_2segments() : -* can count match length with ip & match in potentially 2 different segments. +* can count match length with `ip` & `match` in 2 different segments. * convention : on reaching mEnd, match count continue starting from iStart */ static size_t ZSTD_count_2segments(const BYTE* ip, const BYTE* match, const BYTE* iEnd, const BYTE* mEnd, const BYTE* iStart) diff --git a/lib/zstd_internal.h b/lib/zstd_internal.h index ca291bd53..318fd55f6 100644 --- a/lib/zstd_internal.h +++ b/lib/zstd_internal.h @@ -32,11 +32,7 @@ #ifndef ZSTD_CCOMMON_H_MODULE #define ZSTD_CCOMMON_H_MODULE -#if defined (__cplusplus) -extern "C" { -#endif - -/* ************************************* +/*-************************************* * Dependencies ***************************************/ #include "mem.h" @@ -44,14 +40,14 @@ extern "C" { #include "zstd_static.h" -/* ************************************* +/*-************************************* * Common macros ***************************************/ #define MIN(a,b) ((a)<(b) ? (a) : (b)) #define MAX(a,b) ((a)>(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); -- GitLab