diff --git a/mrf_apps/mrf_insert.cpp b/mrf_apps/mrf_insert.cpp index f26e7a6..66b610c 100644 --- a/mrf_apps/mrf_insert.cpp +++ b/mrf_apps/mrf_insert.cpp @@ -1,39 +1,39 @@ /* -* Copyright (c) 2002-2015, California Institute of Technology. -* All rights reserved. Based on Government Sponsored Research under contracts NAS7-1407 and/or NAS7-03001. -* Redistribution and use in source and binary forms, with or without modification, are permitted provided -* that the following conditions are met: -* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and -* the following disclaimer. -* 2. 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. -* 3. Neither the name of the California Institute of Technology (Caltech), its operating division the -* Jet Propulsion Laboratory (JPL), the National Aeronautics and Space Administration (NASA), -* nor the names of its contributors may be used to endorse or promote products derived from this software -* without specific prior written permission. -* -* 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 CALIFORNIA INSTITUTE OF TECHNOLOGY 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. -* -* Copyright 2015 Esri -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Copyright (c) 2002-2015, California Institute of Technology. + * All rights reserved. Based on Government Sponsored Research under contracts NAS7-1407 and/or NAS7-03001. + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and + * the following disclaimer. + * 2. 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. + * 3. Neither the name of the California Institute of Technology (Caltech), its operating division the + * Jet Propulsion Laboratory (JPL), the National Aeronautics and Space Administration (NASA), + * nor the names of its contributors may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 CALIFORNIA INSTITUTE OF TECHNOLOGY 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. + * + * Copyright 2015 Esri + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "mrf_insert.h" @@ -41,7 +41,8 @@ using namespace std; USING_NAMESPACE_MRF // Size and location from handle -img_info::img_info(GDALDatasetH hDS) { +img_info::img_info(GDALDatasetH hDS) +{ double adfGT[6]; GDALGetGeoTransform(hDS, adfGT); @@ -57,15 +58,13 @@ img_info::img_info(GDALDatasetH hDS) { res.y = adfGT[5]; } - static bool outside_bounds(const Bounds &inside, const Bounds &outside, XY tolerance) { return ( - inside.lx + tolerance.x < outside.lx - || inside.ux - tolerance.x > outside.ux - || inside.ly + tolerance.y < outside.ly - || inside.uy - tolerance.y > outside.uy - ); + inside.lx + tolerance.x < outside.lx || + inside.ux - tolerance.x > outside.ux || + inside.ly + tolerance.y < outside.ly || + inside.uy - tolerance.y > outside.uy); } // @@ -73,57 +72,67 @@ static bool outside_bounds(const Bounds &inside, const Bounds &outside, XY toler // Only works with read currently // CPLErr ClippedRasterIO(GDALRasterBand *band, GDALRWFlag eRWFlag, - int nXOff, int nYOff, - int nXSize, int nYSize, - void * pData, - GDALDataType eBufType, - int nPixelSpace, - int nLineSpace) + int nXOff, int nYOff, + int nXSize, int nYSize, + void *pData, + GDALDataType eBufType, + int nPixelSpace, + int nLineSpace) { CPLAssert(GF_Read == eRWFlag); auto pcData = reinterpret_cast(pData); - if (nXOff < 0 || nXOff + nXSize > band->GetXSize()) { // Clip needed on X - if (nXOff < 0) { // Adjust the start of the line - // nXoff is negative, so this is addition - pcData -= nXOff * nPixelSpace; - nXSize += nXOff; // XOff is negative, so this is a subtraction - nXOff = 0; - } - if (nXOff + nXSize > band->GetXSize()) {// Clip end of lines - nXSize = band->GetXSize() - nXOff; - } + if (nXOff < 0) + { + // Adjust the start of the line + // nXoff is negative, so this is addition + pcData -= nXOff * nPixelSpace; + nXSize += nXOff; // XOff is negative, so this is a subtraction + nXOff = 0; + } + if (nXOff + nXSize > band->GetXSize()) + { + // Clip end of lines + nXSize = band->GetXSize() - nXOff; } - if (nYOff < 0 || nYOff + nYSize > band->GetYSize()) { // Clip needed on X - if (nYOff < 0) { // Adjust the start of the column - // nYoff is negative, so this is addition - pcData -= nYOff * nLineSpace; - nYSize += nYOff; // YOff is negative, so this is a subtraction - nYOff = 0; - } - if (nYOff + nYSize > band->GetYSize()) // Clip end of columns - nYSize = band->GetYSize() - nYOff; + if (nYOff < 0) + { + // Adjust the start of the column + // nYoff is negative, so this is addition + pcData -= nYOff * nLineSpace; + nYSize += nYOff; // YOff is negative, so this is a subtraction + nYOff = 0; + } + if (nYOff + nYSize > band->GetYSize()) + { + // Clip end of columns + nYSize = band->GetYSize() - nYOff; } // Call the raster band read with the trimmed values return band->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, - pData, nXSize, nYSize, eBufType, nPixelSpace, nLineSpace, NULL); + pcData, nXSize, nYSize, eBufType, nPixelSpace, nLineSpace, NULL); } // Insert the target in the base level -bool state::patch() { +bool state::patch() +{ if (TargetName.empty()) + { return false; + } // These are the same thing, the handle for the C functions, the dataset class for C++ - union { + union + { GDALDatasetH hDataset; GDALDataset *pTDS; MRFDataset *pTarg; }; - union { + union + { GDALDatasetH hPatch; GDALDataset *pSDS; }; @@ -132,14 +141,17 @@ bool state::patch() { hDataset = GDALOpen(TargetName.c_str(), GA_Update); CPLPopErrorHandler(); - if (hDataset == NULL) { + if (hDataset == NULL) + { CPLError(CE_Failure, CPLE_AppDefined, "Can't open file %s for update", TargetName.c_str()); return false; } - try { + try + { // GetDescription is the driver name, uppercase - if (!EQUAL(pTDS->GetDriver()->GetDescription(), "MRF")) { + if (!EQUAL(pTDS->GetDriver()->GetDescription(), "MRF")) + { CPLError(CE_Failure, CPLE_AppDefined, "Target file is not an MRF"); throw 1; } @@ -148,14 +160,16 @@ bool state::patch() { hPatch = GDALOpen(SourceName.c_str(), GA_ReadOnly); CPLPopErrorHandler(); - if (hPatch == NULL) { + if (hPatch == NULL) + { CPLError(CE_Failure, CPLE_AppDefined, "Can't open file %s", SourceName.c_str()); throw 1; - }; - + } } - catch (int e) { - if (e > 0) GDALClose(hDataset); + catch (int e) + { + if (e > 0) + GDALClose(hDataset); return false; } @@ -164,62 +178,73 @@ bool state::patch() { int overview_count = 0; void *buffer = NULL; - try { + try + { img_info in_img(hPatch); img_info out_img(hDataset); - // Tolerance of 1/100 of an output pixel + + if (verbose != 0) + { + cerr << "Out " << out_img.bbox << endl + << "In " << in_img.bbox << endl; + } + + // Tolerance of 1/2 of an output pixel XY tolerance; - tolerance.x = fabs(out_img.res.x / 100); - tolerance.y = fabs(out_img.res.y / 100); + tolerance.x = fabs(out_img.res.x / 2); + tolerance.y = fabs(out_img.res.y / 2); XY factor; factor.x = in_img.res.x / out_img.res.x; factor.y = in_img.res.y / out_img.res.y; - if (!CPLIsEqual(factor.x,factor.y)) { + if (!CPLIsEqual(factor.x, factor.y)) + { CPLError(CE_Failure, CPLE_AppDefined, "Scaling factor for X and Y are not the same"); throw 2; } - if (verbose > 0) - cerr << "Out " << out_img.bbox << endl << "In " << in_img.bbox << endl; - - if (outside_bounds(in_img.bbox, out_img.bbox, tolerance)) { + if (outside_bounds(in_img.bbox, out_img.bbox, tolerance)) + { CPLError(CE_Failure, CPLE_AppDefined, "Input patch outside of target"); throw 2; } - // Get the first band, which always exists, use it to collect some info + // tolerance of 1/1000 of the resolution + if ((fabs(in_img.res.x - out_img.res.x) * 1000 > fabs(out_img.res.x)) || + (fabs(in_img.res.y - out_img.res.y) * 1000 > fabs(out_img.res.y))) + { + CPLError(CE_Failure, CPLE_AppDefined, "Source and target resolutions don't match"); + throw 2; + } + + // Get the first band from the output MRF, which always exists, use it to + // collect band count and block size GDALRasterBand *b0 = pTDS->GetRasterBand(1); int bands = pTDS->GetRasterCount(); int tsz_x, tsz_y; b0->GetBlockSize(&tsz_x, &tsz_y); + GDALDataType eDataType = b0->GetRasterDataType(); overview_count = b0->GetOverviewCount(); int pixel_size = GDALGetDataTypeSize(eDataType) / 8; // Bytes per pixel per band - int line_size = tsz_x * pixel_size; // A line has this many bytes - int buffer_size = line_size * tsz_y; // A block size in bytes - - // tolerance of 1/1000 of the resolution - if ((fabs(in_img.res.x - out_img.res.x) * 1000 > fabs(out_img.res.x)) || - (fabs(in_img.res.y - out_img.res.y) * 1000 > fabs(out_img.res.y))) - { - CPLError(CE_Failure, CPLE_AppDefined, "Source and target resolutions don't match"); - throw 2; - } + int line_size = tsz_x * pixel_size; // A line has this many bytes + int buffer_size = line_size * tsz_y; // A block size in bytes // - // Location in target pixels + // Location in target (output MRF) pixels pix_bbox.lx = int((in_img.bbox.lx - out_img.bbox.lx) / in_img.res.x + 0.5); pix_bbox.ux = int((in_img.bbox.ux - out_img.bbox.lx) / in_img.res.x + 0.5); - // uy < ly + // note that uy < ly pix_bbox.uy = int((in_img.bbox.uy - out_img.bbox.uy) / in_img.res.y + 0.5); pix_bbox.ly = int((in_img.bbox.ly - out_img.bbox.uy) / in_img.res.y + 0.5); if (verbose != 0) + { cerr << "Pixel location " << pix_bbox << endl - << "Factor " << factor.x << "," << factor.y << endl; + << "Factor " << factor.x << "," << factor.y << endl; + } // First blocks to consider blocks_bbox.lx = int(pix_bbox.lx / tsz_x); @@ -230,13 +255,16 @@ bool state::patch() { blocks_bbox.uy = int(pix_bbox.uy / tsz_y); if (verbose != 0) + { cerr << "Blocks location " << blocks_bbox << endl; + } // Build a vector of output bands - vectorsrc_b; - vectordst_b; + vector src_b; + vector dst_b; - for (int band = 1; band <= bands; band++) { + for (int band = 1; band <= bands; band++) + { src_b.push_back(pSDS->GetRasterBand(band)); dst_b.push_back(pTDS->GetRasterBand(band)); } @@ -247,70 +275,92 @@ bool state::patch() { // Use the innner loop for bands, helps if output is interleaved // // Using the factor enables scaling of input - // However, the input coverage still has to be exactly on + // However, the input coverage still has to be exactly on // ouput block boundaries // if (start_level == 0) // Skip if start level is not zero - for (int y = static_cast(blocks_bbox.uy); y < static_cast(blocks_bbox.ly); y++) { + { + for (int y = static_cast(blocks_bbox.uy); y <= static_cast(blocks_bbox.ly); y++) + { // Source offset relative to this block on y - int src_offset_y = static_cast(factor.y * tsz_y * y - pix_bbox.uy + 0.5); + int src_offset_y = static_cast(factor.y * tsz_y * y - pix_bbox.uy); - for (int x = static_cast(blocks_bbox.lx); x < static_cast(blocks_bbox.ux); x++) { + for (int x = static_cast(blocks_bbox.lx); x <= static_cast(blocks_bbox.ux); x++) + { // Source offset relative to this block on x - int src_offset_x = static_cast(factor.x * tsz_x * x - pix_bbox.lx + 0.5); - for (int band = 0; band < bands; band++) { // Counting from zero in a vector - // cerr << " Y block " << y << " X block " << x << endl; + int src_offset_x = static_cast(factor.x * tsz_x * x - pix_bbox.lx); + + for (int band = 0; band < bands; band++) // Counting from zero in a vector + { + if (verbose != 0) + { + cerr << "src_offset_x = " << src_offset_x << " src_offset_y = " << src_offset_y << endl; + cerr << " Y block " << y << " X block " << x << endl; + } // READ CPLErr eErr = CE_None; // If input needs padding, initialize the buffer with destination content - if (src_offset_x < 0 || src_offset_x + tsz_x > src_b[band]->GetXSize() - && src_offset_y < 0 || src_offset_y + tsz_y > src_b[band]->GetYSize()) { - + if (src_offset_x < 0 || src_offset_x + tsz_x > src_b[band]->GetXSize() || src_offset_y < 0 || src_offset_y + tsz_y > src_b[band]->GetYSize()) + { + // Clunky solution to this problem, but GDAL API does not support padding + if (x * tsz_x == dst_b[band]->GetXSize() || y * tsz_y == dst_b[band]->GetYSize()) + { + continue; + } eErr = dst_b[band]->RasterIO(GF_Read, - x * tsz_x, y * tsz_y, // offset in output image - tsz_x, tsz_y, // Size in output image - buffer, tsz_x, tsz_y, // Buffer and size in buffer - eDataType, // Requested type - pixel_size, line_size // Pixel and line space - , NULL // ExtraIO arguments + x * tsz_x, y * tsz_y, // offset in output image + tsz_x, tsz_y, // Size in output image + buffer, tsz_x, tsz_y, // Buffer and size in buffer + eDataType, // Requested type + pixel_size, line_size, // Pixel and line space + NULL // ExtraIO arguments ); - if (CE_None != eErr) { - cerr << "Read error" << endl; + if (CE_None != eErr) + { + cerr << "Fill data read error" << endl; throw static_cast(eErr); } } - // Works just like RasterIO, except that it only reads the + // Works just like RasterIO, except that it only reads the // valid parts of the input band and has no scaling - ClippedRasterIO(src_b[band], GF_Read, - src_offset_x, src_offset_y, // offset in input image - tsz_x, tsz_y, // Size in input image - buffer, // buffer - eDataType, // Requested type - pixel_size, line_size); // Pixel and line space - - // WRITE + eErr = ClippedRasterIO(src_b[band], GF_Read, + src_offset_x, src_offset_y, // offset in input image + tsz_x, tsz_y, // Size in input image + buffer, // buffer + eDataType, // Requested type + pixel_size, line_size); // Pixel and line space + if (CE_None != eErr) + { + cerr << "Clipped rasterio read error" << endl; + throw static_cast(eErr); + } + + // WRITE eErr = dst_b[band]->RasterIO(GF_Write, - x * tsz_x, y * tsz_y, // offset in output image - tsz_x, tsz_y, // Size in output image - buffer, tsz_x, tsz_y, // Buffer and size in buffer - eDataType, // Requested type - pixel_size, line_size // Pixel and line space - , NULL // ExtraIO arguments + x * tsz_x, y * tsz_y, // offset in output image + tsz_x, tsz_y, // Size in output image + buffer, tsz_x, tsz_y, // Buffer and size in buffer + eDataType, // Requested type + pixel_size, line_size, // Pixel and line space + NULL // ExtraIO arguments ); - if (CE_None != eErr) { + if (CE_None != eErr) + { cerr << "Read error" << endl; throw static_cast(eErr); } } } } - + } CPLFree(buffer); } - catch (int e) { - if (e > 0) GDALClose(hDataset); + catch (int e) + { + if (e > 0) + GDALClose(hDataset); CPLFree(buffer); return false; } @@ -320,35 +370,27 @@ bool state::patch() { GDALFlushCache(hDataset); // Call the patchOverview for the MRF - if (overlays) { + if (overlays) + { auto BlockXOut = static_cast(blocks_bbox.lx); auto BlockYOut = static_cast(blocks_bbox.uy); - auto WidthOut = static_cast(blocks_bbox.ux - blocks_bbox.lx); - auto HeightOut = static_cast(blocks_bbox.ly - blocks_bbox.uy); + // correct off-by-one error when generating overlays + auto WidthOut = static_cast(blocks_bbox.ux - blocks_bbox.lx + 1); + auto HeightOut = static_cast(blocks_bbox.ly - blocks_bbox.uy + 1); // If stop level is not set, do all levels if (stop_level == -1) + { stop_level = overview_count; + } // convert level limits to source levels start_level--; - // Loop by source level - for (int sl = 0; sl < overview_count; sl++) { - if (sl >= start_level && sl < stop_level) { - pTarg->PatchOverview(BlockXOut, BlockYOut, WidthOut, HeightOut, - sl, false, Resampling); - GDALFlushCache(hDataset); - } - - // Scale down by two, rouding up height and width and add the block rounding - WidthOut = WidthOut / 2 + (WidthOut & 1) + (BlockXOut & 1); // Round up - HeightOut = HeightOut / 2 + (HeightOut & 1) + (BlockYOut & 1); // Round up - - // Start block always rounds down - BlockXOut = BlockXOut / 2; // Round down - BlockYOut = BlockYOut / 2; // Round down - } + // Use recursive mode to have the MRF driver handle overviews automatically + pTarg->PatchOverview(BlockXOut, BlockYOut, WidthOut, HeightOut, + 0, true, Resampling); + GDALFlushCache(hDataset); } // Now for the upper levels @@ -376,7 +418,8 @@ static int Usage() return 1; } -int main(int nArgc, char **papszArgv) { +int main(int nArgc, char **papszArgv) +{ state State; int ret = 0; @@ -387,7 +430,8 @@ int main(int nArgc, char **papszArgv) { if (atoi(GDALVersionInfo("VERSION_NUM")) < 3000) { fprintf(stderr, "At least, GDAL >= 3.0.0 is required for this version of %s, " - "which was compiled against GDAL %s\n", papszArgv[0], GDAL_RELEASE_NAME); + "which was compiled against GDAL %s\n", + papszArgv[0], GDAL_RELEASE_NAME); exit(1); } @@ -397,7 +441,7 @@ int main(int nArgc, char **papszArgv) { // Set up a reasonable large cache size, say 256MB GDALSetCacheMax(256 * 1024 * 1024); // - // Done before the CmdLineProcessor has looked at options, so it can be overriden by the user + // Done before the CmdLineProcessor has looked at options, so it can be overriden by the user // by setting the GDAL_CACHEMAX env, or passing it as a --config option // // See http://trac.osgeo.org/gdal/wiki/ConfigOptions @@ -408,7 +452,6 @@ int main(int nArgc, char **papszArgv) { if (nArgc < 1) exit(-nArgc); - /* -------------------------------------------------------------------- */ /* Parse commandline, set up state */ /* -------------------------------------------------------------------- */ @@ -418,51 +461,65 @@ int main(int nArgc, char **papszArgv) { if (EQUAL(papszArgv[iArg], "--utility_version")) { printf("%s was compiled against GDAL %s and is running against GDAL %s\n", - papszArgv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME")); + papszArgv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME")); return 0; } - else if (EQUAL(papszArgv[iArg], "-start_level") && iArg < nArgc - 1) + { State.setStart(strtol(papszArgv[++iArg], 0, 0)); - + } else if (EQUAL(papszArgv[iArg], "-stop_level") && iArg < nArgc - 1) + { State.setStop(strtol(papszArgv[++iArg], 0, 0)); - - else if (EQUAL(papszArgv[iArg], "-r") && iArg < nArgc - 1) { + } + else if (EQUAL(papszArgv[iArg], "-r") && iArg < nArgc - 1) + { // R is required for building overviews State.setResampling(papszArgv[++iArg]); State.setOverlays(); } - else if (EQUAL(papszArgv[iArg], "-q") || EQUAL(papszArgv[iArg], "-quiet")) + { State.setProgress(GDALDummyProgress); - + } else if (EQUAL(papszArgv[iArg], "-v")) + { State.setDebug(1); - - else fnames.push_back(papszArgv[iArg]); + } + else + { + fnames.push_back(papszArgv[iArg]); + } } // Need at least a target and a source - if (fnames.size() > 0) { + if (fnames.size() > 0) + { State.setTarget(fnames[fnames.size() - 1]); fnames.pop_back(); } - if (fnames.empty()) return Usage(); + if (fnames.empty()) + { + return Usage(); + } - try { + try + { // Each input file in sequence, as they were passed as arguments - for (int i = 0; i < fnames.size(); i++) { + for (int i = 0; i < fnames.size(); i++) + { State.setSource(fnames[i]); // false return means error was detected and printed, just exit if (!State.patch()) + { throw 2; - + } } } // Try, all execution - catch (int err_ret) { + catch (int err_ret) + { ret = err_ret; };