<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#include <sal/config.h>

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <jpeglib.h>
#include <jerror.h>

#include <com/sun/star/task/XStatusIndicator.hpp>
#include <osl/diagnose.h>

extern "C" {
#include "transupp.h"
}

#include "jpeg.h"
#include <JpegReader.hxx>
#include <JpegWriter.hxx>
#include <boost/scoped_array.hpp>

struct ErrorManagerStruct
{
    jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};

extern "C" void errorExit (j_common_ptr cinfo)
{
    ErrorManagerStruct * error = (ErrorManagerStruct *) cinfo->err;
    (*cinfo->err->output_message) (cinfo);
    longjmp(error->setjmp_buffer, 1);
}

extern "C" void outputMessage (j_common_ptr cinfo)
{
    char buffer[JMSG_LENGTH_MAX];
    (*cinfo->err->format_message) (cinfo, buffer);
}

void ReadJPEG( JPEGReader* pJPEGReader, void* pInputStream, long* pLines,
               Size const & previewSize )
{
    jpeg_decompress_struct          cinfo;
    ErrorManagerStruct              jerr;
    JPEGCreateBitmapParam           aCreateBitmapParam;
    unsigned char *                 pDIB;
    unsigned char *                 pTmp;
    long                            nWidth;
    long                            nHeight;
    long                            nAlignedWidth;
    JSAMPLE*                        aRangeLimit;
    boost::scoped_array<unsigned char> pScanLineBuffer;
    long                            nScanLineBufferComponents;

    if ( setjmp( jerr.setjmp_buffer ) )
    {
        jpeg_destroy_decompress( &cinfo );
        return;
    }

    cinfo.err = jpeg_std_error( &jerr.pub );
    jerr.pub.error_exit = errorExit;
    jerr.pub.output_message = outputMessage;

    jpeg_create_decompress( &cinfo );
    jpeg_svstream_src( &cinfo, pInputStream );
    jpeg_read_header( &cinfo, TRUE );

    cinfo.scale_num = 1;
    cinfo.scale_denom = 1;
    cinfo.output_gamma = 1.0;
    cinfo.raw_data_out = FALSE;
    cinfo.quantize_colors = FALSE;
    if ( cinfo.jpeg_color_space == JCS_YCbCr )
        cinfo.out_color_space = JCS_RGB;
    else if ( cinfo.jpeg_color_space == JCS_YCCK )
        cinfo.out_color_space = JCS_CMYK;

    OSL_ASSERT(cinfo.out_color_space == JCS_CMYK || cinfo.out_color_space == JCS_GRAYSCALE || cinfo.out_color_space == JCS_RGB);

    /* change scale for preview import */
    long nPreviewWidth = previewSize.Width();
    long nPreviewHeight = previewSize.Height();
    if( nPreviewWidth || nPreviewHeight )
    {
        if( nPreviewWidth == 0 )
        {
            nPreviewWidth = ( cinfo.image_width * nPreviewHeight ) / cinfo.image_height;
            if( nPreviewWidth <= 0 )
            {
                nPreviewWidth = 1;
            }
        }
        else if( nPreviewHeight == 0 )
        {
            nPreviewHeight = ( cinfo.image_height * nPreviewWidth ) / cinfo.image_width;
            if( nPreviewHeight <= 0 )
            {
                nPreviewHeight = 1;
            }
        }

        for( cinfo.scale_denom = 1; cinfo.scale_denom < 8; cinfo.scale_denom *= 2 )
        {
            if( cinfo.image_width < nPreviewWidth * cinfo.scale_denom )
                break;
            if( cinfo.image_height < nPreviewHeight * cinfo.scale_denom )
                break;
        }

        if( cinfo.scale_denom > 1 )
        {
            cinfo.dct_method            = JDCT_FASTEST;
            cinfo.do_fancy_upsampling   = FALSE;
            cinfo.do_block_smoothing    = FALSE;
        }
    }

    jpeg_start_decompress( &cinfo );

    nWidth = cinfo.output_width;
    nHeight = cinfo.output_height;
    aCreateBitmapParam.nWidth = nWidth;
    aCreateBitmapParam.nHeight = nHeight;

    aCreateBitmapParam.density_unit = cinfo.density_unit;
    aCreateBitmapParam.X_density = cinfo.X_density;
    aCreateBitmapParam.Y_density = cinfo.Y_density;
    aCreateBitmapParam.bGray = long(cinfo.output_components == 1);
    pDIB = pJPEGReader->CreateBitmap( &aCreateBitmapParam );
    nAlignedWidth = aCreateBitmapParam.nAlignedWidth;
    aRangeLimit = cinfo.sample_range_limit;

    nScanLineBufferComponents = 0;
    if ( cinfo.out_color_space == JCS_CMYK )
    {
        nScanLineBufferComponents = cinfo.output_width * 4;
        pScanLineBuffer.reset(new unsigned char[nScanLineBufferComponents]);
    }

    if( pDIB )
    {
        if( aCreateBitmapParam.bTopDown )
        {
            pTmp = pDIB;
        }
        else
        {
            pTmp = pDIB + ( nHeight - 1 ) * nAlignedWidth;
            nAlignedWidth = -nAlignedWidth;
        }

        for ( *pLines = 0; *pLines < nHeight; (*pLines)++ )
        {
            if (pScanLineBuffer)
            { // in other words cinfo.out_color_space == JCS_CMYK
                int i;
                int j;
                unsigned char *pSLB = pScanLineBuffer.get();
                jpeg_read_scanlines( &cinfo, (JSAMPARRAY) &pSLB, 1 );
                // convert CMYK to RGB
                for( i=0, j=0; i < nScanLineBufferComponents; i+=4, j+=3 )
                {
                    int color_C = 255 - pScanLineBuffer[i+0];
                    int color_M = 255 - pScanLineBuffer[i+1];
                    int color_Y = 255 - pScanLineBuffer[i+2];
                    int color_K = 255 - pScanLineBuffer[i+3];
                    pTmp[j+0] = aRangeLimit[ 255L - ( color_C + color_K ) ];
                    pTmp[j+1] = aRangeLimit[ 255L - ( color_M + color_K ) ];
                    pTmp[j+2] = aRangeLimit[ 255L - ( color_Y + color_K ) ];
                }
            }
            else
            {
                jpeg_read_scanlines( &cinfo, (JSAMPARRAY) &pTmp, 1 );
            }

            /* PENDING ??? */
            if ( cinfo.err->msg_code == 113 )
                break;

            pTmp += nAlignedWidth;
        }
    }

    if ( pDIB )
    {
        jpeg_finish_decompress( &cinfo );
    }
    else
    {
        jpeg_abort_decompress( &cinfo );
    }

    pScanLineBuffer.reset();

    jpeg_destroy_decompress( &cinfo );
}

bool WriteJPEG( JPEGWriter* pJPEGWriter, void* pOutputStream,
                long nWidth, long nHeight, bool bGreys,
                long nQualityPercent, long aChromaSubsampling,
                css::uno::Reference<css::task::XStatusIndicator> const & status )
{
    jpeg_compress_struct        cinfo;
    ErrorManagerStruct          jerr;
    void*                       pScanline;
    long                        nY;

    if ( setjmp( jerr.setjmp_buffer ) )
    {
        jpeg_destroy_compress( &cinfo );
        return false;
    }

    cinfo.err = jpeg_std_error( &jerr.pub );
    jerr.pub.error_exit = errorExit;
    jerr.pub.output_message = outputMessage;

    jpeg_create_compress( &cinfo );
    jpeg_svstream_dest( &cinfo, pOutputStream );

    cinfo.image_width = (JDIMENSION) nWidth;
    cinfo.image_height = (JDIMENSION) nHeight;
    if ( bGreys )
    {
        cinfo.input_components = 1;
        cinfo.in_color_space = JCS_GRAYSCALE;
    }
    else
    {
        cinfo.input_components = 3;
        cinfo.in_color_space = JCS_RGB;
    }

    jpeg_set_defaults( &cinfo );
    jpeg_set_quality( &cinfo, (int) nQualityPercent, FALSE );

    if ( ( nWidth > 128 ) || ( nHeight > 128 ) )
        jpeg_simple_progression( &cinfo );

    if (aChromaSubsampling == 1) // YUV 4:4:4
    {
        cinfo.comp_info[0].h_samp_factor = 1;
        cinfo.comp_info[0].v_samp_factor = 1;
    }
    else if (aChromaSubsampling == 2) // YUV 4:2:2
    {
        cinfo.comp_info[0].h_samp_factor = 2;
        cinfo.comp_info[0].v_samp_factor = 1;
    }
    else if (aChromaSubsampling == 3) // YUV 4:2:0
    {
        cinfo.comp_info[0].h_samp_factor = 2;
        cinfo.comp_info[0].v_samp_factor = 2;
    }

    jpeg_start_compress( &cinfo, TRUE );

    for( nY = 0; nY < nHeight; nY++ )
    {
        pScanline = pJPEGWriter->GetScanline( nY );

        if( pScanline )
        {
            jpeg_write_scanlines( &cinfo, (JSAMPARRAY) &pScanline, 1 );
        }

        if( status.is() )
        {
            status->setValue( nY * 100L / nHeight );
        }
    }

    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress( &cinfo );

    return true;
}

long Transform(void* pInputStream, void* pOutputStream, long nAngle)
{
    jpeg_transform_info aTransformOption;
    JCOPY_OPTION        aCopyOption = JCOPYOPT_ALL;

    jpeg_decompress_struct   aSourceInfo;
    jpeg_compress_struct     aDestinationInfo;
    ErrorManagerStruct       aSourceError;
    ErrorManagerStruct       aDestinationError;

    jvirt_barray_ptr* aSourceCoefArrays      = 0;
    jvirt_barray_ptr* aDestinationCoefArrays = 0;

    aTransformOption.force_grayscale = FALSE;
    aTransformOption.trim            = FALSE;
    aTransformOption.perfect         = FALSE;
    aTransformOption.crop            = FALSE;

    // Angle to transform option
    // 90 Clockwise = 270 Counterclockwise
    switch (nAngle)
    {
        case 2700:
            aTransformOption.transform  = JXFORM_ROT_90;
            break;
        case 1800:
            aTransformOption.transform  = JXFORM_ROT_180;
            break;
        case 900:
            aTransformOption.transform  = JXFORM_ROT_270;
            break;
        default:
            aTransformOption.transform  = JXFORM_NONE;
    }

    // Decompression
    aSourceInfo.err                 = jpeg_std_error(&aSourceError.pub);
    aSourceInfo.err->error_exit     = errorExit;
    aSourceInfo.err->output_message = outputMessage;

    // Compression
    aDestinationInfo.err                 = jpeg_std_error(&aDestinationError.pub);
    aDestinationInfo.err->error_exit     = errorExit;
    aDestinationInfo.err->output_message = outputMessage;

    aDestinationInfo.optimize_coding = TRUE;

    if (setjmp(aSourceError.setjmp_buffer) || setjmp(aDestinationError.setjmp_buffer))
    {
        jpeg_destroy_decompress(&aSourceInfo);
        jpeg_destroy_compress(&aDestinationInfo);
        return 0;
    }

    jpeg_create_decompress(&aSourceInfo);
    jpeg_create_compress(&aDestinationInfo);

    jpeg_svstream_src (&aSourceInfo, pInputStream);

    jcopy_markers_setup(&aSourceInfo, aCopyOption);
    jpeg_read_header(&aSourceInfo, TRUE);
    jtransform_request_workspace(&aSourceInfo, &aTransformOption);

    aSourceCoefArrays = jpeg_read_coefficients(&aSourceInfo);
    jpeg_copy_critical_parameters(&aSourceInfo, &aDestinationInfo);

    aDestinationCoefArrays = jtransform_adjust_parameters(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);
    jpeg_svstream_dest (&aDestinationInfo, pOutputStream);

    // Compute optimal Huffman coding tables instead of precomuted tables
    aDestinationInfo.optimize_coding = TRUE;
    jpeg_write_coefficients(&aDestinationInfo, aDestinationCoefArrays);
    jcopy_markers_execute(&aSourceInfo, &aDestinationInfo, aCopyOption);
    jtransform_execute_transformation(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);

    jpeg_finish_compress(&aDestinationInfo);
    jpeg_destroy_compress(&aDestinationInfo);

    jpeg_finish_decompress(&aSourceInfo);
    jpeg_destroy_decompress(&aSourceInfo);

    return 1;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>Revert "grml.. nScanLineBufferComponents still needs to be on jmp stack"<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#include <sal/config.h>

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <jpeglib.h>
#include <jerror.h>

#include <com/sun/star/task/XStatusIndicator.hpp>
#include <osl/diagnose.h>

extern "C" {
#include "transupp.h"
}

#include "jpeg.h"
#include <JpegReader.hxx>
#include <JpegWriter.hxx>
#include <boost/scoped_array.hpp>

struct ErrorManagerStruct
{
    jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};

extern "C" void errorExit (j_common_ptr cinfo)
{
    ErrorManagerStruct * error = (ErrorManagerStruct *) cinfo->err;
    (*cinfo->err->output_message) (cinfo);
    longjmp(error->setjmp_buffer, 1);
}

extern "C" void outputMessage (j_common_ptr cinfo)
{
    char buffer[JMSG_LENGTH_MAX];
    (*cinfo->err->format_message) (cinfo, buffer);
}

void ReadJPEG( JPEGReader* pJPEGReader, void* pInputStream, long* pLines,
               Size const & previewSize )
{
    jpeg_decompress_struct          cinfo;
    ErrorManagerStruct              jerr;
    JPEGCreateBitmapParam           aCreateBitmapParam;
    unsigned char *                 pDIB;
    unsigned char *                 pTmp;
    long                            nWidth;
    long                            nHeight;
    long                            nAlignedWidth;
    JSAMPLE*                        aRangeLimit;
    boost::scoped_array<unsigned char> pScanLineBuffer;

    if ( setjmp( jerr.setjmp_buffer ) )
    {
        jpeg_destroy_decompress( &cinfo );
        return;
    }

    cinfo.err = jpeg_std_error( &jerr.pub );
    jerr.pub.error_exit = errorExit;
    jerr.pub.output_message = outputMessage;

    jpeg_create_decompress( &cinfo );
    jpeg_svstream_src( &cinfo, pInputStream );
    jpeg_read_header( &cinfo, TRUE );

    cinfo.scale_num = 1;
    cinfo.scale_denom = 1;
    cinfo.output_gamma = 1.0;
    cinfo.raw_data_out = FALSE;
    cinfo.quantize_colors = FALSE;
    if ( cinfo.jpeg_color_space == JCS_YCbCr )
        cinfo.out_color_space = JCS_RGB;
    else if ( cinfo.jpeg_color_space == JCS_YCCK )
        cinfo.out_color_space = JCS_CMYK;

    OSL_ASSERT(cinfo.out_color_space == JCS_CMYK || cinfo.out_color_space == JCS_GRAYSCALE || cinfo.out_color_space == JCS_RGB);

    /* change scale for preview import */
    long nPreviewWidth = previewSize.Width();
    long nPreviewHeight = previewSize.Height();
    if( nPreviewWidth || nPreviewHeight )
    {
        if( nPreviewWidth == 0 )
        {
            nPreviewWidth = ( cinfo.image_width * nPreviewHeight ) / cinfo.image_height;
            if( nPreviewWidth <= 0 )
            {
                nPreviewWidth = 1;
            }
        }
        else if( nPreviewHeight == 0 )
        {
            nPreviewHeight = ( cinfo.image_height * nPreviewWidth ) / cinfo.image_width;
            if( nPreviewHeight <= 0 )
            {
                nPreviewHeight = 1;
            }
        }

        for( cinfo.scale_denom = 1; cinfo.scale_denom < 8; cinfo.scale_denom *= 2 )
        {
            if( cinfo.image_width < nPreviewWidth * cinfo.scale_denom )
                break;
            if( cinfo.image_height < nPreviewHeight * cinfo.scale_denom )
                break;
        }

        if( cinfo.scale_denom > 1 )
        {
            cinfo.dct_method            = JDCT_FASTEST;
            cinfo.do_fancy_upsampling   = FALSE;
            cinfo.do_block_smoothing    = FALSE;
        }
    }

    jpeg_start_decompress( &cinfo );

    nWidth = cinfo.output_width;
    nHeight = cinfo.output_height;
    aCreateBitmapParam.nWidth = nWidth;
    aCreateBitmapParam.nHeight = nHeight;

    aCreateBitmapParam.density_unit = cinfo.density_unit;
    aCreateBitmapParam.X_density = cinfo.X_density;
    aCreateBitmapParam.Y_density = cinfo.Y_density;
    aCreateBitmapParam.bGray = long(cinfo.output_components == 1);
    pDIB = pJPEGReader->CreateBitmap( &aCreateBitmapParam );
    nAlignedWidth = aCreateBitmapParam.nAlignedWidth;
    aRangeLimit = cinfo.sample_range_limit;

    long nScanLineBufferComponents = 0;
    if ( cinfo.out_color_space == JCS_CMYK )
    {
        nScanLineBufferComponents = cinfo.output_width * 4;
        pScanLineBuffer.reset(new unsigned char[nScanLineBufferComponents]);
    }

    if( pDIB )
    {
        if( aCreateBitmapParam.bTopDown )
        {
            pTmp = pDIB;
        }
        else
        {
            pTmp = pDIB + ( nHeight - 1 ) * nAlignedWidth;
            nAlignedWidth = -nAlignedWidth;
        }

        for ( *pLines = 0; *pLines < nHeight; (*pLines)++ )
        {
            if (pScanLineBuffer)
            { // in other words cinfo.out_color_space == JCS_CMYK
                int i;
                int j;
                unsigned char *pSLB = pScanLineBuffer.get();
                jpeg_read_scanlines( &cinfo, (JSAMPARRAY) &pSLB, 1 );
                // convert CMYK to RGB
                for( i=0, j=0; i < nScanLineBufferComponents; i+=4, j+=3 )
                {
                    int color_C = 255 - pScanLineBuffer[i+0];
                    int color_M = 255 - pScanLineBuffer[i+1];
                    int color_Y = 255 - pScanLineBuffer[i+2];
                    int color_K = 255 - pScanLineBuffer[i+3];
                    pTmp[j+0] = aRangeLimit[ 255L - ( color_C + color_K ) ];
                    pTmp[j+1] = aRangeLimit[ 255L - ( color_M + color_K ) ];
                    pTmp[j+2] = aRangeLimit[ 255L - ( color_Y + color_K ) ];
                }
            }
            else
            {
                jpeg_read_scanlines( &cinfo, (JSAMPARRAY) &pTmp, 1 );
            }

            /* PENDING ??? */
            if ( cinfo.err->msg_code == 113 )
                break;

            pTmp += nAlignedWidth;
        }
    }

    if ( pDIB )
    {
        jpeg_finish_decompress( &cinfo );
    }
    else
    {
        jpeg_abort_decompress( &cinfo );
    }

    pScanLineBuffer.reset();

    jpeg_destroy_decompress( &cinfo );
}

bool WriteJPEG( JPEGWriter* pJPEGWriter, void* pOutputStream,
                long nWidth, long nHeight, bool bGreys,
                long nQualityPercent, long aChromaSubsampling,
                css::uno::Reference<css::task::XStatusIndicator> const & status )
{
    jpeg_compress_struct        cinfo;
    ErrorManagerStruct          jerr;
    void*                       pScanline;
    long                        nY;

    if ( setjmp( jerr.setjmp_buffer ) )
    {
        jpeg_destroy_compress( &cinfo );
        return false;
    }

    cinfo.err = jpeg_std_error( &jerr.pub );
    jerr.pub.error_exit = errorExit;
    jerr.pub.output_message = outputMessage;

    jpeg_create_compress( &cinfo );
    jpeg_svstream_dest( &cinfo, pOutputStream );

    cinfo.image_width = (JDIMENSION) nWidth;
    cinfo.image_height = (JDIMENSION) nHeight;
    if ( bGreys )
    {
        cinfo.input_components = 1;
        cinfo.in_color_space = JCS_GRAYSCALE;
    }
    else
    {
        cinfo.input_components = 3;
        cinfo.in_color_space = JCS_RGB;
    }

    jpeg_set_defaults( &cinfo );
    jpeg_set_quality( &cinfo, (int) nQualityPercent, FALSE );

    if ( ( nWidth > 128 ) || ( nHeight > 128 ) )
        jpeg_simple_progression( &cinfo );

    if (aChromaSubsampling == 1) // YUV 4:4:4
    {
        cinfo.comp_info[0].h_samp_factor = 1;
        cinfo.comp_info[0].v_samp_factor = 1;
    }
    else if (aChromaSubsampling == 2) // YUV 4:2:2
    {
        cinfo.comp_info[0].h_samp_factor = 2;
        cinfo.comp_info[0].v_samp_factor = 1;
    }
    else if (aChromaSubsampling == 3) // YUV 4:2:0
    {
        cinfo.comp_info[0].h_samp_factor = 2;
        cinfo.comp_info[0].v_samp_factor = 2;
    }

    jpeg_start_compress( &cinfo, TRUE );

    for( nY = 0; nY < nHeight; nY++ )
    {
        pScanline = pJPEGWriter->GetScanline( nY );

        if( pScanline )
        {
            jpeg_write_scanlines( &cinfo, (JSAMPARRAY) &pScanline, 1 );
        }

        if( status.is() )
        {
            status->setValue( nY * 100L / nHeight );
        }
    }

    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress( &cinfo );

    return true;
}

long Transform(void* pInputStream, void* pOutputStream, long nAngle)
{
    jpeg_transform_info aTransformOption;
    JCOPY_OPTION        aCopyOption = JCOPYOPT_ALL;

    jpeg_decompress_struct   aSourceInfo;
    jpeg_compress_struct     aDestinationInfo;
    ErrorManagerStruct       aSourceError;
    ErrorManagerStruct       aDestinationError;

    jvirt_barray_ptr* aSourceCoefArrays      = 0;
    jvirt_barray_ptr* aDestinationCoefArrays = 0;

    aTransformOption.force_grayscale = FALSE;
    aTransformOption.trim            = FALSE;
    aTransformOption.perfect         = FALSE;
    aTransformOption.crop            = FALSE;

    // Angle to transform option
    // 90 Clockwise = 270 Counterclockwise
    switch (nAngle)
    {
        case 2700:
            aTransformOption.transform  = JXFORM_ROT_90;
            break;
        case 1800:
            aTransformOption.transform  = JXFORM_ROT_180;
            break;
        case 900:
            aTransformOption.transform  = JXFORM_ROT_270;
            break;
        default:
            aTransformOption.transform  = JXFORM_NONE;
    }

    // Decompression
    aSourceInfo.err                 = jpeg_std_error(&aSourceError.pub);
    aSourceInfo.err->error_exit     = errorExit;
    aSourceInfo.err->output_message = outputMessage;

    // Compression
    aDestinationInfo.err                 = jpeg_std_error(&aDestinationError.pub);
    aDestinationInfo.err->error_exit     = errorExit;
    aDestinationInfo.err->output_message = outputMessage;

    aDestinationInfo.optimize_coding = TRUE;

    if (setjmp(aSourceError.setjmp_buffer) || setjmp(aDestinationError.setjmp_buffer))
    {
        jpeg_destroy_decompress(&aSourceInfo);
        jpeg_destroy_compress(&aDestinationInfo);
        return 0;
    }

    jpeg_create_decompress(&aSourceInfo);
    jpeg_create_compress(&aDestinationInfo);

    jpeg_svstream_src (&aSourceInfo, pInputStream);

    jcopy_markers_setup(&aSourceInfo, aCopyOption);
    jpeg_read_header(&aSourceInfo, TRUE);
    jtransform_request_workspace(&aSourceInfo, &aTransformOption);

    aSourceCoefArrays = jpeg_read_coefficients(&aSourceInfo);
    jpeg_copy_critical_parameters(&aSourceInfo, &aDestinationInfo);

    aDestinationCoefArrays = jtransform_adjust_parameters(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);
    jpeg_svstream_dest (&aDestinationInfo, pOutputStream);

    // Compute optimal Huffman coding tables instead of precomuted tables
    aDestinationInfo.optimize_coding = TRUE;
    jpeg_write_coefficients(&aDestinationInfo, aDestinationCoefArrays);
    jcopy_markers_execute(&aSourceInfo, &aDestinationInfo, aCopyOption);
    jtransform_execute_transformation(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);

    jpeg_finish_compress(&aDestinationInfo);
    jpeg_destroy_compress(&aDestinationInfo);

    jpeg_finish_decompress(&aSourceInfo);
    jpeg_destroy_decompress(&aSourceInfo);

    return 1;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#pragma once

#include <sdbusplus/bus.hpp>
#include <thread>

namespace sdbusplus
{
namespace server
{
namespace transaction
{
namespace details
{

// Transaction Id
extern thread_local uint64_t id;

struct Transaction
{
    Transaction() : time(std::time(nullptr)), thread(std::this_thread::get_id())
    {
    }

    int time;
    std::thread::id thread;
};

} // namespace details

struct Transaction
{
    Transaction(sdbusplus::bus::bus& bus, sdbusplus::message::message& msg) :
        bus(bus), msg(msg)
    {
    }

    sdbusplus::bus::bus& bus;
    sdbusplus::message::message& msg;
};

} // namespace transaction
} // namespace server
} // namespace sdbusplus

namespace std
{

/** @ brief Overload of std::hash for sdbusplus::bus::bus */
template <>
struct hash<sdbusplus::bus::bus>
{
    auto operator()(sdbusplus::bus::bus& b) const
    {
        auto name = b.get_unique_name();
        return std::hash<std::string>{}(name);
    }
};

/** @ brief Overload of std::hash for sdbusplus::message::message */
template <>
struct hash<sdbusplus::message::message>
{
    auto operator()(sdbusplus::message::message& m) const
    {
        auto cookie = m.get_cookie();
        return std::hash<uint64_t>{}(cookie);
    }
};

/** @ brief Overload of std::hash for Transaction */
template <>
struct hash<sdbusplus::server::transaction::Transaction>
{
    auto operator()(sdbusplus::server::transaction::Transaction const& t) const
    {
        auto hash1 = std::hash<sdbusplus::bus::bus>{}(t.bus);
        auto hash2 = std::hash<sdbusplus::message::message>{}(t.msg);

        // boost::hash_combine() algorithm.
        return static_cast<size_t>(
            hash1 ^ (hash2 + 0x9e3779b9 + (hash1 << 6) + (hash1 >> 2)));
    }
};

/** @ brief Overload of std::hash for details::Transaction */
template <>
struct hash<sdbusplus::server::transaction::details::Transaction>
{
    auto operator()(
        sdbusplus::server::transaction::details::Transaction const& t) const
    {
        auto hash1 = std::hash<int>{}(t.time);
        auto hash2 = std::hash<std::thread::id>{}(t.thread);

        // boost::hash_combine() algorithm.
        return static_cast<size_t>(
            hash1 ^ (hash2 + 0x9e3779b9 + (hash1 << 6) + (hash1 >> 2)));
    }
};

} // namespace std

namespace sdbusplus
{
namespace server
{
namespace transaction
{

/** @brief Get transaction id
 *
 * @return The value of the transaction id
 */
inline uint64_t get_id()
{
    // If the transaction id has not been initialized, generate one.
    if (!details::id)
    {
        details::Transaction t;
        details::id = std::hash<details::Transaction>{}(t);
    }
    return details::id;
}

/** @brief Set transaction id
 *
 * @param[in] value - Desired value for the transaction id
 */
inline void set_id(uint64_t value)
{
    details::id = value;
}

} // namespace transaction
} // namespace server
} // namespace sdbusplus
<commit_msg>server/transaction: Fix message hash<commit_after>#pragma once

#include <systemd/sd-bus.h>

#include <chrono>
#include <sdbusplus/bus.hpp>
#include <stdexcept>
#include <thread>

namespace sdbusplus
{
namespace server
{
namespace transaction
{
namespace details
{

// Transaction Id
extern thread_local uint64_t id;

struct Transaction
{
    Transaction() : time(std::time(nullptr)), thread(std::this_thread::get_id())
    {
    }

    int time;
    std::thread::id thread;
};

} // namespace details

struct Transaction
{
    Transaction(sdbusplus::bus::bus& bus, sdbusplus::message::message& msg) :
        bus(bus), msg(msg)
    {
    }

    sdbusplus::bus::bus& bus;
    sdbusplus::message::message& msg;
};

} // namespace transaction
} // namespace server
} // namespace sdbusplus

namespace std
{

/** @ brief Overload of std::hash for sdbusplus::bus::bus */
template <>
struct hash<sdbusplus::bus::bus>
{
    auto operator()(sdbusplus::bus::bus& b) const
    {
        auto name = b.get_unique_name();
        return std::hash<std::string>{}(name);
    }
};

/** @ brief Overload of std::hash for sdbusplus::message::message */
template <>
struct hash<sdbusplus::message::message>
{
    auto operator()(sdbusplus::message::message& m) const
    {
        switch (m.get_type())
        {
            // Reply messages will always embed the cookie of the original
            // message in a separate location. We want to use this cookie
            // to correlate messages as one transaction.
            case SD_BUS_MESSAGE_METHOD_RETURN:
            case SD_BUS_MESSAGE_METHOD_ERROR:
                return std::hash<uint64_t>{}(m.get_reply_cookie());
            // Method calls will have the cookie in the header when sealed.
            // Since we are on the server side that should always be the case.
            case SD_BUS_MESSAGE_METHOD_CALL:
                return std::hash<uint64_t>{}(m.get_cookie());
            // Outgoing signals don't have a cookie so we need to use
            // something else as an id. Just use a monotonic unique one.
            case SD_BUS_MESSAGE_SIGNAL:
                return std::hash<uint64_t>{}(std::chrono::steady_clock::now()
                                                 .time_since_epoch()
                                                 .count());
            default:
                throw std::runtime_error("hash message: Unknown message type");
        }
    }
};

/** @ brief Overload of std::hash for Transaction */
template <>
struct hash<sdbusplus::server::transaction::Transaction>
{
    auto operator()(sdbusplus::server::transaction::Transaction const& t) const
    {
        auto hash1 = std::hash<sdbusplus::bus::bus>{}(t.bus);
        auto hash2 = std::hash<sdbusplus::message::message>{}(t.msg);

        // boost::hash_combine() algorithm.
        return static_cast<size_t>(
            hash1 ^ (hash2 + 0x9e3779b9 + (hash1 << 6) + (hash1 >> 2)));
    }
};

/** @ brief Overload of std::hash for details::Transaction */
template <>
struct hash<sdbusplus::server::transaction::details::Transaction>
{
    auto operator()(
        sdbusplus::server::transaction::details::Transaction const& t) const
    {
        auto hash1 = std::hash<int>{}(t.time);
        auto hash2 = std::hash<std::thread::id>{}(t.thread);

        // boost::hash_combine() algorithm.
        return static_cast<size_t>(
            hash1 ^ (hash2 + 0x9e3779b9 + (hash1 << 6) + (hash1 >> 2)));
    }
};

} // namespace std

namespace sdbusplus
{
namespace server
{
namespace transaction
{

/** @brief Get transaction id
 *
 * @return The value of the transaction id
 */
inline uint64_t get_id()
{
    // If the transaction id has not been initialized, generate one.
    if (!details::id)
    {
        details::Transaction t;
        details::id = std::hash<details::Transaction>{}(t);
    }
    return details::id;
}

/** @brief Set transaction id
 *
 * @param[in] value - Desired value for the transaction id
 */
inline void set_id(uint64_t value)
{
    details::id = value;
}

} // namespace transaction
} // namespace server
} // namespace sdbusplus
<|endoftext|>


<commit_before>//=====================================================================================================================================
//The MIT License (MIT)
//Copyright (c) 2015 Austin Salgat
//
//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 ENUM_ITERATOR_ENUMIT_HPP
#define ENUM_ITERATOR_ENUMIT_HPP

namespace enumit {
    /**
     * Supports iteration of enumerations (and enumeration classes).
     */
    template<class enum_class>
    class Iterate {
    public:
        enum_class last;

        Iterate() = default;

        Iterate(enum_class last_) {
            last = static_cast<enum_class>(static_cast<int>(last_)+1);
        }

        class Iterator {
        private:
            int current_position;

        public:
            Iterator(Iterator const& iter) = default;

            Iterator(int index = 0) {
                current_position = index;
            }

            ~Iterator(){}

            Iterator& operator=(Iterator const& iter) = default;

            Iterator operator+(int const value) {
                auto temp(*this);
                temp.current_position += value;
                return temp;
            }

            Iterator& operator+=(int const value) {
                current_position += value;
                return *this;
            }

            Iterator operator-(int const value) {
                auto temp(*this);
                temp.current_position -= value;
                return temp;
            }

            Iterator& operator-=(int const value) {
                current_position -= value;
                return *this;
            }

            Iterator& operator++() {
                ++current_position;
                return *this;
            }

            Iterator operator++(int) {
                auto temp(*this);
                ++current_position;
                return temp;
            }

            Iterator& operator--() {
                --current_position;
                return *this;
            }

            Iterator operator--(int) {
                auto temp(*this);
                --current_position;
                return temp;
            }

            bool operator==(Iterator const& other) const {
                return current_position == other.current_position;
            }

            bool operator!=(Iterator const& other) const {
                return current_position != other.current_position;
            }

            enum_class operator*() const {
                return static_cast<enum_class>(current_position);
            }
        };
    };

    template<class enum_class>
    typename Iterate<enum_class>::Iterator begin(Iterate<enum_class>) {
        return typename Iterate<enum_class>::Iterator(0);
    }

    template<class enum_class>
    typename Iterate<enum_class>::Iterator end(Iterate<enum_class> end) {
        return typename Iterate<enum_class>::Iterator(static_cast<int>(end.last));
    }

    /**
     *  Returns iterator pointing to first element of enumeration.
     *
     *  Note: The default argument is for people who want visual consistency between Begin(last_enum) and End(last_enum).
     */
    template<class enum_class>
    typename Iterate<enum_class>::Iterator Begin(enum_class ignore = static_cast<enum_class>(0)) {
        return typename Iterate<enum_class>::Iterator(0);
    };

    /**
     * Returns iterator pointing to one past the last element of enumeration.
     */
    template<class enum_class>
    typename Iterate<enum_class>::Iterator End(enum_class last_entry) {
        return typename Iterate<enum_class>::Iterator(static_cast<int>(last_entry)+1);
    };
}

#endif //ENUM_ITERATOR_ENUMIT_HPP
<commit_msg>Update Begin to allow custom starting enum value<commit_after>//=====================================================================================================================================
//The MIT License (MIT)
//Copyright (c) 2015 Austin Salgat
//
//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 ENUM_ITERATOR_ENUMIT_HPP
#define ENUM_ITERATOR_ENUMIT_HPP

namespace enumit {
    /**
     * Supports iteration of enumerations (and enumeration classes).
     */
    template<class enum_class>
    class Iterate {
    public:
        enum_class last;

        Iterate() = default;

        Iterate(enum_class last_) {
            last = static_cast<enum_class>(static_cast<int>(last_)+1);
        }

        class Iterator {
        private:
            int current_position;

        public:
            Iterator(Iterator const& iter) = default;

            Iterator(int index = 0) {
                current_position = index;
            }

            ~Iterator(){}

            Iterator& operator=(Iterator const& iter) = default;

            Iterator operator+(int const value) {
                auto temp(*this);
                temp.current_position += value;
                return temp;
            }

            Iterator& operator+=(int const value) {
                current_position += value;
                return *this;
            }

            Iterator operator-(int const value) {
                auto temp(*this);
                temp.current_position -= value;
                return temp;
            }

            Iterator& operator-=(int const value) {
                current_position -= value;
                return *this;
            }

            Iterator& operator++() {
                ++current_position;
                return *this;
            }

            Iterator operator++(int) {
                auto temp(*this);
                ++current_position;
                return temp;
            }

            Iterator& operator--() {
                --current_position;
                return *this;
            }

            Iterator operator--(int) {
                auto temp(*this);
                --current_position;
                return temp;
            }

            bool operator==(Iterator const& other) const {
                return current_position == other.current_position;
            }

            bool operator!=(Iterator const& other) const {
                return current_position != other.current_position;
            }

            enum_class operator*() const {
                return static_cast<enum_class>(current_position);
            }
        };
    };

    template<class enum_class>
    typename Iterate<enum_class>::Iterator begin(Iterate<enum_class>) {
        return typename Iterate<enum_class>::Iterator(0);
    }

    template<class enum_class>
    typename Iterate<enum_class>::Iterator end(Iterate<enum_class> end) {
        return typename Iterate<enum_class>::Iterator(static_cast<int>(end.last));
    }

    /**
     *  Returns iterator pointing to first element of enumeration.
     */
    template<class enum_class>
    typename Iterate<enum_class>::Iterator Begin(enum_class start = static_cast<enum_class>(0)) {
        return typename Iterate<enum_class>::Iterator(static_cast<int>(start));
    };

    /**
     * Returns iterator pointing to one past the last element of enumeration.
     */
    template<class enum_class>
    typename Iterate<enum_class>::Iterator End(enum_class last_entry) {
        return typename Iterate<enum_class>::Iterator(static_cast<int>(last_entry)+1);
    };
}

#endif //ENUM_ITERATOR_ENUMIT_HPP
<|endoftext|>


<commit_before>/*
    This file is part of Anabel

    Anabel 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 3 of the License, or
    (at your option) any later version.

    Anabel 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 Anabel; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include <anabel/stdafx.h>
#include <anabel/anabel.h>

using namespace boost::filesystem;
using namespace boost::interprocess;
using namespace Anabel::Exceptions;
using namespace std;
using namespace Anabel;
using namespace Anabel::Internal;
/**
* Chooses a timestamp that can be chosen, so it contains needle.
*
* Haystack must not be empty - other way, it would not make any sense, would it?
* Haystack must contain entry that will follow us to needle.
*/
Timestamp choose(vector<Timestamp> haystack, Timestamp needle) {
	for (unsigned i=0; i<haystack.size(); i++) {
		if (haystack[i] <= needle) return haystack[i];
	}
	throw InternalError("needle not found");
}

Anabel::ReadQuery * Anabel::TimeSeries::get_query(Anabel::Timestamp from, Anabel::Timestamp to) {
	// Sanity checks
	if ((this->mode != TSO_READ) && (this->mode != TSO_WRITE)) throw InvalidInvocation("invalid open mode");

	typedef vector<Timestamp> timevector;
	typedef vector<Timestamp>::iterator timevectoriter;

	// Init variables
	path cpath = *this->root_path;
	timevector elements;
	Timestamp choice;
	vector<path> * files = new vector<path>();

	// Locate UBA
	cpath = *this->root_path;
	try {
		while (is_directory(cpath)) {
			elements = scan_directory(cpath);
			sort(elements.begin(), elements.end(), greater<Timestamp>());
			choice = choose(elements, to);
			cpath /= timestamp_to_string(choice);
		}
	} catch (InternalError e) {
		// query empty.
		delete files;
		return new Anabel::ReadQuery(from, to, NULL, this->record_size);
	}

		files->push_back(cpath);
	if (choice <= from) {
		return new Anabel::ReadQuery(from, to, files, this->record_size);	// response is a single-file wonder
	}

	cpath = cpath.parent_path();

	// Now we will trace thru the filesystem, finding doodz. First up the tree, and then sharp dive towards 'to'
	Timestamp previous_choice = choice;
	while (true) {
		elements = scan_directory(cpath);
		sort(elements.begin(), elements.end(), std::greater<Timestamp>());
		timevectoriter bound = upper_bound(elements.begin(), elements.end(), from, std::greater<Timestamp>());

		if (bound == elements.end()) {	// we need to ascend
			// index files from bound upwards
			for (timevectoriter start_bound = upper_bound(elements.begin(), elements.end(), to, std::greater<Timestamp>()); start_bound != elements.end(); start_bound++) {
				if (previous_choice == *start_bound) continue;
				files->push_back(cpath / timestamp_to_string(*start_bound));
			}
			cpath = cpath.parent_path();
			continue;
		}

		// Descent to "bound"
		for (timevectoriter start_bound = upper_bound(elements.begin(), elements.end(), to, std::greater<Timestamp>()); start_bound != bound; start_bound++) {
			if (previous_choice == *start_bound) continue;
			files->push_back(cpath / timestamp_to_string(*start_bound));
		}

		cpath = cpath / timestamp_to_string(*bound);
		if (is_regular_file(cpath)) {
			files->push_back(cpath);
			break;
		}
	}

	return new Anabel::ReadQuery(from, to, files, this->record_size);
}

void Anabel::TimeSeries::open(TimeSeriesOpenMode open_mode) {
	// Do the locking!
	/*
		Now, how do I resolve deadlocks? They may happen.
		During a deadlock, second acquisiton will raise an exception. Then, I will release the locks.
		And fun begins again. Maybe backoff algorithm can be used by the upper layer?
	*/
	switch (open_mode) {
		case TSO_APPEND:
			try {
				this->alock->try_lock();
			} catch (boost::interprocess::lock_exception exc) {
				throw TimeSeriesLocked();
			}
			break;
		case TSO_REBALANCE:
			try {
				this->block->try_lock();
			} catch (boost::interprocess::lock_exception exc) {
				throw TimeSeriesLocked();
			}
			break;
		case TSO_READ:
			try {
				this->alock->try_lock_sharable();
			} catch (boost::interprocess::lock_exception exc) {
				throw TimeSeriesLocked();
			}
			try {
				this->block->try_lock_sharable();
			} catch (boost::interprocess::lock_exception exc) {
				this->alock->unlock_sharable();
				throw TimeSeriesLocked();
			}
			break;
		case TSO_WRITE:
			try {
				this->alock->try_lock();
			} catch (boost::interprocess::lock_exception exc) {
				throw TimeSeriesLocked();
			}
			try {
				this->block->try_lock();
			} catch (boost::interprocess::lock_exception exc) {
				this->alock->unlock();
				throw TimeSeriesLocked();
			}
			break;
		case TSO_CLOSED:	// funnt, not undefined though
			throw InvalidInvocation("invalid open_mode");
			break;
		default:
			throw InvalidInvocation("unknown open_mode");
	}

	// Read type of time series, as we don't know it now
	std::ifstream conf((*this->root_path / "record_size").string());
	conf >> this->record_size;
	conf.close();

	this->mode = open_mode;
}

void Anabel::TimeSeries::close(void) {
	switch (this->mode) {
		case TSO_READ:
			this->alock->unlock_sharable();
			this->block->unlock_sharable();
			break;
		case TSO_WRITE:
			this->alock->unlock();
			this->block->unlock();
			break;
		case TSO_REBALANCE:
			this->block->unlock();
			break;
		case TSO_APPEND:
			this->alock->unlock();
			break;
		case TSO_CLOSED:
			break;
	}
}

Anabel::TimeSeries::TimeSeries(std::string rootdirpath) : mode(TSO_CLOSED), record_size(0) {
	// Prepare pathes
	this->root_path = new path(rootdirpath);
	path rsize_path(*this->root_path);
	rsize_path /= "record_size";
	path alock_path(*this->root_path);
	alock_path /= "alock";
	path block_path(*this->root_path);
	block_path /= "block";

	// Sanity-check pathes
	if (!exists(*this->root_path)) throw InvalidRootDirectory("root directory does not exist");
	if (!is_directory(*this->root_path)) throw InvalidRootDirectory("root directory is not a directory");
	if (!exists(rsize_path)) throw InvalidRootDirectory("rsize_path file not found");
	if (!exists(alock_path)) throw InvalidRootDirectory("append lock not found");
	if (!exists(block_path)) throw InvalidRootDirectory("rebalance lock not found");

	// Create locks
	this->alock = new boost::interprocess::file_lock(alock_path.string().c_str());
	this->block = new boost::interprocess::file_lock(block_path.string().c_str());
}
Anabel::TimeSeries::~TimeSeries() {
	if (this->mode != TSO_CLOSED) this->close();
	delete this->alock;
	delete this->block;
	delete this->root_path;
}
<commit_msg>more bugs fixed<commit_after>/*
    This file is part of Anabel

    Anabel 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 3 of the License, or
    (at your option) any later version.

    Anabel 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 Anabel; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include <anabel/stdafx.h>
#include <anabel/anabel.h>

using namespace boost::filesystem;
using namespace boost::interprocess;
using namespace Anabel::Exceptions;
using namespace std;
using namespace Anabel;
using namespace Anabel::Internal;
/**
* Chooses a timestamp that can be chosen, so it contains needle.
*
* Haystack must not be empty - other way, it would not make any sense, would it?
* Haystack must contain entry that will follow us to needle.
*/
Timestamp choose(vector<Timestamp> haystack, Timestamp needle) {
	for (unsigned i=0; i<haystack.size(); i++) {
		if (haystack[i] <= needle) return haystack[i];
	}
	throw InternalError("needle not found");
}

Anabel::ReadQuery * Anabel::TimeSeries::get_query(Anabel::Timestamp from, Anabel::Timestamp to) {
	// Sanity checks
	if ((this->mode != TSO_READ) && (this->mode != TSO_WRITE)) throw InvalidInvocation("invalid open mode");

	typedef vector<Timestamp> timevector;
	typedef vector<Timestamp>::iterator timevectoriter;

	// Init variables
	path cpath = *this->root_path;
	timevector elements;
	Timestamp choice;
	vector<path> * files = new vector<path>();

	// Locate UBA
	cpath = *this->root_path;
	try {
		while (is_directory(cpath)) {
			elements = scan_directory(cpath);
			sort(elements.begin(), elements.end(), greater<Timestamp>());
			choice = choose(elements, to);
			cpath /= timestamp_to_string(choice);
		}
	} catch (InternalError e) {
		// query empty.
		delete files;
		return new Anabel::ReadQuery(from, to, NULL, this->record_size);
	}

		files->push_back(cpath);
	if (choice <= from) {
		return new Anabel::ReadQuery(from, to, files, this->record_size);	// response is a single-file wonder
	}

	cpath = cpath.parent_path();

	// Now we will trace thru the filesystem, finding doodz. First up the tree, and then sharp dive towards 'to'
	Timestamp previous_choice = choice;
	while (true) {
		elements = scan_directory(cpath);
		sort(elements.begin(), elements.end(), std::greater<Timestamp>());
		timevectoriter bound = lower_bound(elements.begin(), elements.end(), from, std::greater<Timestamp>());

		if (bound == elements.end()) {	// we need to ascend
			// index files from bound upwards
			for (timevectoriter start_bound = upper_bound(elements.begin(), elements.end(), to, std::greater<Timestamp>()); start_bound != elements.end(); start_bound++) {
				if (previous_choice == *start_bound) continue;
				previous_choice = *start_bound;
				files->push_back(cpath / timestamp_to_string(*start_bound));
			}
			cpath = cpath.parent_path();
			continue;
		}

		// Descent to "bound"
		for (timevectoriter start_bound = upper_bound(elements.begin(), elements.end(), to, std::greater<Timestamp>()); start_bound != bound; start_bound++) {
			if (previous_choice == *start_bound) continue;
			previous_choice = *start_bound;
			files->push_back(cpath / timestamp_to_string(*start_bound));
		}

		cpath = cpath / timestamp_to_string(*bound);
		if (is_regular_file(cpath)) {
			files->push_back(cpath);
			break;
		}
	}

	return new Anabel::ReadQuery(from, to, files, this->record_size);
}

void Anabel::TimeSeries::open(TimeSeriesOpenMode open_mode) {
	// Do the locking!
	/*
		Now, how do I resolve deadlocks? They may happen.
		During a deadlock, second acquisiton will raise an exception. Then, I will release the locks.
		And fun begins again. Maybe backoff algorithm can be used by the upper layer?
	*/
	switch (open_mode) {
		case TSO_APPEND:
			try {
				this->alock->try_lock();
			} catch (boost::interprocess::lock_exception exc) {
				throw TimeSeriesLocked();
			}
			break;
		case TSO_REBALANCE:
			try {
				this->block->try_lock();
			} catch (boost::interprocess::lock_exception exc) {
				throw TimeSeriesLocked();
			}
			break;
		case TSO_READ:
			try {
				this->alock->try_lock_sharable();
			} catch (boost::interprocess::lock_exception exc) {
				throw TimeSeriesLocked();
			}
			try {
				this->block->try_lock_sharable();
			} catch (boost::interprocess::lock_exception exc) {
				this->alock->unlock_sharable();
				throw TimeSeriesLocked();
			}
			break;
		case TSO_WRITE:
			try {
				this->alock->try_lock();
			} catch (boost::interprocess::lock_exception exc) {
				throw TimeSeriesLocked();
			}
			try {
				this->block->try_lock();
			} catch (boost::interprocess::lock_exception exc) {
				this->alock->unlock();
				throw TimeSeriesLocked();
			}
			break;
		case TSO_CLOSED:	// funnt, not undefined though
			throw InvalidInvocation("invalid open_mode");
			break;
		default:
			throw InvalidInvocation("unknown open_mode");
	}

	// Read type of time series, as we don't know it now
	std::ifstream conf((*this->root_path / "record_size").string());
	conf >> this->record_size;
	conf.close();

	this->mode = open_mode;
}

void Anabel::TimeSeries::close(void) {
	switch (this->mode) {
		case TSO_READ:
			this->alock->unlock_sharable();
			this->block->unlock_sharable();
			break;
		case TSO_WRITE:
			this->alock->unlock();
			this->block->unlock();
			break;
		case TSO_REBALANCE:
			this->block->unlock();
			break;
		case TSO_APPEND:
			this->alock->unlock();
			break;
		case TSO_CLOSED:
			break;
	}
}

Anabel::TimeSeries::TimeSeries(std::string rootdirpath) : mode(TSO_CLOSED), record_size(0) {
	// Prepare pathes
	this->root_path = new path(rootdirpath);
	path rsize_path(*this->root_path);
	rsize_path /= "record_size";
	path alock_path(*this->root_path);
	alock_path /= "alock";
	path block_path(*this->root_path);
	block_path /= "block";

	// Sanity-check pathes
	if (!exists(*this->root_path)) throw InvalidRootDirectory("root directory does not exist");
	if (!is_directory(*this->root_path)) throw InvalidRootDirectory("root directory is not a directory");
	if (!exists(rsize_path)) throw InvalidRootDirectory("rsize_path file not found");
	if (!exists(alock_path)) throw InvalidRootDirectory("append lock not found");
	if (!exists(block_path)) throw InvalidRootDirectory("rebalance lock not found");

	// Create locks
	this->alock = new boost::interprocess::file_lock(alock_path.string().c_str());
	this->block = new boost::interprocess::file_lock(block_path.string().c_str());
}
Anabel::TimeSeries::~TimeSeries() {
	if (this->mode != TSO_CLOSED) this->close();
	delete this->alock;
	delete this->block;
	delete this->root_path;
}
<|endoftext|>


<commit_before>#define _LARGEFILE64_SOURCE

#include "util/file.hh"

#include "util/exception.hh"

#include <cstdlib>
#include <cstdio>
#include <iostream>

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>

#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#include <io.h>
#include <algorithm>
#include <limits.h>
#else
#include <unistd.h>
#endif

namespace util {

scoped_fd::~scoped_fd() {
  if (fd_ != -1 && close(fd_)) {
    std::cerr << "Could not close file " << fd_ << std::endl;
    std::abort();
  }
}

scoped_FILE::~scoped_FILE() {
  if (file_ && std::fclose(file_)) {
    std::cerr << "Could not close file " << std::endl;
    std::abort();
  }
}

int OpenReadOrThrow(const char *name) {
  int ret;
#if defined(_WIN32) || defined(_WIN64)
  UTIL_THROW_IF(-1 == (ret = _open(name, _O_BINARY | _O_RDONLY)), ErrnoException, "while opening " << name);
#else
  UTIL_THROW_IF(-1 == (ret = open(name, O_RDONLY)), ErrnoException, "while opening " << name);
#endif
  return ret;
}

int CreateOrThrow(const char *name) {
  int ret;
#if defined(_WIN32) || defined(_WIN64)
  UTIL_THROW_IF(-1 == (ret = _open(name, _O_CREAT | _O_TRUNC | _O_RDWR | _O_BINARY, _S_IREAD | _S_IWRITE)), ErrnoException, "while creating " << name);
#else
  UTIL_THROW_IF(-1 == (ret = open(name, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)), ErrnoException, "while creating " << name);
#endif
  return ret;
}

uint64_t SizeFile(int fd) {
#if defined(_WIN32) || defined(_WIN64)
  __int64 ret = _filelengthi64(fd);
  return (ret == -1) ? kBadSize : ret;
#else
  struct stat sb;
  if (fstat(fd, &sb) == -1 || (!sb.st_size && !S_ISREG(sb.st_mode))) return kBadSize;
  return sb.st_size;
#endif
}

void ResizeOrThrow(int fd, uint64_t to) {
#if defined(_WIN32) || defined(_WIN64)
  UTIL_THROW_IF(_chsize_s(fd, to), ErrnoException, "Resizing to " << to << " bytes failed");
#else
  UTIL_THROW_IF(ftruncate(fd, to), ErrnoException, "Resizing to " << to << " bytes failed");
#endif
}

std::size_t PartialRead(int fd, void *to, std::size_t amount) {
#if defined(_WIN32) || defined(_WIN64)
  amount = min(static_cast<std::size_t>(INT_MAX), amount);
  int ret = _read(fd, to, amount);
#else
  ssize_t ret = read(fd, to, amount);
#endif
  UTIL_THROW_IF(ret < 0, ErrnoException, "Reading " << amount << " from fd " << fd << " failed.");
  return static_cast<std::size_t>(ret);
}

void ReadOrThrow(int fd, void *to_void, std::size_t amount) {
  uint8_t *to = static_cast<uint8_t*>(to_void);
  while (amount) {
    std::size_t ret = PartialRead(fd, to, amount);
    UTIL_THROW_IF(ret == 0, EndOfFileException, " in fd " << fd << " but there should be " << amount << " more bytes to read.");
    amount -= ret;
    to += ret;
  }
}

std::size_t ReadOrEOF(int fd, void *to_void, std::size_t amount) {
  uint8_t *to = static_cast<uint8_t*>(to_void);
  std::size_t remaining = amount;
  while (remaining) {
    std::size_t ret = PartialRead(fd, to, remaining);
    if (!ret) return amount - remaining;
    remaining -= ret;
    to += ret;
  }
  return amount;
}

void WriteOrThrow(int fd, const void *data_void, std::size_t size) {
  const uint8_t *data = static_cast<const uint8_t*>(data_void);
  while (size) {
#if defined(_WIN32) || defined(_WIN64)
    int ret = write(fd, data, min(static_cast<std::size_t>(INT_MAX), size));
#else
    ssize_t ret = write(fd, data, size);
#endif
    if (ret < 1) UTIL_THROW(util::ErrnoException, "Write failed");
    data += ret;
    size -= ret;
  }
}

void WriteOrThrow(FILE *to, const void *data, std::size_t size) {
  assert(size);
  UTIL_THROW_IF(1 != std::fwrite(data, size, 1, to), util::ErrnoException, "Short write; requested size " << size);
}

void FSyncOrThrow(int fd) {
// Apparently windows doesn't have fsync?
#if !defined(_WIN32) && !defined(_WIN64)
  UTIL_THROW_IF(-1 == fsync(fd), ErrnoException, "Sync of " << fd << " failed.");
#endif
}

namespace {
void InternalSeek(int fd, int64_t off, int whence) {
#if defined(_WIN32) || defined(_WIN64)
  UTIL_THROW_IF((__int64)-1 == _lseeki64(fd, off, whence), ErrnoException, "Windows seek failed");

#else
  UTIL_THROW_IF((off_t)-1 == lseek64(fd, off, whence), ErrnoException, "Seek failed");
#endif
}
} // namespace

void SeekOrThrow(int fd, uint64_t off) {
  InternalSeek(fd, off, SEEK_SET);
}

void AdvanceOrThrow(int fd, int64_t off) {
  InternalSeek(fd, off, SEEK_CUR);
}

void SeekEnd(int fd) {
  InternalSeek(fd, 0, SEEK_END);
}

std::FILE *FDOpenOrThrow(scoped_fd &file) {
  std::FILE *ret = fdopen(file.get(), "r+b");
  if (!ret) UTIL_THROW(util::ErrnoException, "Could not fdopen descriptor " << file.get());
  file.release();
  return ret;
}

std::FILE *FDOpenReadOrThrow(scoped_fd &file) {
  std::FILE *ret = fdopen(file.get(), "rb");
  if (!ret) UTIL_THROW(util::ErrnoException, "Could not fdopen descriptor " << file.get());
  file.release();
  return ret;
}

TempMaker::TempMaker(const std::string &prefix) : base_(prefix) {
  base_ += "XXXXXX";
}

// Sigh.  Windows temporary file creation is full of race conditions.
#if defined(_WIN32) || defined(_WIN64)
/* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
   (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.  */

/* This has been modified from the original version to rename the function and
 * set the Windows temporary flag. */

static const char letters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

/* Generate a temporary file name based on TMPL.  TMPL must match the
   rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
   does not exist at the time of the call to mkstemp.  TMPL is
   overwritten with the result.  */
int
mkstemp_and_unlink(char *tmpl)
{
  int len;
  char *XXXXXX;
  static unsigned long long value;
  unsigned long long random_time_bits;
  unsigned int count;
  int fd = -1;
  int save_errno = errno;

  /* A lower bound on the number of temporary files to attempt to
     generate.  The maximum total number of temporary file names that
     can exist for a given template is 62**6.  It should never be
     necessary to try all these combinations.  Instead if a reasonable
     number of names is tried (we define reasonable as 62**3) fail to
     give the system administrator the chance to remove the problems.  */
#define ATTEMPTS_MIN (62 * 62 * 62)

  /* The number of times to attempt to generate a temporary file.  To
     conform to POSIX, this must be no smaller than TMP_MAX.  */
#if ATTEMPTS_MIN < TMP_MAX
  unsigned int attempts = TMP_MAX;
#else
  unsigned int attempts = ATTEMPTS_MIN;
#endif

  len = strlen (tmpl);
  if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
    {
      errno = EINVAL;
      return -1;
    }

/* This is where the Xs start.  */
  XXXXXX = &tmpl[len - 6];

  /* Get some more or less random data.  */
  {
    SYSTEMTIME      stNow;
    FILETIME ftNow;

    // get system time
    GetSystemTime(&stNow);
    stNow.wMilliseconds = 500;
    if (!SystemTimeToFileTime(&stNow, &ftNow))
    {
        errno = -1;
        return -1;
    }

    random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32)
                        | (unsigned long long)ftNow.dwLowDateTime);
  }
  value += random_time_bits ^ (unsigned long long)GetCurrentThreadId ();

  for (count = 0; count < attempts; value += 7777, ++count)
  {
    unsigned long long v = value;

    /* Fill in the random bits.  */
    XXXXXX[0] = letters[v % 62];
    v /= 62;
    XXXXXX[1] = letters[v % 62];
    v /= 62;
    XXXXXX[2] = letters[v % 62];
    v /= 62;
    XXXXXX[3] = letters[v % 62];
    v /= 62;
    XXXXXX[4] = letters[v % 62];
    v /= 62;
    XXXXXX[5] = letters[v % 62];

    /* Modified for windows and to unlink */
    //      fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE);
    int flags = _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY;
    flags |= _O_TEMPORARY;
    fd = _open (tmpl, flags, _S_IREAD | _S_IWRITE);
    if (fd >= 0)
    {
      errno = save_errno;
      return fd;
    }
    else if (errno != EEXIST)
      return -1;
  }

  /* We got out of the loop because we ran out of combinations to try.  */
  errno = EEXIST;
  return -1;
}
#else
int
mkstemp_and_unlink(char *tmpl) {
  int ret = mkstemp(tmpl);
  if (ret != -1) {
    UTIL_THROW_IF(unlink(tmpl), util::ErrnoException, "Failed to delete " << tmpl);
  }
  return ret;
}
#endif

int TempMaker::Make() const {
  std::string name(base_);
  name.push_back(0);
  int ret;
  UTIL_THROW_IF(-1 == (ret = mkstemp_and_unlink(&name[0])), util::ErrnoException, "Failed to make a temporary based on " << base_);
  return ret;
}

std::FILE *TempMaker::MakeFile() const {
  util::scoped_fd file(Make());
  return FDOpenOrThrow(file);
}

} // namespace util
<commit_msg>mac doesn't have lseek64()<commit_after>#define _LARGEFILE64_SOURCE

#include "util/file.hh"

#include "util/exception.hh"

#include <cstdlib>
#include <cstdio>
#include <iostream>

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>

#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#include <io.h>
#include <algorithm>
#include <limits.h>
#else
#include <unistd.h>
#endif

#ifdef __APPLE__ & __MACH__
#define lseek64 lseek
#define open64 open
#endif

namespace util {

scoped_fd::~scoped_fd() {
  if (fd_ != -1 && close(fd_)) {
    std::cerr << "Could not close file " << fd_ << std::endl;
    std::abort();
  }
}

scoped_FILE::~scoped_FILE() {
  if (file_ && std::fclose(file_)) {
    std::cerr << "Could not close file " << std::endl;
    std::abort();
  }
}

int OpenReadOrThrow(const char *name) {
  int ret;
#if defined(_WIN32) || defined(_WIN64)
  UTIL_THROW_IF(-1 == (ret = _open(name, _O_BINARY | _O_RDONLY)), ErrnoException, "while opening " << name);
#else
  UTIL_THROW_IF(-1 == (ret = open(name, O_RDONLY)), ErrnoException, "while opening " << name);
#endif
  return ret;
}

int CreateOrThrow(const char *name) {
  int ret;
#if defined(_WIN32) || defined(_WIN64)
  UTIL_THROW_IF(-1 == (ret = _open(name, _O_CREAT | _O_TRUNC | _O_RDWR | _O_BINARY, _S_IREAD | _S_IWRITE)), ErrnoException, "while creating " << name);
#else
  UTIL_THROW_IF(-1 == (ret = open(name, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)), ErrnoException, "while creating " << name);
#endif
  return ret;
}

uint64_t SizeFile(int fd) {
#if defined(_WIN32) || defined(_WIN64)
  __int64 ret = _filelengthi64(fd);
  return (ret == -1) ? kBadSize : ret;
#else
  struct stat sb;
  if (fstat(fd, &sb) == -1 || (!sb.st_size && !S_ISREG(sb.st_mode))) return kBadSize;
  return sb.st_size;
#endif
}

void ResizeOrThrow(int fd, uint64_t to) {
#if defined(_WIN32) || defined(_WIN64)
  UTIL_THROW_IF(_chsize_s(fd, to), ErrnoException, "Resizing to " << to << " bytes failed");
#else
  UTIL_THROW_IF(ftruncate(fd, to), ErrnoException, "Resizing to " << to << " bytes failed");
#endif
}

std::size_t PartialRead(int fd, void *to, std::size_t amount) {
#if defined(_WIN32) || defined(_WIN64)
  amount = min(static_cast<std::size_t>(INT_MAX), amount);
  int ret = _read(fd, to, amount);
#else
  ssize_t ret = read(fd, to, amount);
#endif
  UTIL_THROW_IF(ret < 0, ErrnoException, "Reading " << amount << " from fd " << fd << " failed.");
  return static_cast<std::size_t>(ret);
}

void ReadOrThrow(int fd, void *to_void, std::size_t amount) {
  uint8_t *to = static_cast<uint8_t*>(to_void);
  while (amount) {
    std::size_t ret = PartialRead(fd, to, amount);
    UTIL_THROW_IF(ret == 0, EndOfFileException, " in fd " << fd << " but there should be " << amount << " more bytes to read.");
    amount -= ret;
    to += ret;
  }
}

std::size_t ReadOrEOF(int fd, void *to_void, std::size_t amount) {
  uint8_t *to = static_cast<uint8_t*>(to_void);
  std::size_t remaining = amount;
  while (remaining) {
    std::size_t ret = PartialRead(fd, to, remaining);
    if (!ret) return amount - remaining;
    remaining -= ret;
    to += ret;
  }
  return amount;
}

void WriteOrThrow(int fd, const void *data_void, std::size_t size) {
  const uint8_t *data = static_cast<const uint8_t*>(data_void);
  while (size) {
#if defined(_WIN32) || defined(_WIN64)
    int ret = write(fd, data, min(static_cast<std::size_t>(INT_MAX), size));
#else
    ssize_t ret = write(fd, data, size);
#endif
    if (ret < 1) UTIL_THROW(util::ErrnoException, "Write failed");
    data += ret;
    size -= ret;
  }
}

void WriteOrThrow(FILE *to, const void *data, std::size_t size) {
  assert(size);
  UTIL_THROW_IF(1 != std::fwrite(data, size, 1, to), util::ErrnoException, "Short write; requested size " << size);
}

void FSyncOrThrow(int fd) {
// Apparently windows doesn't have fsync?
#if !defined(_WIN32) && !defined(_WIN64)
  UTIL_THROW_IF(-1 == fsync(fd), ErrnoException, "Sync of " << fd << " failed.");
#endif
}

namespace {
void InternalSeek(int fd, int64_t off, int whence) {
#if defined(_WIN32) || defined(_WIN64)
  UTIL_THROW_IF((__int64)-1 == _lseeki64(fd, off, whence), ErrnoException, "Windows seek failed");

#else
  UTIL_THROW_IF((off_t)-1 == lseek64(fd, off, whence), ErrnoException, "Seek failed");
#endif
}
} // namespace

void SeekOrThrow(int fd, uint64_t off) {
  InternalSeek(fd, off, SEEK_SET);
}

void AdvanceOrThrow(int fd, int64_t off) {
  InternalSeek(fd, off, SEEK_CUR);
}

void SeekEnd(int fd) {
  InternalSeek(fd, 0, SEEK_END);
}

std::FILE *FDOpenOrThrow(scoped_fd &file) {
  std::FILE *ret = fdopen(file.get(), "r+b");
  if (!ret) UTIL_THROW(util::ErrnoException, "Could not fdopen descriptor " << file.get());
  file.release();
  return ret;
}

std::FILE *FDOpenReadOrThrow(scoped_fd &file) {
  std::FILE *ret = fdopen(file.get(), "rb");
  if (!ret) UTIL_THROW(util::ErrnoException, "Could not fdopen descriptor " << file.get());
  file.release();
  return ret;
}

TempMaker::TempMaker(const std::string &prefix) : base_(prefix) {
  base_ += "XXXXXX";
}

// Sigh.  Windows temporary file creation is full of race conditions.
#if defined(_WIN32) || defined(_WIN64)
/* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
   (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.  */

/* This has been modified from the original version to rename the function and
 * set the Windows temporary flag. */

static const char letters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

/* Generate a temporary file name based on TMPL.  TMPL must match the
   rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
   does not exist at the time of the call to mkstemp.  TMPL is
   overwritten with the result.  */
int
mkstemp_and_unlink(char *tmpl)
{
  int len;
  char *XXXXXX;
  static unsigned long long value;
  unsigned long long random_time_bits;
  unsigned int count;
  int fd = -1;
  int save_errno = errno;

  /* A lower bound on the number of temporary files to attempt to
     generate.  The maximum total number of temporary file names that
     can exist for a given template is 62**6.  It should never be
     necessary to try all these combinations.  Instead if a reasonable
     number of names is tried (we define reasonable as 62**3) fail to
     give the system administrator the chance to remove the problems.  */
#define ATTEMPTS_MIN (62 * 62 * 62)

  /* The number of times to attempt to generate a temporary file.  To
     conform to POSIX, this must be no smaller than TMP_MAX.  */
#if ATTEMPTS_MIN < TMP_MAX
  unsigned int attempts = TMP_MAX;
#else
  unsigned int attempts = ATTEMPTS_MIN;
#endif

  len = strlen (tmpl);
  if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
    {
      errno = EINVAL;
      return -1;
    }

/* This is where the Xs start.  */
  XXXXXX = &tmpl[len - 6];

  /* Get some more or less random data.  */
  {
    SYSTEMTIME      stNow;
    FILETIME ftNow;

    // get system time
    GetSystemTime(&stNow);
    stNow.wMilliseconds = 500;
    if (!SystemTimeToFileTime(&stNow, &ftNow))
    {
        errno = -1;
        return -1;
    }

    random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32)
                        | (unsigned long long)ftNow.dwLowDateTime);
  }
  value += random_time_bits ^ (unsigned long long)GetCurrentThreadId ();

  for (count = 0; count < attempts; value += 7777, ++count)
  {
    unsigned long long v = value;

    /* Fill in the random bits.  */
    XXXXXX[0] = letters[v % 62];
    v /= 62;
    XXXXXX[1] = letters[v % 62];
    v /= 62;
    XXXXXX[2] = letters[v % 62];
    v /= 62;
    XXXXXX[3] = letters[v % 62];
    v /= 62;
    XXXXXX[4] = letters[v % 62];
    v /= 62;
    XXXXXX[5] = letters[v % 62];

    /* Modified for windows and to unlink */
    //      fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE);
    int flags = _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY;
    flags |= _O_TEMPORARY;
    fd = _open (tmpl, flags, _S_IREAD | _S_IWRITE);
    if (fd >= 0)
    {
      errno = save_errno;
      return fd;
    }
    else if (errno != EEXIST)
      return -1;
  }

  /* We got out of the loop because we ran out of combinations to try.  */
  errno = EEXIST;
  return -1;
}
#else
int
mkstemp_and_unlink(char *tmpl) {
  int ret = mkstemp(tmpl);
  if (ret != -1) {
    UTIL_THROW_IF(unlink(tmpl), util::ErrnoException, "Failed to delete " << tmpl);
  }
  return ret;
}
#endif

int TempMaker::Make() const {
  std::string name(base_);
  name.push_back(0);
  int ret;
  UTIL_THROW_IF(-1 == (ret = mkstemp_and_unlink(&name[0])), util::ErrnoException, "Failed to make a temporary based on " << base_);
  return ret;
}

std::FILE *TempMaker::MakeFile() const {
  util::scoped_fd file(Make());
  return FDOpenOrThrow(file);
}

} // namespace util
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_OUTBOUND_PATH_HPP
#define CAF_OUTBOUND_PATH_HPP

#include <deque>
#include <vector>
#include <cstdint>
#include <cstddef>

#include "caf/actor_control_block.hpp"
#include "caf/downstream_msg.hpp"
#include "caf/fwd.hpp"
#include "caf/logger.hpp"
#include "caf/stream_aborter.hpp"
#include "caf/stream_slot.hpp"
#include "caf/system_messages.hpp"

#include "caf/meta/type_name.hpp"

namespace caf {

/// State for a single path to a sink of a `downstream_manager`.
class outbound_path {
public:
  // -- member types -----------------------------------------------------------

  /// Propagates graceful shutdowns.
  using regular_shutdown = downstream_msg::close;

  /// Propagates errors.
  using irregular_shutdown = downstream_msg::forced_close;

  /// Stores batches until receiving corresponding ACKs.
  using cache_type = std::deque<std::pair<int64_t, downstream_msg::batch>>;

  // -- constants --------------------------------------------------------------

  /// Stream aborter flag to monitor a path.
  static constexpr const auto aborter_type = stream_aborter::sink_aborter;

  // -- constructors, destructors, and assignment operators --------------------

  /// Constructs a pending path for given slot and handle.
  outbound_path(stream_slot sender_slot, strong_actor_ptr receiver_hdl);

  ~outbound_path();

  // -- downstream communication -----------------------------------------------

  /// Sends an `open_stream_msg` handshake.
  static void emit_open(local_actor* self, stream_slot slot,
                        strong_actor_ptr to, message handshake_data,
                        stream_priority prio);

  /// Sends a `downstream_msg::batch` on this path. Decrements `open_credit` by
  /// `xs_size` and increments `next_batch_id` by 1.
  void emit_batch(local_actor* self, long xs_size, message xs);

  /// Calls `emit_batch` for each chunk in the cache, whereas each chunk is of
  /// size `desired_batch_size`. Does nothing for pending paths.
  template <class T>
  void emit_batches(local_actor* self, std::vector<T>& cache,
                    bool force_underfull) {
    CAF_LOG_TRACE(CAF_ARG(cache) << CAF_ARG(force_underfull));
    if (pending())
      return;
    CAF_ASSERT(desired_batch_size > 0);
    if (cache.size() == desired_batch_size) {
      emit_batch(self, desired_batch_size, make_message(std::move(cache)));
      return;
    }
    auto i = cache.begin();
    auto e = cache.end();
    while (std::distance(i, e) >= static_cast<ptrdiff_t>(desired_batch_size)) {
      std::vector<T> tmp{std::make_move_iterator(i),
                         std::make_move_iterator(i + desired_batch_size)};
      emit_batch(self, desired_batch_size, make_message(std::move(tmp)));
      i += desired_batch_size;
    }
    if (i == e) {
      cache.clear();
      return;
    }
    cache.erase(cache.begin(), i);
    if (force_underfull)
      emit_batch(self, cache.size(), make_message(std::move(cache)));
  }

  /// Sends a `downstream_msg::close` on this path.
  void emit_regular_shutdown(local_actor* self);

  /// Sends a `downstream_msg::forced_close` on this path.
  void emit_irregular_shutdown(local_actor* self, error reason);

  /// Sends a `downstream_msg::forced_close`.
  static void emit_irregular_shutdown(local_actor* self, stream_slots slots,
                                      const strong_actor_ptr& hdl,
                                      error reason);

  // -- properties -------------------------------------------------------------

  /// Returns whether this path is pending, i.e., didn't receive an `ack_open`
  /// yet.
  inline bool pending() const noexcept {
    return slots.receiver == invalid_stream_slot;
  }

  // -- member variables -------------------------------------------------------

  /// Slot IDs for sender (self) and receiver (hdl).
  stream_slots slots;

  /// Handle to the sink.
  strong_actor_ptr hdl;

  /// Next expected batch ID.
  int64_t next_batch_id;

  /// Currently available credit on this path.
  long open_credit;

  /// Ideal batch size. Configured by the sink.
  uint64_t desired_batch_size;

  /// ID of the first unacknowledged batch. Note that CAF uses accumulative
  /// ACKs, i.e., receiving an ACK with a higher ID is not an error.
  int64_t next_ack_id;
};

/// @relates outbound_path
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, outbound_path& x) {
  return f(meta::type_name("outbound_path"), x.slots, x.hdl, x.next_batch_id,
           x.open_credit, x.desired_batch_size, x.next_ack_id);
}

} // namespace caf

#endif // CAF_OUTBOUND_PATH_HPP
<commit_msg>Deal with caches greater than available credit<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_OUTBOUND_PATH_HPP
#define CAF_OUTBOUND_PATH_HPP

#include <deque>
#include <vector>
#include <cstdint>
#include <cstddef>

#include "caf/actor_control_block.hpp"
#include "caf/downstream_msg.hpp"
#include "caf/fwd.hpp"
#include "caf/logger.hpp"
#include "caf/stream_aborter.hpp"
#include "caf/stream_slot.hpp"
#include "caf/system_messages.hpp"

#include "caf/detail/type_traits.hpp"

#include "caf/meta/type_name.hpp"

namespace caf {

/// State for a single path to a sink of a `downstream_manager`.
class outbound_path {
public:
  // -- member types -----------------------------------------------------------

  /// Propagates graceful shutdowns.
  using regular_shutdown = downstream_msg::close;

  /// Propagates errors.
  using irregular_shutdown = downstream_msg::forced_close;

  /// Stores batches until receiving corresponding ACKs.
  using cache_type = std::deque<std::pair<int64_t, downstream_msg::batch>>;

  // -- constants --------------------------------------------------------------

  /// Stream aborter flag to monitor a path.
  static constexpr const auto aborter_type = stream_aborter::sink_aborter;

  // -- constructors, destructors, and assignment operators --------------------

  /// Constructs a pending path for given slot and handle.
  outbound_path(stream_slot sender_slot, strong_actor_ptr receiver_hdl);

  ~outbound_path();

  // -- downstream communication -----------------------------------------------

  /// Sends an `open_stream_msg` handshake.
  static void emit_open(local_actor* self, stream_slot slot,
                        strong_actor_ptr to, message handshake_data,
                        stream_priority prio);

  /// Sends a `downstream_msg::batch` on this path. Decrements `open_credit` by
  /// `xs_size` and increments `next_batch_id` by 1.
  void emit_batch(local_actor* self, long xs_size, message xs);

  template <class Iterator>
  Iterator emit_batches_impl(local_actor* self, Iterator i, Iterator e,
                             bool force_underfull) {
    CAF_LOG_TRACE(CAF_ARG(force_underfull));
    using type = detail::decay_t<decltype(*i)>;
    while (std::distance(i, e) >= static_cast<ptrdiff_t>(desired_batch_size)) {
      std::vector<type> tmp{std::make_move_iterator(i),
                            std::make_move_iterator(i + desired_batch_size)};
      emit_batch(self, desired_batch_size, make_message(std::move(tmp)));
      i += desired_batch_size;
    }
    if (i != e && force_underfull) {
      std::vector<type> tmp{std::make_move_iterator(i),
                            std::make_move_iterator(e)};
      emit_batch(self, tmp.size(), make_message(std::move(tmp)));
      return e;
    }
    return i;
  }

  /// Calls `emit_batch` for each chunk in the cache, whereas each chunk is of
  /// size `desired_batch_size`. Does nothing for pending paths.
  template <class T>
  void emit_batches(local_actor* self, std::vector<T>& cache,
                    bool force_underfull) {
    CAF_LOG_TRACE(CAF_ARG(cache) << CAF_ARG(force_underfull));
    if (pending())
      return;
    CAF_ASSERT(desired_batch_size > 0);
    auto first = cache.begin();
    auto last = first + std::min(open_credit, static_cast<long>(cache.size()));
    if (first == last)
      return;
    auto i = emit_batches_impl(self, first, last, force_underfull);
    if (i == cache.end()) {
      cache.clear();
    } else if (i != first) {
      cache.erase(first, i);
    }
  }

  /// Sends a `downstream_msg::close` on this path.
  void emit_regular_shutdown(local_actor* self);

  /// Sends a `downstream_msg::forced_close` on this path.
  void emit_irregular_shutdown(local_actor* self, error reason);

  /// Sends a `downstream_msg::forced_close`.
  static void emit_irregular_shutdown(local_actor* self, stream_slots slots,
                                      const strong_actor_ptr& hdl,
                                      error reason);

  // -- properties -------------------------------------------------------------

  /// Returns whether this path is pending, i.e., didn't receive an `ack_open`
  /// yet.
  inline bool pending() const noexcept {
    return slots.receiver == invalid_stream_slot;
  }

  // -- member variables -------------------------------------------------------

  /// Slot IDs for sender (self) and receiver (hdl).
  stream_slots slots;

  /// Handle to the sink.
  strong_actor_ptr hdl;

  /// Next expected batch ID.
  int64_t next_batch_id;

  /// Currently available credit on this path.
  long open_credit;

  /// Ideal batch size. Configured by the sink.
  uint64_t desired_batch_size;

  /// ID of the first unacknowledged batch. Note that CAF uses accumulative
  /// ACKs, i.e., receiving an ACK with a higher ID is not an error.
  int64_t next_ack_id;
};

/// @relates outbound_path
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, outbound_path& x) {
  return f(meta::type_name("outbound_path"), x.slots, x.hdl, x.next_batch_id,
           x.open_credit, x.desired_batch_size, x.next_ack_id);
}

} // namespace caf

#endif // CAF_OUTBOUND_PATH_HPP
<|endoftext|>