sheepy

build system (sheepy) and package manager (spm) for C
git clone https://spartatek.se/git/sheepy.git
Log | Files | Refs | README | LICENSE

commit 1a7be58c4a33d902102ebfef6c38465ea37f6ff2
parent 3b81c2969575294f831d652bc0d0d6c7bcf1dea3
Author: Remy Noulin <loader2x@gmail.com>
Date:   Thu, 26 Dec 2019 08:01:35 +0100

fix dependencies compilation

create c file dependencies recursively to list all h files needed for
each c file
create header file associated which each c file is updated
copy file dates after all dependency updates are detected

src/sheepy.c | 245 +++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 173 insertions(+), 72 deletions(-)

Diffstat:
Msrc/sheepy.c | 245++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 173 insertions(+), 72 deletions(-)

diff --git a/src/sheepy.c b/src/sheepy.c @@ -53,7 +53,8 @@ int MAIN(int ARGC, char** ARGV); char *getInclude(smallStringt *L); void addFileToCompile(char *actualDir, const char *cFileName, char *oPath, smallDictt *toCompile); -void copyHeaderToBuildPath(char *actualDir, const char *cFileName, char *oPath, smallDictt *toCompile, char *bPath, struct stat hst); +void storeHeaderDep(smallArrayt *copyHeaderDepToBuildPath, char *bPath, struct stat hst); +void copyHeaderToBuildPath(char *bPath, struct stat hst); void copyHeaderDates(char *actualDir, smallArrayt *hDeps, char *buildPath); bool symlinkFile(char *path, char *buildPath); bool isC(const char *path); @@ -257,7 +258,7 @@ int MAIN(int ARGC, char** ARGV) { printf(" Current libsheepy location is " UDL "%s" RST "\n", libsheepyDir); } putsG(" The configuration is located in " UDL HOME_DIR CONFIG_NAME RST); - char *dum = expandHome(HOME_DIR CONFIG_NAME);; + char *dum = expandHome(HOME_DIR CONFIG_NAME); if (!fileExists(dum)) { printf(" %s missing.", dum); } @@ -518,7 +519,7 @@ int MAIN(int ARGC, char** ARGV) { iReplaceS(&s1, "ClassTemplate", dum, 0); iReplaceS(&s2, "ClassTemplate", dum, 0); - char *DUM = upperS(dum);; + char *DUM = upperS(dum); iReplaceS(&s, "CLASSTEMPLATE", DUM, 0); free(DUM); @@ -612,7 +613,7 @@ int MAIN(int ARGC, char** ARGV) { char *buildPath = catS(homedir, "build", actualDir); - char *buildExePath = catS(buildPath, "/", rootMain);; + char *buildExePath = catS(buildPath, "/", rootMain); //logVarG(buildPath); if (sheepyParams.showExe) { printf("The executable path is: " GRN "%s" RST @@ -633,7 +634,7 @@ int MAIN(int ARGC, char** ARGV) { readFileG(mainFile, mainFilename); // detect hash bang and remove - char *hBang = NULL;; + char *hBang = NULL; hBang = getG(mainFile, hBang, 0); if (hBang && hBang[0] == '#' && hBang[1] == '!' && findS(hBang, "sheepy")) { setG(mainFile, 0, ""); @@ -695,8 +696,8 @@ int MAIN(int ARGC, char** ARGV) { char *fN; fN = strdup(basename(s)); // add new include to inc array - char *realFPath = catS(actualDir, "/", s);; - char *buildFPath = catS(buildPath, "/", s);; + char *realFPath = catS(actualDir, "/", s); + char *buildFPath = catS(buildPath, "/", s); iNormalizePath(&realFPath); iNormalizePath(&buildFPath); // create a link to header in buildPath @@ -749,7 +750,7 @@ int MAIN(int ARGC, char** ARGV) { if (!hasG(dependencies, ssGet(L))) { // scan the package header to symlink includes in the build path so that the compiler find them // real path to h file - dum = catS(actualDir, "/", ssGet(L));; + dum = catS(actualDir, "/", ssGet(L)); readFileG(file, dum); free(dum); @@ -765,8 +766,8 @@ int MAIN(int ARGC, char** ARGV) { // add new include to inc array char *tmp = catS(fileDir, "/", s); iNormalizePath(&tmp); - char *realFPath = catS(actualDir, "/", tmp);; - char *buildFPath = catS(buildPath, "/", tmp);; + char *realFPath = catS(actualDir, "/", tmp); + char *buildFPath = catS(buildPath, "/", tmp); iNormalizePath(&realFPath); iNormalizePath(&buildFPath); //logVarG(fileDir); @@ -790,15 +791,15 @@ int MAIN(int ARGC, char** ARGV) { // remove sub directories in packages, the a file is located at the package root // shpPackages/ini/src/ini.h // shpPackages/ini/ini.a - smallArrayt *spl = splitG(L, "shpPackages/");; - smallStringt *last = getG(spl, rtSmallStringt, -1);; + smallArrayt *spl = splitG(L, "shpPackages/"); + smallStringt *last = getG(spl, rtSmallStringt, -1); setG(last, -1, 'a'); // last is "ini/src/ini.a" - smallArrayt *sslash = splitG(last, '/');; + smallArrayt *sslash = splitG(last, '/'); finishG(last); - char *lib = catS(getG(sslash, rtChar, 0), "/", getG(sslash, rtChar, -1));; + char *lib = catS(getG(sslash, rtChar, 0), "/", getG(sslash, rtChar, -1)); setNFreeG(spl, -1, lib); - lib = joinSG(spl, "shpPackages/");; + lib = joinSG(spl, "shpPackages/"); terminateManyG(spl,sslash); if (fileExists(lib)) { // link library only when it exists, there is no library in header only packages @@ -822,7 +823,7 @@ int MAIN(int ARGC, char** ARGV) { // scan h // real path to h file - dum = catS(actualDir, "/", ssGet(L));; + dum = catS(actualDir, "/", ssGet(L)); readFileG(file, dum); free(dum); @@ -838,8 +839,8 @@ int MAIN(int ARGC, char** ARGV) { // add new include to inc array char *tmp = catS(fileDir, "/", s); iNormalizePath(&tmp); - char *realFPath = catS(actualDir, "/", tmp);; - char *buildFPath = catS(buildPath, "/", tmp);; + char *realFPath = catS(actualDir, "/", tmp); + char *buildFPath = catS(buildPath, "/", tmp); iNormalizePath(&realFPath); iNormalizePath(&buildFPath); //logVarG(fileDir); @@ -878,7 +879,7 @@ int MAIN(int ARGC, char** ARGV) { if (!hasG(dependencies, ssGet(L))) { initiateAllocateSmallArray(&depFiles); - dum = catS(actualDir, "/", ssGet(L));; + dum = catS(actualDir, "/", ssGet(L)); if (fileExists(dum)) { readFileG(file, dum); @@ -889,8 +890,8 @@ int MAIN(int ARGC, char** ARGV) { if (s && !hasG(inc, s)) { char *tmp = catS(fileDir, "/", s); iNormalizePath(&tmp); - char *realFPath = catS(actualDir, "/", tmp);; - char *buildFPath = catS(buildPath, "/", tmp);; + char *realFPath = catS(actualDir, "/", tmp); + char *buildFPath = catS(buildPath, "/", tmp); iNormalizePath(&realFPath); iNormalizePath(&buildFPath); if (symlinkFile(realFPath, buildFPath)) { @@ -950,7 +951,7 @@ int MAIN(int ARGC, char** ARGV) { // detect execute bit // if yes then run (run) - char *run = emptySF();; + char *run = emptySF(); if (!sheepyParams.compileLib && !sheepyParams.compileOnly && access(mainFilename, X_OK) == 0) { // run @@ -978,14 +979,15 @@ int MAIN(int ARGC, char** ARGV) { // create cDependencies dict // key: c files // value: array of direct/indirect includes - //puts("--- cDependencies"); + //logP("--- cDependencies"); createAllocateSmallDict(cDependencies); if (lenG(dependencies) > 0) { char *k; iter(dependencies, depArray) { k = (char*)iterKeyG(dependencies); if (isC(k) || isCP(k)) { - cast(smallArrayt *, depsOrig, depArray) + //logVarG(k); + cast(smallArrayt *, depsOrig, depArray); // add dependencies from header file to c dependency list // to recompile when a header is updated if (isC(k)) { @@ -1004,17 +1006,30 @@ int MAIN(int ARGC, char** ARGV) { } // update depArray pointer in dependencies because it might have been updated setPG(dependencies, k, depsOrig); - cast(smallArrayt *, deps, dupG(depArray)) - // for each dep, append dep array to c dep array - // TODO check if the array is empty? - forEachSmallArray(depsOrig, dep) { - castS(depName, dep) - if (isH(ssGet(depName)) || isCH(ssGet(depName))) { - smallArrayt *thisDeps = getNDupG(dependencies, rtSmallArrayt, ssGet(depName));; - appendNSmashG(deps, thisDeps); + smallArrayt *deps = dupG(depsOrig); + // recursively add dependencies for the C file 'k' + createAllocateSmallArray(depStack); + pushG(depStack, depsOrig); + while (not isEmptyG(depStack)) { + smallArrayt *fileDeps = dequeueG(depStack, rtSmallArrayt);; + // for each dep, append dep array to c dep array + forEachSmallArray(fileDeps, dep) { + castS(depName, dep) + if (isH(ssGet(depName)) || isCH(ssGet(depName))) { + // check if the array is empty + smallArrayt *dp = getG(dependencies, rtSmallArrayt, ssGet(depName)); + if (not isEmptyG(dp)) { + pushG(depStack, dp); + smallArrayt *thisDeps = getNDupG(dependencies, rtSmallArrayt, ssGet(depName)); + appendNSmashG(deps, thisDeps); + } + finishG(dp); + } + finishG(dep); } - finishG(dep); + finishG(fileDeps); } + terminateG(depStack); // uniquify deps uniqG(deps, unusedV); // add array to cDependencies @@ -1033,12 +1048,15 @@ int MAIN(int ARGC, char** ARGV) { if (lenG(cDependencies) > 0) { const char *k; createSmallArray(copyAllHeaderDatesToDep); + // Array to store h deps to be able to copy deps after all dependencies are found + createSmallArray(copyHeaderDepToBuildPath); iter(cDependencies, depValue) { k = iterKeyG(cDependencies); + //logVarG(k); cast(smallArrayt *, hDeps, depValue) - char *realCPath = catS(actualDir, "/", k);; - char *buildCPath = catS(buildPath, "/", k);; + char *realCPath = catS(actualDir, "/", k); + char *buildCPath = catS(buildPath, "/", k); // TODO free ^^ before continue and at the end iNormalizePath(&realCPath); iNormalizePath(&buildCPath); @@ -1056,15 +1074,16 @@ int MAIN(int ARGC, char** ARGV) { setS(buildCPath, -2, 'o'); setS(buildCPath, -1, 0); } - //puts(k); //puts(buildCPath); if (fileExists(buildCPath)) { - //puts("EXISTS"); + //logP("EXISTS"); struct stat ost; pError(stat(buildCPath, &ost)) + //logVarG(cst.st_mtime); + //logVarG(ost.st_mtime); if (cst.st_mtime != ost.st_mtime) { - //puts("DIFFERENT DATES RECOMPILE"); + //logP("DIFFERENT DATES RECOMPILE"); addFileToCompile(actualDir, k, buildCPath, toCompile); // copy header dates to not recompile unecesserarily next time // change dep files after all modified files are found @@ -1075,13 +1094,54 @@ int MAIN(int ARGC, char** ARGV) { pushG (&copyAllHeaderDatesToDep, &headerInfo); free(buildCPath); continue; - // else puts("C AND O HAVE SAME DATE SKIP COMPILATION - CHECK IF H FILES ARE UPDATED"); + } + else { + // puts("C AND O HAVE SAME DATE SKIP COMPILATION - CHECK IF H FILES ARE UPDATED"); + // check if h file for this c file is modified + // if not check if h files dependencies are modified // execute the code below + + //logP("CHECK HEADER"); + // get modification time for header + struct stat hst; + dum = catS(actualDir, "/", k); + setS(dum, -1, 'h'); + if (fileExists(dum)) { + pError(stat(dum, &hst)) + free(dum); + + // get modification time for dep file + dum = catS(buildPath, "/", k, ".dep"); + setS(dum, -5, 'h'); + //logVarG(k); + //logVarG(dum); + if (fileExists(dum)) { + //puts("H EXISTS"); + // compare to header modification time in build path + struct stat bst; + pError(stat(dum, &bst)) + if (hst.st_mtime != bst.st_mtime) { + // h file for the k c file is modified, recompile the c file + addFileToCompile(actualDir, k, buildCPath, toCompile); + // copy header dates to not recompile unecesserarily next time + // change dep files after all modified files are found + createSmallArray(headerInfo); + pushG (&headerInfo, actualDir); + pushNFreeG(&headerInfo, dupG(hDeps)); + pushG (&headerInfo, buildPath); + pushG (&copyAllHeaderDatesToDep, &headerInfo); + free(buildCPath); + free(dum); + continue; + } + } + } + free(dum); } } else { // object file not found, compile c file - //puts("COMPILE"); + //logP("COMPILE"); //puts(buildCPath); addFileToCompile(actualDir, k, buildCPath, toCompile); // copy header dates to not recompile unecesserarily next time @@ -1095,6 +1155,7 @@ int MAIN(int ARGC, char** ARGV) { continue; } + bool addCFileK = no; // check if h files are modified forEachSmallArray(hDeps, dep) { castS(Dep, dep); @@ -1102,7 +1163,7 @@ int MAIN(int ARGC, char** ARGV) { // get modification time for header depName struct stat hst; - dum = catS(actualDir, "/", depName);; + dum = catS(actualDir, "/", depName); pError(stat(dum, &hst)) free(dum); @@ -1110,15 +1171,19 @@ int MAIN(int ARGC, char** ARGV) { //logVarG(depName); //logVarG(dum); if (fileExists(dum)) { - //puts("H EXISTS"); + //logP("H EXISTS"); // compare to header modification time in build path struct stat bst; pError(stat(dum, &bst)) if (hst.st_mtime != bst.st_mtime) { // header is updated - //puts("H UPDATED"); + //logP("H UPDATED"); + addCFileK = yes; char *hBP = catS(buildPath, "/", depName); - copyHeaderToBuildPath(actualDir, k, buildCPath, toCompile, hBP, hst); + // store h dep in array to copy deps after all dependencies are found + storeHeaderDep(&copyHeaderDepToBuildPath, hBP, hst); + addFileToCompile(actualDir, k, buildCPath, toCompile); + // when h file is modified, recompile the associated C/CP file if (!hasG(depName, PACKAGE_DIR)) { char *cfile = dupG(depName); @@ -1129,12 +1194,20 @@ int MAIN(int ARGC, char** ARGV) { setG(cfile, -1, 'p'); } if (fileExists(cfile)) { - addFileToCompile(actualDir, cfile, buildCPath, toCompile); + char *buildCPathForH = catS(buildPath, "/", cfile); + if (isC(k)) { + setS(buildCPathForH, -1, 'o'); + } + else { + setS(buildCPathForH, -2, 'o'); + setS(buildCPathForH, -1, 0); + } + addFileToCompile(actualDir, cfile, buildCPathForH, toCompile); + free(buildCPathForH); } free(cfile); - } - free(hBP); } + } else if (endsWithG(depName, ".a")) { // check if package is newer than executable or library // to handle case when several program/libraries use a common package @@ -1150,14 +1223,15 @@ int MAIN(int ARGC, char** ARGV) { logI("recompile source file '%s' because the existing exe is older than %s", k, depName); } char *hBP = catS(buildPath, "/", depName); - copyHeaderToBuildPath(actualDir, k, buildCPath, toCompile, hBP, hst); - free(hBP); + // store h dep in array to copy deps after all dependencies are found + storeHeaderDep(&copyHeaderDepToBuildPath, hBP, hst); + addFileToCompile(actualDir, k, buildCPath, toCompile); } } } else { // check if package is newer than the library currently being built - char *libName = catS(rootMain, ".a");; + char *libName = catS(rootMain, ".a"); if (fileExists(libName)) { struct stat bst; pError(stat(libName, &bst)) @@ -1167,8 +1241,9 @@ int MAIN(int ARGC, char** ARGV) { logI("recompile source file '%s' because the existing exe is older than %s", k, depName); } char *hBP = catS(buildPath, "/", depName); - copyHeaderToBuildPath(actualDir, k, buildCPath, toCompile, hBP, hst); - free(hBP); + // store h dep in array to copy deps after all dependencies are found + storeHeaderDep(&copyHeaderDepToBuildPath, hBP, hst); + addFileToCompile(actualDir, k, buildCPath, toCompile); } } free(libName); @@ -1178,19 +1253,32 @@ int MAIN(int ARGC, char** ARGV) { else { // new h file: compile dependencies and copy h file to build path char *hBP = catS(buildPath, "/", depName); - copyHeaderToBuildPath(actualDir, k, buildCPath, toCompile, hBP, hst); - free(hBP); + // store h dep in array to copy deps after all dependencies are found + storeHeaderDep(&copyHeaderDepToBuildPath, hBP, hst); + addFileToCompile(actualDir, k, buildCPath, toCompile); } free(dum); finishG(dep); } + if (addCFileK) { + addFileToCompile(actualDir, k, buildCPath, toCompile); + } + free(buildCPath); + //logVarG(toCompile); } // copy all header dates to dep files in build directory iter(&copyAllHeaderDatesToDep, HeaderInfo) { cast(smallArrayt*, headerInfo, HeaderInfo); copyHeaderDates(/*actualDir*/getG(headerInfo, rtChar, 0), /*hDeps*/getG(headerInfo, rtSmallArrayt, 1), /*buildPath*/getG(headerInfo, rtChar, 2)); } + iter(&copyHeaderDepToBuildPath, HeaderInfo) { + cast(smallContainert*,headerInfo, HeaderInfo); + srcFilet *srcI = O(headerInfo,get);; + copyHeaderToBuildPath(srcI->bpath, srcI->st); + free(srcI->bpath); + free(srcI); + } freeG(&copyAllHeaderDatesToDep); } @@ -1352,7 +1440,7 @@ int MAIN(int ARGC, char** ARGV) { // load package.yml dum = catS(dir,"/", PACKAGE); - smallDictt *pkgD = NULL;; + smallDictt *pkgD = NULL; if (fileExists(dum)) { createAllocateSmallJson(pkgInfo); readFileG(pkgInfo, dum); @@ -1369,13 +1457,13 @@ int MAIN(int ARGC, char** ARGV) { //print 'main doesnt need to be recompiled' if (sheepyParams.compileLib) { // check if the library exists - char *libname = appendS(rootMain, ".so");; + char *libname = appendS(rootMain, ".so"); if (!fileExists(libname)) { free(libname); goto linkOrClean; } free(libname); - libname = appendS(rootMain, ".a");; + libname = appendS(rootMain, ".a"); if (!fileExists(libname)) { free(libname); goto linkOrClean; @@ -1427,7 +1515,7 @@ int MAIN(int ARGC, char** ARGV) { // get compile options from user configuration - char *cflags = NULL;; + char *cflags = NULL; if (pkgD) { cflags = getG(pkgD, rtChar, cflagsKey); if (!cflags) { @@ -1540,7 +1628,7 @@ int MAIN(int ARGC, char** ARGV) { if (sheepyParams.parallel) { // compile in parallel // create argument list for child process that compile the source code - char **jobArgs = split(ccmd, " ");; + char **jobArgs = split(ccmd, " "); terminateG(ccmdL); // remove possible empty strings due to split before @@ -1570,7 +1658,7 @@ int MAIN(int ARGC, char** ARGV) { // list include objects and libraries in incObjFiles char *incObjFiles; // collect lflags from packages - char *packageLflags = NULL;; + char *packageLflags = NULL; createAllocateSmallArray(incList); forEachSmallArray(inc, incL) { castS(L, incL) @@ -1598,7 +1686,7 @@ int MAIN(int ARGC, char** ARGV) { // the package is compiled at the package root // remove the the subdirectories in L smallStringt *sp = allocG(s); - smallArrayt *arr = splitG(sp, "/");; + smallArrayt *arr = splitG(sp, "/"); terminateG(sp); int keepIndex; @@ -1627,7 +1715,7 @@ int MAIN(int ARGC, char** ARGV) { } // collect lflags for this package - char *pkg = shDirnameG(s);; + char *pkg = shDirnameG(s); iAppendManyS(&pkg, "/", PACKAGE); //logVarG(pkg); createAllocateSmallJson(pkgJ); @@ -1706,7 +1794,7 @@ int MAIN(int ARGC, char** ARGV) { } rmAll(exePath); } - char *libname = appendS(rootMain, ".so");; + char *libname = appendS(rootMain, ".so"); if (fileExists(libname)) { if (getG(cfgD, rtBool, "print_compile_commands")) { printf("rm %s", libname); @@ -1715,7 +1803,7 @@ int MAIN(int ARGC, char** ARGV) { rmAll(libname); } free(libname); - libname = appendS(rootMain, ".a");; + libname = appendS(rootMain, ".a"); if (fileExists(libname)) { if (getG(cfgD, rtBool, "print_compile_commands")) { printf("rm %s", libname); @@ -1788,7 +1876,7 @@ int MAIN(int ARGC, char** ARGV) { lflags = ""; } - lflags = dupG(lflags);; + lflags = dupG(lflags); if (!isBlankG(packageLflags)) { appendG(&lflags, packageLflags); @@ -1872,7 +1960,7 @@ int MAIN(int ARGC, char** ARGV) { iter(toCompile, info) { cast(smallContainert *, infoC, info) srcFilet *srcF = getG(infoC, rtVoid, 0); - //print srcF->fpath + //logVarG(srcF->fpath); if (isH(srcF->bpath) || isCH(srcF->bpath)) { char *dep = catS(srcF->bpath, ".dep"); @@ -1890,6 +1978,7 @@ int MAIN(int ARGC, char** ARGV) { logW("'%s' not found.", srcF->bpath); } else { + //logP("SET TIME FOR %s", srcF->bpath); pError0(setModificationTime(srcF->bpath, srcF->st.st_mtime)) { // bug in cg_c } @@ -1960,23 +2049,35 @@ void addFileToCompile(char *actualDir, const char *cFileName, char *oPath, small // add c file to compile list if (!hasG(toCompile, cFileName)) { + //logP("ADD TO TOCOMPILE"); + //logVarG(actualDir); + //logVarG(cFileName); + //logVarG(oPath); srcFilet *srcI; // allocate srcFile info, set later srcI = malloc(sizeof(srcFilet)); - char *tmp = catS(actualDir, "/", cFileName);; + char *tmp = catS(actualDir, "/", cFileName); pError(stat(tmp, &(srcI->st))) srcI->fpath = strdup(tmp); srcI->bpath = strdup(oPath); free(tmp); - smallContainert *srcFInfo = allocSmallContainer(srcI);; + smallContainert *srcFInfo = allocSmallContainer(srcI); setNFreeG(toCompile, cFileName, srcFInfo); } } +void storeHeaderDep(smallArrayt *copyHeaderDepToBuildPath, char *bPath, struct stat hst) { + srcFilet *srcI = NULL; -void copyHeaderToBuildPath(char *actualDir, const char *cFileName, char *oPath, smallDictt *toCompile, char *bPath, struct stat hst) { + // allocate srcFile info, set later + srcI = malloc(sizeof(srcFilet)); + srcI->bpath = bPath; + srcI->st = hst; + smallContainert *srcFInfo = allocSmallContainer(srcI); + pushNFreeG(copyHeaderDepToBuildPath, srcFInfo); +} - addFileToCompile(actualDir, cFileName, oPath, toCompile); +void copyHeaderToBuildPath(char *bPath, struct stat hst) { // 'copy' header to build path, to check modification time next time // the h.dep hold the modification time of the original header @@ -1987,7 +2088,7 @@ void copyHeaderToBuildPath(char *actualDir, const char *cFileName, char *oPath, programResult += commandNFree(tmp); // copy modification time - char *b = catS(bPath, ".dep");; + char *b = catS(bPath, ".dep"); pError0(setModificationTime(b, hst.st_mtime)) { free(b); // bug in cg_c - IF in MODIFICATION @@ -2115,7 +2216,7 @@ void generateCBuffers(smallArrayt *sheepySrc, smallArrayt *cCode) { bool start = true; /* if len <= QUEX_SETTING_BUFFER_MIN_FALLBACK_N+2 */ - /* char *tmp = realloc(src-1, QUEX_SETTING_BUFFER_MIN_FALLBACK_N+3); */ + /* char *tmp = realloc(src-1, QUEX_SETTING_BUFFER_MIN_FALLBACK_N+3) */ /* len = QUEX_SETTING_BUFFER_MIN_FALLBACK_N+3 */ /* src = tmp+1 */