<commit_before>/*
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks 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 COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/////////////////////////////////////////////////////////////////////////
/// \file
///
/// Shader interpreter implementation of assignment: '=' in all its
/// flavors.
///
/////////////////////////////////////////////////////////////////////////

#include <iostream>

#include "oslexec_pvt.h"
#include "oslops.h"
#include "dual.h"

#include "OpenImageIO/varyingref.h"

#ifdef OSL_NAMESPACE
namespace OSL_NAMESPACE {
#endif
namespace OSL {
namespace pvt {

// Heavy lifting of 'assign', this is a specialized version that knows
// the types of the arguments.
template <class RET, class SRC>
static DECLOP (specialized_assign)
{
    // Get references to the symbols this op accesses
    Symbol &Result (exec->sym (args[0]));
    Symbol &Src (exec->sym (args[1]));

    // Adjust the result's uniform/varying status
    exec->adjust_varying (Result, Src.is_varying(),
                          Result.data() == Src.data());

    // Loop over points, do the assignment.
    VaryingRef<RET> result ((RET *)Result.data(), Result.step());
    VaryingRef<SRC> src ((SRC *)Src.data(), Src.step());
    if (result.is_uniform()) {
        // Uniform case
        *result = RET (*src);
        if (Result.has_derivs())
            exec->zero_derivs (Result);
    } else {
        // Potentially varying case
        if (!std::numeric_limits<RET>::is_integer &&
            !std::numeric_limits<SRC>::is_integer &&
                 Result.has_derivs() && Src.has_derivs()) {
            VaryingRef<Dual2<RET> > result ((Dual2<RET> *)Result.data(), Result.step());
            VaryingRef<Dual2<SRC> > src ((Dual2<SRC> *)Src.data(), Src.step());
            SHADE_LOOP (
                result[i] = Dual2<RET> (src[i]);
            )
        } else {
            SHADE_LOOP (
                result[i] = RET (src[i]);
            )
            if (Result.has_derivs())
                exec->zero_derivs (Result);
        }
    }
}

// Special version of assign for when the source and result are the same
// exact type, so we can just memcpy.
static DECLOP (assign_copy)
{
    // Get references to the symbols this op accesses
    Symbol &Result (exec->sym (args[0]));
    Symbol &Src (exec->sym (args[1]));

    // Adjust the result's uniform/varying status
    exec->adjust_varying (Result, Src.is_varying(),
                          Result.data() == Src.data());

    size_t size = Result.size ();
    if (Result.has_derivs()) {
        if (Src.has_derivs())            // Both have derivs...
            size *= 3;                   //    memcpy the derivs too
        else                             // Result needs derivs but
            exec->zero_derivs (Result);  //   src doesn't have -> zero
    }

    // Loop over points, do the assignment.
    if (Result.is_uniform()) {
        // Uniform case
        memcpy (Result.data(), Src.data(), size);
#if 0
    } else if (exec->all_points_on() && Src.is_varying() &&
               Result.has_derivs() == Src.has_derivs()) {
        // Simple case where a single memcpy will do
        memcpy (Result.data(), Src.data(), Src.step() * exec->npoints());
#endif
    } else {
        // Potentially varying case
        VaryingRef<char> result ((char *)Result.data(), Result.step());
        VaryingRef<char> src ((char *)Src.data(), Src.step());
        SHADE_LOOP_BEGIN
            memcpy (&result[i], &src[i], size);
        SHADE_LOOP_END
    }
}

static void
assign_closure (ShadingExecution *exec, int nargs, const int *args)
{
    // Get references to the symbols this op accesses
    Symbol &Result (exec->sym (args[0]));
    Symbol &Src (exec->sym (args[1]));

    // closures are always varying
    DASSERT(Result.typespec().is_closure() && Result.is_varying());
    DASSERT(Src.typespec().is_closure() && Src.is_varying());

    // Loop over points, do the assignment.
    VaryingRef<ClosureColor *> result ((ClosureColor **)Result.data(), Result.step());
    VaryingRef<ClosureColor *> src ((ClosureColor **)Src.data(), Src.step());
    SHADE_LOOP_BEGIN
        *(result[i]) = *(src[i]);
    SHADE_LOOP_END
    // N.B. You can't take a derivative of a closure
}

static void
clear_closure (ShadingExecution *exec, int nargs, const int *args)
{
    // Get references to the symbols this op accesses
    Symbol &Result (exec->sym (args[0]));

    // closures are always varying
    DASSERT(Result.typespec().is_closure() && Result.is_varying());

    // Loop over points, do the assignment.
    VaryingRef<ClosureColor *> result ((ClosureColor **)Result.data(), Result.step());
    SHADE_LOOP_BEGIN
        result[i]->clear ();
    SHADE_LOOP_END

    // N.B. You can't take a derivative of a closure
}

DECLOP (OP_assign)
{
    ASSERT (nargs == 2);
    Symbol &Result (exec->sym (args[0]));
    Symbol &Src (exec->sym (args[1]));
    ASSERT (! Result.typespec().is_structure() &&
            ! Result.typespec().is_array());   // Not yet
    ASSERT (! Src.typespec().is_structure() &&
            ! Src.typespec().is_array());   // Not yet
    OpImpl impl = NULL;
    if (Result.typespec().is_closure() || Src.typespec().is_closure()) {
        if (Result.typespec().is_closure() && Src.typespec().is_closure())
            impl = assign_closure;
        else if (Result.typespec().is_closure() &&
                 (Src.typespec().is_int() || Src.typespec().is_float())) {
            // Special case:  closure = 0 -> clear it
            impl = clear_closure;
        }
        // otherwise, it's an error
    } else if (Result.typespec().is_structure()) {
        // FIXME -- not handled yet
    } else if (Result.typespec().simpletype() == Src.typespec().simpletype()) {
        // Easy case -- the two types are exactly the same.  That's
        // always legal and has a very specialized implementation.  This
        // case handles f=f, p=p, v=v, n=n, c=c, s=s, m=m.
        impl = assign_copy;
    } else if (Result.typespec().is_float()) {
        if (Src.typespec().is_int())
            impl = specialized_assign<float,int>;
    } else if (Result.typespec().is_int()) {
        if (Src.typespec().is_float())
            impl = specialized_assign<int,float>;
    } else if (Result.typespec().is_triple()) {
        if (Src.typespec().is_triple())
            impl = assign_copy;  // p=n, v=p, etc.
        else if (Src.typespec().is_float())
            impl = specialized_assign<Vec3,float>;
        else if (Src.typespec().is_int())
            impl = specialized_assign<Vec3,int>;
    } else if (Result.typespec().is_matrix()) {
        if (Src.typespec().is_float())
            impl = specialized_assign<MatrixProxy,float>;
        else if (Src.typespec().is_int())
            impl = specialized_assign<MatrixProxy,int>;
    }
    if (impl) {
        impl (exec, nargs, args);
        // Use the specialized one for next time!  Never have to check the
        // types or do the other sanity checks again.
        // FIXME -- is this thread-safe?
        exec->op().implementation (impl);
        return;
    } else {
        exec->error ("Don't know how to assign %s = %s",
                     Result.typespec().c_str(), Src.typespec().c_str());
        ASSERT (0 && "Assignment types can't be handled");
    }
}

}; // namespace pvt
}; // namespace OSL
#ifdef OSL_NAMESPACE
}; // end namespace OSL_NAMESPACE
#endif
<commit_msg>Bug fix -- runtime optimization could at times generate closure=color_0 (derived from closure*=color_foo, where foo turned out to be zero).  But this was asserting at runtime, so we fixed. http://codereview.appspot.com/737041/show Review by: ckulla<commit_after>/*
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks 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 COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/////////////////////////////////////////////////////////////////////////
/// \file
///
/// Shader interpreter implementation of assignment: '=' in all its
/// flavors.
///
/////////////////////////////////////////////////////////////////////////

#include <iostream>

#include "oslexec_pvt.h"
#include "oslops.h"
#include "dual.h"

#include "OpenImageIO/varyingref.h"

#ifdef OSL_NAMESPACE
namespace OSL_NAMESPACE {
#endif
namespace OSL {
namespace pvt {

// Heavy lifting of 'assign', this is a specialized version that knows
// the types of the arguments.
template <class RET, class SRC>
static DECLOP (specialized_assign)
{
    // Get references to the symbols this op accesses
    Symbol &Result (exec->sym (args[0]));
    Symbol &Src (exec->sym (args[1]));

    // Adjust the result's uniform/varying status
    exec->adjust_varying (Result, Src.is_varying(),
                          Result.data() == Src.data());

    // Loop over points, do the assignment.
    VaryingRef<RET> result ((RET *)Result.data(), Result.step());
    VaryingRef<SRC> src ((SRC *)Src.data(), Src.step());
    if (result.is_uniform()) {
        // Uniform case
        *result = RET (*src);
        if (Result.has_derivs())
            exec->zero_derivs (Result);
    } else {
        // Potentially varying case
        if (!std::numeric_limits<RET>::is_integer &&
            !std::numeric_limits<SRC>::is_integer &&
                 Result.has_derivs() && Src.has_derivs()) {
            VaryingRef<Dual2<RET> > result ((Dual2<RET> *)Result.data(), Result.step());
            VaryingRef<Dual2<SRC> > src ((Dual2<SRC> *)Src.data(), Src.step());
            SHADE_LOOP (
                result[i] = Dual2<RET> (src[i]);
            )
        } else {
            SHADE_LOOP (
                result[i] = RET (src[i]);
            )
            if (Result.has_derivs())
                exec->zero_derivs (Result);
        }
    }
}

// Special version of assign for when the source and result are the same
// exact type, so we can just memcpy.
static DECLOP (assign_copy)
{
    // Get references to the symbols this op accesses
    Symbol &Result (exec->sym (args[0]));
    Symbol &Src (exec->sym (args[1]));

    // Adjust the result's uniform/varying status
    exec->adjust_varying (Result, Src.is_varying(),
                          Result.data() == Src.data());

    size_t size = Result.size ();
    if (Result.has_derivs()) {
        if (Src.has_derivs())            // Both have derivs...
            size *= 3;                   //    memcpy the derivs too
        else                             // Result needs derivs but
            exec->zero_derivs (Result);  //   src doesn't have -> zero
    }

    // Loop over points, do the assignment.
    if (Result.is_uniform()) {
        // Uniform case
        memcpy (Result.data(), Src.data(), size);
#if 0
    } else if (exec->all_points_on() && Src.is_varying() &&
               Result.has_derivs() == Src.has_derivs()) {
        // Simple case where a single memcpy will do
        memcpy (Result.data(), Src.data(), Src.step() * exec->npoints());
#endif
    } else {
        // Potentially varying case
        VaryingRef<char> result ((char *)Result.data(), Result.step());
        VaryingRef<char> src ((char *)Src.data(), Src.step());
        SHADE_LOOP_BEGIN
            memcpy (&result[i], &src[i], size);
        SHADE_LOOP_END
    }
}

static void
assign_closure (ShadingExecution *exec, int nargs, const int *args)
{
    // Get references to the symbols this op accesses
    Symbol &Result (exec->sym (args[0]));
    Symbol &Src (exec->sym (args[1]));

    // closures are always varying
    DASSERT(Result.typespec().is_closure() && Result.is_varying());
    DASSERT(Src.typespec().is_closure() && Src.is_varying());

    // Loop over points, do the assignment.
    VaryingRef<ClosureColor *> result ((ClosureColor **)Result.data(), Result.step());
    VaryingRef<ClosureColor *> src ((ClosureColor **)Src.data(), Src.step());
    SHADE_LOOP_BEGIN
        *(result[i]) = *(src[i]);
    SHADE_LOOP_END
    // N.B. You can't take a derivative of a closure
}

static void
clear_closure (ShadingExecution *exec, int nargs, const int *args)
{
    // Get references to the symbols this op accesses
    Symbol &Result (exec->sym (args[0]));

    // closures are always varying
    DASSERT(Result.typespec().is_closure() && Result.is_varying());

    // Loop over points, do the assignment.
    VaryingRef<ClosureColor *> result ((ClosureColor **)Result.data(), Result.step());
    SHADE_LOOP_BEGIN
        result[i]->clear ();
    SHADE_LOOP_END

    // N.B. You can't take a derivative of a closure
}

DECLOP (OP_assign)
{
    ASSERT (nargs == 2);
    Symbol &Result (exec->sym (args[0]));
    Symbol &Src (exec->sym (args[1]));
    ASSERT (! Result.typespec().is_structure() &&
            ! Result.typespec().is_array());   // Not yet
    ASSERT (! Src.typespec().is_structure() &&
            ! Src.typespec().is_array());   // Not yet
    OpImpl impl = NULL;
    if (Result.typespec().is_closure() || Src.typespec().is_closure()) {
        if (Result.typespec().is_closure() && Src.typespec().is_closure())
            impl = assign_closure;
        else if (Result.typespec().is_closure() &&
                 (Src.typespec().is_int() || Src.typespec().is_float() ||
                  Src.typespec().is_color())) {
            // Special case:  closure = 0 -> clear it
            impl = clear_closure;
        }
        // otherwise, it's an error
    } else if (Result.typespec().is_structure()) {
        // FIXME -- not handled yet
    } else if (Result.typespec().simpletype() == Src.typespec().simpletype()) {
        // Easy case -- the two types are exactly the same.  That's
        // always legal and has a very specialized implementation.  This
        // case handles f=f, p=p, v=v, n=n, c=c, s=s, m=m.
        impl = assign_copy;
    } else if (Result.typespec().is_float()) {
        if (Src.typespec().is_int())
            impl = specialized_assign<float,int>;
    } else if (Result.typespec().is_int()) {
        if (Src.typespec().is_float())
            impl = specialized_assign<int,float>;
    } else if (Result.typespec().is_triple()) {
        if (Src.typespec().is_triple())
            impl = assign_copy;  // p=n, v=p, etc.
        else if (Src.typespec().is_float())
            impl = specialized_assign<Vec3,float>;
        else if (Src.typespec().is_int())
            impl = specialized_assign<Vec3,int>;
    } else if (Result.typespec().is_matrix()) {
        if (Src.typespec().is_float())
            impl = specialized_assign<MatrixProxy,float>;
        else if (Src.typespec().is_int())
            impl = specialized_assign<MatrixProxy,int>;
    }
    if (impl) {
        impl (exec, nargs, args);
        // Use the specialized one for next time!  Never have to check the
        // types or do the other sanity checks again.
        // FIXME -- is this thread-safe?
        exec->op().implementation (impl);
        return;
    } else {
        exec->error ("Don't know how to assign %s = %s",
                     Result.typespec().c_str(), Src.typespec().c_str());
        ASSERT (0 && "Assignment types can't be handled");
    }
}

}; // namespace pvt
}; // namespace OSL
#ifdef OSL_NAMESPACE
}; // end namespace OSL_NAMESPACE
#endif
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "persistencemessagetracker.h"
#include <vespa/storage/common/vectorprinter.h>
#include <vespa/storage/common/bucketoperationlogger.h>
#include <vespa/storageapi/message/persistence.h>
#include "distributor_bucket_space_repo.h"
#include "distributor_bucket_space.h"

#include <vespa/log/log.h>

LOG_SETUP(".persistencemessagetracker");

namespace storage::distributor {

PersistenceMessageTrackerImpl::PersistenceMessageTrackerImpl(
        PersistenceOperationMetricSet& metric,
        std::shared_ptr<api::BucketInfoReply> reply,
        DistributorComponent& link,
        api::Timestamp revertTimestamp)
    : MessageTracker(link.getClusterName()),
      _metric(metric),
      _reply(std::move(reply)),
      _manager(link),
      _revertTimestamp(revertTimestamp),
      _requestTimer(link.getClock()),
      _priority(_reply->getPriority()),
      _success(true)
{
}

PersistenceMessageTrackerImpl::~PersistenceMessageTrackerImpl() = default;

void
PersistenceMessageTrackerImpl::updateDB()
{
    for (const auto & entry : _bucketInfo) {
        _manager.updateBucketDatabase(entry.first, entry.second);
    }

    for (const auto & entry :  _remapBucketInfo){
        _manager.updateBucketDatabase(entry.first, entry.second,DatabaseUpdate::CREATE_IF_NONEXISTING);
    }
}

void
PersistenceMessageTrackerImpl::updateMetrics()
{
    const api::ReturnCode& result(_reply->getResult());
    _metric.updateFromResult(result);
    _metric.latency.addValue(_requestTimer.getElapsedTimeAsDouble());
}

void
PersistenceMessageTrackerImpl::fail(MessageSender& sender, const api::ReturnCode& result) {
    if (_reply.get()) {
        _reply->setResult(result);
        updateMetrics();
        sender.sendReply(_reply);
        _reply.reset();
    }
}

uint16_t
PersistenceMessageTrackerImpl::receiveReply(
        MessageSender& sender,
        api::BucketInfoReply& reply)
{
    uint16_t node = handleReply(reply);

    if (node != (uint16_t)-1) {
        updateFromReply(sender, reply, node);
    }

    return node;
}

void
PersistenceMessageTrackerImpl::revert(
        MessageSender& sender,
        const std::vector<BucketNodePair>& revertNodes)
{
    if (_revertTimestamp != 0) {
        // Since we're reverting, all received bucket info is voided.
        _bucketInfo.clear();

        std::vector<api::Timestamp> reverts;
        reverts.push_back(_revertTimestamp);

        for (const auto & revertNode : revertNodes) {
            auto toRevert = std::make_shared<api::RevertCommand>(revertNode.first, reverts);
            toRevert->setPriority(_priority);
            queueCommand(std::move(toRevert), revertNode.second);
        }

        flushQueue(sender);
    }
}

void
PersistenceMessageTrackerImpl::queueMessageBatch(const std::vector<MessageTracker::ToSend>& messages) {
    _messageBatches.emplace_back();
    for (const auto & message : messages) {
        if (_reply) {
            message._msg->getTrace().setLevel(_reply->getTrace().getLevel());
        }

        _messageBatches.back().push_back(message._msg->getMsgId());
        queueCommand(message._msg, message._target);
    }
}

bool
PersistenceMessageTrackerImpl::canSendReplyEarly() const
{
    if (!_reply.get() || !_reply->getResult().success()) {
        LOG(spam, "Can't return early because we have already replied or failed");
        return false;
    }
    auto &bucketSpaceRepo(_manager.getBucketSpaceRepo());
    auto &bucketSpace(bucketSpaceRepo.get(_reply->getBucket().getBucketSpace()));
    const lib::Distribution& distribution = bucketSpace.getDistribution();

    if (distribution.getInitialRedundancy() == 0) {
        LOG(spam, "Not returning early because initial redundancy wasn't set");
        return false;
    }

    for (const MessageBatch & batch : _messageBatches) {
        uint32_t messagesDone = 0;

        for (uint32_t i = 0; i < batch.size(); i++) {
            if (_sentMessages.find(batch[i]) == _sentMessages.end()) {
                messagesDone++;
            } else if (distribution.ensurePrimaryPersisted() && i == 0) {
                // Primary must always be written.
                LOG(debug, "Not returning early because primary node wasn't done");
                return false;
            }
        }

        if (messagesDone < distribution.getInitialRedundancy()) {
            LOG(spam, "Not returning early because only %d messages out of %d are done",
                messagesDone, distribution.getInitialRedundancy());
            return false;
        }
    }

    return true;
}

void
PersistenceMessageTrackerImpl::checkCopiesDeleted()
{
    if ( ! _reply) {
        return;
    }

    // Don't check the buckets that have been remapped here, as we will
    // create them.
    const auto &bucketSpaceRepo(_manager.getBucketSpaceRepo());
    for (const auto & entry : _bucketInfo) {
        const auto &bucketSpace(bucketSpaceRepo.get(entry.first.getBucketSpace()));
        const auto &bucketDb(bucketSpace.getBucketDatabase());
        BucketDatabase::Entry dbentry = bucketDb.get(entry.first.getBucketId());

        if (!dbentry.valid()) {
            continue;
        }

        std::vector<uint16_t> missing;
        std::vector<uint16_t> total;

        for (const BucketCopy & bucketCopy : entry.second) {
            if (dbentry->getNode(bucketCopy.getNode()) == nullptr) {
                missing.push_back(bucketCopy.getNode());
            }

            total.push_back(bucketCopy.getNode());
        }

        if (!missing.empty()) {
            std::ostringstream msg;
            msg << entry.first.toString() << " was deleted from nodes ["
                << commaSeparated(missing)
                << "] after message was sent but before it was done. Sent to ["
                << commaSeparated(total)
                << "]";

            LOG(debug, "%s", msg.str().c_str());
            _reply->setResult(api::ReturnCode(api::ReturnCode::BUCKET_DELETED,
                                              msg.str()));
            break;
        }
    }
}

void
PersistenceMessageTrackerImpl::addBucketInfoFromReply(
        uint16_t node,
        const api::BucketInfoReply& reply)
{
    document::Bucket bucket(reply.getBucket());
    const api::BucketInfo& bucketInfo(reply.getBucketInfo());

    if (reply.hasBeenRemapped()) {
        LOG(debug, "Bucket %s: Received remapped bucket info %s from node %d",
            bucket.toString().c_str(),
            bucketInfo.toString().c_str(),
            node);
        _remapBucketInfo[bucket].push_back(
                BucketCopy(_manager.getUniqueTimestamp(),
                           node,
                           bucketInfo));
    } else {
        LOG(debug, "Bucket %s: Received bucket info %s from node %d",
            bucket.toString().c_str(),
            bucketInfo.toString().c_str(),
            node);
        _bucketInfo[bucket].push_back(
                BucketCopy(_manager.getUniqueTimestamp(),
                           node,
                           bucketInfo));
    }
}

void
PersistenceMessageTrackerImpl::logSuccessfulReply(uint16_t node,
                                              const api::BucketInfoReply& reply) const
{
    LOG(spam, "Bucket %s: Received successful reply %s",
        reply.getBucketId().toString().c_str(),
        reply.toString().c_str());

    if (!reply.getBucketInfo().valid()) {
        LOG(error,
            "Reply %s from node %d contained invalid bucket "
            "information %s. This is a bug! Please report "
            "this to the Vespa team",
            reply.toString().c_str(),
            node,
            reply.getBucketInfo().toString().c_str());
    }
}

bool
PersistenceMessageTrackerImpl::shouldRevert() const
{
    return _manager.getDistributorConfig().enableRevert
            &&  !_revertNodes.empty() && !_success && _reply;
}

void
PersistenceMessageTrackerImpl::sendReply(MessageSender& sender)
{
    updateMetrics();
    _trace.setStrict(false);
    _reply->getTrace().getRoot().addChild(_trace);

    sender.sendReply(_reply);
    _reply = std::shared_ptr<api::BucketInfoReply>();
}

void
PersistenceMessageTrackerImpl::updateFailureResult(const api::BucketInfoReply& reply)
{
    LOG(debug, "Bucket %s: Received failed reply %s with result %s",
        reply.getBucketId().toString().c_str(),
        reply.toString().c_str(),
        reply.getResult().toString().c_str());
    if (reply.getResult().getResult() >
        _reply->getResult().getResult())
    {
        _reply->setResult(reply.getResult());
    }

    _success = false;
}

void
PersistenceMessageTrackerImpl::handleCreateBucketReply(
        api::BucketInfoReply& reply,
        uint16_t node)
{
    LOG(spam, "Received CreateBucket reply for %s from node %u",
        reply.getBucketId().toString().c_str(), node);
    if (!reply.getResult().success()
        && reply.getResult().getResult() != api::ReturnCode::EXISTS)
    {
        LOG(spam, "Create bucket reply failed, so deleting it from bucket db");
        _manager.removeNodeFromDB(reply.getBucket(), node);
        LOG_BUCKET_OPERATION_NO_LOCK(
                reply.getBucketId(),
                vespalib::make_string(
                    "Deleted bucket on node %u due to failing create bucket %s",
                    node, reply.getResult().toString().c_str()));
    }
}

void
PersistenceMessageTrackerImpl::handlePersistenceReply(
        api::BucketInfoReply& reply,
        uint16_t node)
{
    if (reply.getBucketInfo().valid()) {
        addBucketInfoFromReply(node, reply);
    }
    if (reply.getResult().success()) {
        logSuccessfulReply(node, reply);
        _revertNodes.emplace_back(reply.getBucket(), node);
    } else if (!hasSentReply()) {
        updateFailureResult(reply);
    }
}

void
PersistenceMessageTrackerImpl::updateFromReply(
        MessageSender& sender,
        api::BucketInfoReply& reply,
        uint16_t node)
{
    _trace.addChild(reply.getTrace().getRoot());

    if (reply.getType() == api::MessageType::CREATEBUCKET_REPLY) {
        handleCreateBucketReply(reply, node);
    } else {
        handlePersistenceReply(reply, node);
    }

    if (finished()) {
        bool doRevert(shouldRevert());

        checkCopiesDeleted();
        updateDB();

        if (!hasSentReply()) {
            sendReply(sender);
        }
        if (doRevert) {
            revert(sender, _revertNodes);
        }
    } else if (canSendReplyEarly()) {
        LOG(debug, "Sending reply early because initial redundancy has been reached");
        checkCopiesDeleted();
        sendReply(sender);
    }
}

}
<commit_msg>Do not bring along empty trace.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "persistencemessagetracker.h"
#include <vespa/storage/common/vectorprinter.h>
#include <vespa/storage/common/bucketoperationlogger.h>
#include <vespa/storageapi/message/persistence.h>
#include "distributor_bucket_space_repo.h"
#include "distributor_bucket_space.h"

#include <vespa/log/log.h>

LOG_SETUP(".persistencemessagetracker");

namespace storage::distributor {

PersistenceMessageTrackerImpl::PersistenceMessageTrackerImpl(
        PersistenceOperationMetricSet& metric,
        std::shared_ptr<api::BucketInfoReply> reply,
        DistributorComponent& link,
        api::Timestamp revertTimestamp)
    : MessageTracker(link.getClusterName()),
      _metric(metric),
      _reply(std::move(reply)),
      _manager(link),
      _revertTimestamp(revertTimestamp),
      _requestTimer(link.getClock()),
      _priority(_reply->getPriority()),
      _success(true)
{
}

PersistenceMessageTrackerImpl::~PersistenceMessageTrackerImpl() = default;

void
PersistenceMessageTrackerImpl::updateDB()
{
    for (const auto & entry : _bucketInfo) {
        _manager.updateBucketDatabase(entry.first, entry.second);
    }

    for (const auto & entry :  _remapBucketInfo){
        _manager.updateBucketDatabase(entry.first, entry.second,DatabaseUpdate::CREATE_IF_NONEXISTING);
    }
}

void
PersistenceMessageTrackerImpl::updateMetrics()
{
    const api::ReturnCode& result(_reply->getResult());
    _metric.updateFromResult(result);
    _metric.latency.addValue(_requestTimer.getElapsedTimeAsDouble());
}

void
PersistenceMessageTrackerImpl::fail(MessageSender& sender, const api::ReturnCode& result) {
    if (_reply.get()) {
        _reply->setResult(result);
        updateMetrics();
        sender.sendReply(_reply);
        _reply.reset();
    }
}

uint16_t
PersistenceMessageTrackerImpl::receiveReply(
        MessageSender& sender,
        api::BucketInfoReply& reply)
{
    uint16_t node = handleReply(reply);

    if (node != (uint16_t)-1) {
        updateFromReply(sender, reply, node);
    }

    return node;
}

void
PersistenceMessageTrackerImpl::revert(
        MessageSender& sender,
        const std::vector<BucketNodePair>& revertNodes)
{
    if (_revertTimestamp != 0) {
        // Since we're reverting, all received bucket info is voided.
        _bucketInfo.clear();

        std::vector<api::Timestamp> reverts;
        reverts.push_back(_revertTimestamp);

        for (const auto & revertNode : revertNodes) {
            auto toRevert = std::make_shared<api::RevertCommand>(revertNode.first, reverts);
            toRevert->setPriority(_priority);
            queueCommand(std::move(toRevert), revertNode.second);
        }

        flushQueue(sender);
    }
}

void
PersistenceMessageTrackerImpl::queueMessageBatch(const std::vector<MessageTracker::ToSend>& messages) {
    _messageBatches.emplace_back();
    for (const auto & message : messages) {
        if (_reply) {
            message._msg->getTrace().setLevel(_reply->getTrace().getLevel());
        }

        _messageBatches.back().push_back(message._msg->getMsgId());
        queueCommand(message._msg, message._target);
    }
}

bool
PersistenceMessageTrackerImpl::canSendReplyEarly() const
{
    if (!_reply.get() || !_reply->getResult().success()) {
        LOG(spam, "Can't return early because we have already replied or failed");
        return false;
    }
    auto &bucketSpaceRepo(_manager.getBucketSpaceRepo());
    auto &bucketSpace(bucketSpaceRepo.get(_reply->getBucket().getBucketSpace()));
    const lib::Distribution& distribution = bucketSpace.getDistribution();

    if (distribution.getInitialRedundancy() == 0) {
        LOG(spam, "Not returning early because initial redundancy wasn't set");
        return false;
    }

    for (const MessageBatch & batch : _messageBatches) {
        uint32_t messagesDone = 0;

        for (uint32_t i = 0; i < batch.size(); i++) {
            if (_sentMessages.find(batch[i]) == _sentMessages.end()) {
                messagesDone++;
            } else if (distribution.ensurePrimaryPersisted() && i == 0) {
                // Primary must always be written.
                LOG(debug, "Not returning early because primary node wasn't done");
                return false;
            }
        }

        if (messagesDone < distribution.getInitialRedundancy()) {
            LOG(spam, "Not returning early because only %d messages out of %d are done",
                messagesDone, distribution.getInitialRedundancy());
            return false;
        }
    }

    return true;
}

void
PersistenceMessageTrackerImpl::checkCopiesDeleted()
{
    if ( ! _reply) {
        return;
    }

    // Don't check the buckets that have been remapped here, as we will
    // create them.
    const auto &bucketSpaceRepo(_manager.getBucketSpaceRepo());
    for (const auto & entry : _bucketInfo) {
        const auto &bucketSpace(bucketSpaceRepo.get(entry.first.getBucketSpace()));
        const auto &bucketDb(bucketSpace.getBucketDatabase());
        BucketDatabase::Entry dbentry = bucketDb.get(entry.first.getBucketId());

        if (!dbentry.valid()) {
            continue;
        }

        std::vector<uint16_t> missing;
        std::vector<uint16_t> total;

        for (const BucketCopy & bucketCopy : entry.second) {
            if (dbentry->getNode(bucketCopy.getNode()) == nullptr) {
                missing.push_back(bucketCopy.getNode());
            }

            total.push_back(bucketCopy.getNode());
        }

        if (!missing.empty()) {
            std::ostringstream msg;
            msg << entry.first.toString() << " was deleted from nodes ["
                << commaSeparated(missing)
                << "] after message was sent but before it was done. Sent to ["
                << commaSeparated(total)
                << "]";

            LOG(debug, "%s", msg.str().c_str());
            _reply->setResult(api::ReturnCode(api::ReturnCode::BUCKET_DELETED,
                                              msg.str()));
            break;
        }
    }
}

void
PersistenceMessageTrackerImpl::addBucketInfoFromReply(
        uint16_t node,
        const api::BucketInfoReply& reply)
{
    document::Bucket bucket(reply.getBucket());
    const api::BucketInfo& bucketInfo(reply.getBucketInfo());

    if (reply.hasBeenRemapped()) {
        LOG(debug, "Bucket %s: Received remapped bucket info %s from node %d",
            bucket.toString().c_str(),
            bucketInfo.toString().c_str(),
            node);
        _remapBucketInfo[bucket].push_back(
                BucketCopy(_manager.getUniqueTimestamp(),
                           node,
                           bucketInfo));
    } else {
        LOG(debug, "Bucket %s: Received bucket info %s from node %d",
            bucket.toString().c_str(),
            bucketInfo.toString().c_str(),
            node);
        _bucketInfo[bucket].push_back(
                BucketCopy(_manager.getUniqueTimestamp(),
                           node,
                           bucketInfo));
    }
}

void
PersistenceMessageTrackerImpl::logSuccessfulReply(uint16_t node,
                                              const api::BucketInfoReply& reply) const
{
    LOG(spam, "Bucket %s: Received successful reply %s",
        reply.getBucketId().toString().c_str(),
        reply.toString().c_str());

    if (!reply.getBucketInfo().valid()) {
        LOG(error,
            "Reply %s from node %d contained invalid bucket "
            "information %s. This is a bug! Please report "
            "this to the Vespa team",
            reply.toString().c_str(),
            node,
            reply.getBucketInfo().toString().c_str());
    }
}

bool
PersistenceMessageTrackerImpl::shouldRevert() const
{
    return _manager.getDistributorConfig().enableRevert
            &&  !_revertNodes.empty() && !_success && _reply;
}

void
PersistenceMessageTrackerImpl::sendReply(MessageSender& sender)
{
    updateMetrics();
    _trace.setStrict(false);
    if ( ! _trace.isEmpty()) {
        _reply->getTrace().getRoot().addChild(_trace);
    }

    sender.sendReply(_reply);
    _reply = std::shared_ptr<api::BucketInfoReply>();
}

void
PersistenceMessageTrackerImpl::updateFailureResult(const api::BucketInfoReply& reply)
{
    LOG(debug, "Bucket %s: Received failed reply %s with result %s",
        reply.getBucketId().toString().c_str(),
        reply.toString().c_str(),
        reply.getResult().toString().c_str());
    if (reply.getResult().getResult() >
        _reply->getResult().getResult())
    {
        _reply->setResult(reply.getResult());
    }

    _success = false;
}

void
PersistenceMessageTrackerImpl::handleCreateBucketReply(
        api::BucketInfoReply& reply,
        uint16_t node)
{
    LOG(spam, "Received CreateBucket reply for %s from node %u",
        reply.getBucketId().toString().c_str(), node);
    if (!reply.getResult().success()
        && reply.getResult().getResult() != api::ReturnCode::EXISTS)
    {
        LOG(spam, "Create bucket reply failed, so deleting it from bucket db");
        _manager.removeNodeFromDB(reply.getBucket(), node);
        LOG_BUCKET_OPERATION_NO_LOCK(
                reply.getBucketId(),
                vespalib::make_string(
                    "Deleted bucket on node %u due to failing create bucket %s",
                    node, reply.getResult().toString().c_str()));
    }
}

void
PersistenceMessageTrackerImpl::handlePersistenceReply(
        api::BucketInfoReply& reply,
        uint16_t node)
{
    if (reply.getBucketInfo().valid()) {
        addBucketInfoFromReply(node, reply);
    }
    if (reply.getResult().success()) {
        logSuccessfulReply(node, reply);
        _revertNodes.emplace_back(reply.getBucket(), node);
    } else if (!hasSentReply()) {
        updateFailureResult(reply);
    }
}

void
PersistenceMessageTrackerImpl::updateFromReply(
        MessageSender& sender,
        api::BucketInfoReply& reply,
        uint16_t node)
{
    if ( ! reply.getTrace().getRoot().isEmpty()) {
        _trace.addChild(reply.getTrace().getRoot());
    }

    if (reply.getType() == api::MessageType::CREATEBUCKET_REPLY) {
        handleCreateBucketReply(reply, node);
    } else {
        handlePersistenceReply(reply, node);
    }

    if (finished()) {
        bool doRevert(shouldRevert());

        checkCopiesDeleted();
        updateDB();

        if (!hasSentReply()) {
            sendReply(sender);
        }
        if (doRevert) {
            revert(sender, _revertNodes);
        }
    } else if (canSendReplyEarly()) {
        LOG(debug, "Sending reply early because initial redundancy has been reached");
        checkCopiesDeleted();
        sendReply(sender);
    }
}

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: olesh.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: hr $ $Date: 2007-09-27 12:06:05 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _SWOLESH_HXX
#define _SWOLESH_HXX

#include "frmsh.hxx"

class SwOleShell: public SwFrameShell
{
public:
    SFX_DECL_INTERFACE(SW_OLESHELL)

            SwOleShell(SwView &rView);

};

#endif

<commit_msg>INTEGRATION: CWS changefileheader (1.3.242); FILE MERGED 2008/03/31 16:58:37 rt 1.3.242.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: olesh.hxx,v $
 * $Revision: 1.4 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef _SWOLESH_HXX
#define _SWOLESH_HXX

#include "frmsh.hxx"

class SwOleShell: public SwFrameShell
{
public:
    SFX_DECL_INTERFACE(SW_OLESHELL)

            SwOleShell(SwView &rView);

};

#endif

<|endoftext|>


<commit_before>// @(#)root/gui:$Id$
// Author: Fons Rademakers   20/02/98

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TRootDialog                                                          //
//                                                                      //
// A TRootDialog is used to prompt for the arguments of an object's     //
// member function. A TRootDialog is created via the context menu's     //
// when selecting a member function taking arguments.                   //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TRootDialog.h"
#include "TRootContextMenu.h"
#include "TContextMenu.h"
#include "TClassMenuItem.h"
#include "TList.h"
#include "TGLabel.h"
#include "TGTextEntry.h"
#include "TGButton.h"
#include "TObjString.h"
#include "KeySymbols.h"

extern TGTextEntry *gBlinkingEntry;

ClassImp(TRootDialog)

//______________________________________________________________________________
TRootDialog::TRootDialog(TRootContextMenu *cmenu, const TGWindow *main,
    const char *title, Bool_t okB, Bool_t cancelB, Bool_t applyB,
    Bool_t helpB) : TGTransientFrame(gClient->GetRoot(), main, 200, 100)
{
   // Create a method argument prompt dialog.

   fMenu   = cmenu;

   fOk     = okB;
   fCancel = cancelB;
   fApply  = applyB;
   fHelp   = helpB;

   fWidgets = new TList;

   fL1 = new TGLayoutHints(kLHintsTop | kLHintsCenterX, 0, 0, 5, 0);
   fL2 = new TGLayoutHints(kLHintsTop | kLHintsLeft, 5, 5, 5, 5);

   SetWindowName(title);
   SetIconName(title);
   SetEditDisabled(kEditDisable);

   AddInput(kKeyPressMask | kEnterWindowMask | kLeaveWindowMask);
}

//______________________________________________________________________________
TRootDialog::~TRootDialog()
{
   // Delete the dialog.

   fWidgets->Delete();
   delete fWidgets;
   delete fL1;
   delete fL2;
}

//______________________________________________________________________________
void TRootDialog::Add(const char *argname, const char *value, const char *type)
{
   // Add a label and text input field.

   TGLabel      *l = new TGLabel(this, argname);
   TGTextBuffer *b = new TGTextBuffer(20); b->AddText(0, value);
   TGTextEntry  *t = new TGTextEntry(this, b);

   t->Connect("TabPressed()", "TRootDialog", this, "TabPressed()");

   t->Associate(fMenu);
   t->Resize(260, t->GetDefaultHeight());
   AddFrame(l, fL1);
   AddFrame(t, fL2);

   fWidgets->Add(l);
   fWidgets->Add(t);   // TGTextBuffer will be deleted by TGTextEntry
   fWidgets->Add(new TObjString(type));
}

//______________________________________________________________________________
const char *TRootDialog::GetParameters()
{
   // Get parameter string (called by contextmenu after OK or Apply has
   // been selected).

   static TString params;
   TString param;

   TObjString   *str;
   TObject      *obj;

   Int_t selfobjpos;
   if (fMenu->GetContextMenu()->GetSelectedMenuItem())
      selfobjpos = fMenu->GetContextMenu()->GetSelectedMenuItem()->GetSelfObjectPos();
   else
      selfobjpos = -1;

   params.Clear();
   TIter next(fWidgets);
   Int_t nparam = 0;

   while ((obj = next())) {        // first element is label, skip...
      if (obj->IsA() != TGLabel::Class()) break;
      obj = next();                // get either TGTextEntry or TGComboBox
      str = (TObjString *) next(); // get type string

      nparam++;

      const char *type = str ? str->GetString().Data() : 0;
      const char *data = 0;

      if (obj && obj->IsA() == TGTextEntry::Class())
         data = ((TGTextEntry *) obj)->GetBuffer()->GetString();

      // TODO: Combobox...

      // if necessary, replace the selected object by it's address
      if (selfobjpos == nparam-1) {
         if (params.Length()) params += ",";
         param = TString::Format("(TObject*)0x%lx",
               (Long_t)fMenu->GetContextMenu()->GetSelectedObject());
         params += param;
      }

      if (params.Length()) params += ",";
      if (type && data) {
         if (!strncmp(type, "char*", 5))
            param = TString::Format("\"%s\"", data);
         else
            param = data;
      } else
         param = "0";

      params += param;
   }

   // if selected object is the last argument, have to insert it here
   if (selfobjpos == nparam) {
      if (params.Length()) params += ",";
      param = TString::Format("(TObject*)0x%lx",
            (Long_t)fMenu->GetContextMenu()->GetSelectedObject());
      params += param;
   }

   return params.Data();
}

//______________________________________________________________________________
void TRootDialog::Popup()
{
   // Popup dialog.

   //--- create the OK, Apply and Cancel buttons

   UInt_t  nb = 0, width = 0, height = 0;

   TGHorizontalFrame *hf = new TGHorizontalFrame(this, 60, 20, kFixedWidth);
   TGLayoutHints     *l1 = new TGLayoutHints(kLHintsCenterY | kLHintsExpandX, 5, 5, 0, 0);

   // put hf as last in the list to be deleted
   fWidgets->Add(l1);

   TGTextButton *b;
   if (fOk) {
      b = new TGTextButton(hf, "&OK", 1);
      fWidgets->Add(b);
      b->Associate(fMenu);
      hf->AddFrame(b, l1);
      height = b->GetDefaultHeight();
      width  = TMath::Max(width, b->GetDefaultWidth()); ++nb;
   }
   if (fApply) {
      b = new TGTextButton(hf, "&Apply", 2);
      fWidgets->Add(b);
      b->Associate(fMenu);
      hf->AddFrame(b, l1);
      height = b->GetDefaultHeight();
      width  = TMath::Max(width, b->GetDefaultWidth()); ++nb;
   }
   if (fCancel) {
      b = new TGTextButton(hf, "&Cancel", 3);
      fWidgets->Add(b);
      b->Associate(fMenu);
      hf->AddFrame(b, l1);
      height = b->GetDefaultHeight();
      width  = TMath::Max(width, b->GetDefaultWidth()); ++nb;
   }
   if (fHelp) {
      b = new TGTextButton(hf, "Online &Help", 4);
      fWidgets->Add(b);
      b->Associate(fMenu);
      hf->AddFrame(b, l1);
      height = b->GetDefaultHeight();
      width  = TMath::Max(width, b->GetDefaultWidth()); ++nb;
   }

   // place buttons at the bottom
   l1 = new TGLayoutHints(kLHintsBottom | kLHintsCenterX, 0, 0, 5, 5);
   fWidgets->Add(l1);
   fWidgets->Add(hf);

   AddFrame(hf, l1);

   // keep the buttons centered and with the same width
   hf->Resize((width + 20) * nb, height);

   // map all widgets and calculate size of dialog
   MapSubwindows();

   width  = GetDefaultWidth();
   height = GetDefaultHeight();

   Resize(width, height);

   // position relative to the parent's window
   CenterOnParent();

   // make the message box non-resizable
   SetWMSize(width, height);
   SetWMSizeHints(width, height, width, height, 0, 0);

   SetMWMHints(kMWMDecorAll | kMWMDecorResizeH  | kMWMDecorMaximize |
                              kMWMDecorMinimize | kMWMDecorMenu,
               kMWMFuncAll  | kMWMFuncResize    | kMWMFuncMaximize |
                              kMWMFuncMinimize,
               kMWMInputModeless);

   MapWindow();
   fClient->WaitFor(this);
}

//______________________________________________________________________________
void TRootDialog::CloseWindow()
{
   // Called when closed via window manager action.

   // Send Cancel button message to context menu eventhandler
   SendMessage(fMenu, MK_MSG(kC_COMMAND, kCM_BUTTON), 3, 0);
}

//______________________________________________________________________________
void TRootDialog::TabPressed()
{
   // Handle Tab keyboard navigation in this dialog.

   Bool_t setNext = kFALSE;
   TGTextEntry *entry;
   TIter next(fWidgets);

   while ( TObject* obj = next() ) {
      if ( obj->IsA() == TGTextEntry::Class() ) {
         entry = (TGTextEntry*) obj;
         if ( entry == gBlinkingEntry ) {
            setNext = kTRUE;
         } else if ( setNext ) {
            entry->SetFocus();
            entry->End();
            return;
         }
      }
   }

   next.Reset();
   while ( TObject* obj = next() ) {
      if ( obj->IsA() == TGTextEntry::Class() ) {
         entry = (TGTextEntry*) obj;
         entry->SetFocus();
         entry->End();
         return;
      }
   }
}

//______________________________________________________________________________
Bool_t TRootDialog::HandleKey(Event_t* event)
{
   // The key press event handler in this dialog.

   char   tmp[10];
   UInt_t keysym;
   gVirtualX->LookupString(event, tmp, sizeof(tmp), keysym);
   if ((EKeySym)keysym  == kKey_Tab) {

      TGTextEntry *entry;
      TIter next(fWidgets);

      while ( TObject* obj = next() ) {
         if ( obj->IsA() == TGTextEntry::Class() ) {
            entry = (TGTextEntry*) obj;
            entry->TabPressed();
            return kTRUE;
         }
      }
   }

   return TGMainFrame::HandleKey(event);
}
<commit_msg>Fix bug #101060: Editor not preserving MathText<commit_after>// @(#)root/gui:$Id$
// Author: Fons Rademakers   20/02/98

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TRootDialog                                                          //
//                                                                      //
// A TRootDialog is used to prompt for the arguments of an object's     //
// member function. A TRootDialog is created via the context menu's     //
// when selecting a member function taking arguments.                   //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TRootDialog.h"
#include "TRootContextMenu.h"
#include "TContextMenu.h"
#include "TClassMenuItem.h"
#include "TList.h"
#include "TGLabel.h"
#include "TGTextEntry.h"
#include "TGButton.h"
#include "TObjString.h"
#include "KeySymbols.h"

extern TGTextEntry *gBlinkingEntry;

ClassImp(TRootDialog)

//______________________________________________________________________________
TRootDialog::TRootDialog(TRootContextMenu *cmenu, const TGWindow *main,
    const char *title, Bool_t okB, Bool_t cancelB, Bool_t applyB,
    Bool_t helpB) : TGTransientFrame(gClient->GetRoot(), main, 200, 100)
{
   // Create a method argument prompt dialog.

   fMenu   = cmenu;

   fOk     = okB;
   fCancel = cancelB;
   fApply  = applyB;
   fHelp   = helpB;

   fWidgets = new TList;

   fL1 = new TGLayoutHints(kLHintsTop | kLHintsCenterX, 0, 0, 5, 0);
   fL2 = new TGLayoutHints(kLHintsTop | kLHintsLeft, 5, 5, 5, 5);

   SetWindowName(title);
   SetIconName(title);
   SetEditDisabled(kEditDisable);

   AddInput(kKeyPressMask | kEnterWindowMask | kLeaveWindowMask);
}

//______________________________________________________________________________
TRootDialog::~TRootDialog()
{
   // Delete the dialog.

   fWidgets->Delete();
   delete fWidgets;
   delete fL1;
   delete fL2;
}

//______________________________________________________________________________
void TRootDialog::Add(const char *argname, const char *value, const char *type)
{
   // Add a label and text input field.

   TGLabel      *l = new TGLabel(this, argname);
   TString svalue(value);
   // keep double backslashes (e.g. in case of LateX formatting, like \\gamma)
   svalue.ReplaceAll("\\", "\\\\");
   TGTextBuffer *b = new TGTextBuffer(20); b->AddText(0, svalue.Data());
   TGTextEntry  *t = new TGTextEntry(this, b);

   t->Connect("TabPressed()", "TRootDialog", this, "TabPressed()");

   t->Associate(fMenu);
   t->Resize(260, t->GetDefaultHeight());
   AddFrame(l, fL1);
   AddFrame(t, fL2);

   fWidgets->Add(l);
   fWidgets->Add(t);   // TGTextBuffer will be deleted by TGTextEntry
   fWidgets->Add(new TObjString(type));
}

//______________________________________________________________________________
const char *TRootDialog::GetParameters()
{
   // Get parameter string (called by contextmenu after OK or Apply has
   // been selected).

   static TString params;
   TString param;

   TObjString   *str;
   TObject      *obj;

   Int_t selfobjpos;
   if (fMenu->GetContextMenu()->GetSelectedMenuItem())
      selfobjpos = fMenu->GetContextMenu()->GetSelectedMenuItem()->GetSelfObjectPos();
   else
      selfobjpos = -1;

   params.Clear();
   TIter next(fWidgets);
   Int_t nparam = 0;

   while ((obj = next())) {        // first element is label, skip...
      if (obj->IsA() != TGLabel::Class()) break;
      obj = next();                // get either TGTextEntry or TGComboBox
      str = (TObjString *) next(); // get type string

      nparam++;

      const char *type = str ? str->GetString().Data() : 0;
      const char *data = 0;

      if (obj && obj->IsA() == TGTextEntry::Class())
         data = ((TGTextEntry *) obj)->GetBuffer()->GetString();

      // TODO: Combobox...

      // if necessary, replace the selected object by it's address
      if (selfobjpos == nparam-1) {
         if (params.Length()) params += ",";
         param = TString::Format("(TObject*)0x%lx",
               (Long_t)fMenu->GetContextMenu()->GetSelectedObject());
         params += param;
      }

      if (params.Length()) params += ",";
      if (type && data) {
         if (!strncmp(type, "char*", 5))
            param = TString::Format("\"%s\"", data);
         else
            param = data;
      } else
         param = "0";

      params += param;
   }

   // if selected object is the last argument, have to insert it here
   if (selfobjpos == nparam) {
      if (params.Length()) params += ",";
      param = TString::Format("(TObject*)0x%lx",
            (Long_t)fMenu->GetContextMenu()->GetSelectedObject());
      params += param;
   }

   return params.Data();
}

//______________________________________________________________________________
void TRootDialog::Popup()
{
   // Popup dialog.

   //--- create the OK, Apply and Cancel buttons

   UInt_t  nb = 0, width = 0, height = 0;

   TGHorizontalFrame *hf = new TGHorizontalFrame(this, 60, 20, kFixedWidth);
   TGLayoutHints     *l1 = new TGLayoutHints(kLHintsCenterY | kLHintsExpandX, 5, 5, 0, 0);

   // put hf as last in the list to be deleted
   fWidgets->Add(l1);

   TGTextButton *b;
   if (fOk) {
      b = new TGTextButton(hf, "&OK", 1);
      fWidgets->Add(b);
      b->Associate(fMenu);
      hf->AddFrame(b, l1);
      height = b->GetDefaultHeight();
      width  = TMath::Max(width, b->GetDefaultWidth()); ++nb;
   }
   if (fApply) {
      b = new TGTextButton(hf, "&Apply", 2);
      fWidgets->Add(b);
      b->Associate(fMenu);
      hf->AddFrame(b, l1);
      height = b->GetDefaultHeight();
      width  = TMath::Max(width, b->GetDefaultWidth()); ++nb;
   }
   if (fCancel) {
      b = new TGTextButton(hf, "&Cancel", 3);
      fWidgets->Add(b);
      b->Associate(fMenu);
      hf->AddFrame(b, l1);
      height = b->GetDefaultHeight();
      width  = TMath::Max(width, b->GetDefaultWidth()); ++nb;
   }
   if (fHelp) {
      b = new TGTextButton(hf, "Online &Help", 4);
      fWidgets->Add(b);
      b->Associate(fMenu);
      hf->AddFrame(b, l1);
      height = b->GetDefaultHeight();
      width  = TMath::Max(width, b->GetDefaultWidth()); ++nb;
   }

   // place buttons at the bottom
   l1 = new TGLayoutHints(kLHintsBottom | kLHintsCenterX, 0, 0, 5, 5);
   fWidgets->Add(l1);
   fWidgets->Add(hf);

   AddFrame(hf, l1);

   // keep the buttons centered and with the same width
   hf->Resize((width + 20) * nb, height);

   // map all widgets and calculate size of dialog
   MapSubwindows();

   width  = GetDefaultWidth();
   height = GetDefaultHeight();

   Resize(width, height);

   // position relative to the parent's window
   CenterOnParent();

   // make the message box non-resizable
   SetWMSize(width, height);
   SetWMSizeHints(width, height, width, height, 0, 0);

   SetMWMHints(kMWMDecorAll | kMWMDecorResizeH  | kMWMDecorMaximize |
                              kMWMDecorMinimize | kMWMDecorMenu,
               kMWMFuncAll  | kMWMFuncResize    | kMWMFuncMaximize |
                              kMWMFuncMinimize,
               kMWMInputModeless);

   MapWindow();
   fClient->WaitFor(this);
}

//______________________________________________________________________________
void TRootDialog::CloseWindow()
{
   // Called when closed via window manager action.

   // Send Cancel button message to context menu eventhandler
   SendMessage(fMenu, MK_MSG(kC_COMMAND, kCM_BUTTON), 3, 0);
}

//______________________________________________________________________________
void TRootDialog::TabPressed()
{
   // Handle Tab keyboard navigation in this dialog.

   Bool_t setNext = kFALSE;
   TGTextEntry *entry;
   TIter next(fWidgets);

   while ( TObject* obj = next() ) {
      if ( obj->IsA() == TGTextEntry::Class() ) {
         entry = (TGTextEntry*) obj;
         if ( entry == gBlinkingEntry ) {
            setNext = kTRUE;
         } else if ( setNext ) {
            entry->SetFocus();
            entry->End();
            return;
         }
      }
   }

   next.Reset();
   while ( TObject* obj = next() ) {
      if ( obj->IsA() == TGTextEntry::Class() ) {
         entry = (TGTextEntry*) obj;
         entry->SetFocus();
         entry->End();
         return;
      }
   }
}

//______________________________________________________________________________
Bool_t TRootDialog::HandleKey(Event_t* event)
{
   // The key press event handler in this dialog.

   char   tmp[10];
   UInt_t keysym;
   gVirtualX->LookupString(event, tmp, sizeof(tmp), keysym);
   if ((EKeySym)keysym  == kKey_Tab) {

      TGTextEntry *entry;
      TIter next(fWidgets);

      while ( TObject* obj = next() ) {
         if ( obj->IsA() == TGTextEntry::Class() ) {
            entry = (TGTextEntry*) obj;
            entry->TabPressed();
            return kTRUE;
         }
      }
   }

   return TGMainFrame::HandleKey(event);
}
<|endoftext|>


<commit_before>#include "archivator.h"
#include <map>
#include <algorithm>
#include <fstream>
#include <iostream>

void archivator::compress(const std::string &dir, const std::string &destination)
{
	archive *a;
	archive_entry *entry;
	int r;

	std::map<fs::path, fs::path> files;
	fs::path dir_path;
	try {
		dir_path = fs::canonical(fs::path(dir));
		if (fs::is_directory(dir_path)) {
			for (auto i = fs::recursive_directory_iterator(dir_path); i != fs::recursive_directory_iterator(); ++i) {
				fs::path file = *i;
				if (fs::is_regular_file(file)) {
					// find out where the two paths diverge - boost::filesystem::relative() is too new now to use it
					fs::path::const_iterator itr_dir = dir_path.begin();
					fs::path::const_iterator itr_file = file.begin();
					while (*itr_dir == *itr_file && itr_dir != dir_path.end()) {
						++itr_dir;
						++itr_file;
					}
					// result is what is left in second path (it's relative path to the first one)
					fs::path result;
					while (itr_file != file.end()) {
						result /= *itr_file;
						itr_file++;
					}
					files.insert({file, result});
				}
			}
		} else {
			throw archive_exception("Given path '" + dir + "' is not directory.");
		}
	} catch (fs::filesystem_error &e) {
		throw archive_exception(e.what());
	}

	a = archive_write_new();
	if (a == NULL) {
		throw archive_exception("Cannot create destination archive.");
	}
	if (archive_write_set_format_zip(a) < ARCHIVE_OK) {
		throw archive_exception("Cannot set ZIP format on destination archive.");
	}
	if (archive_write_open_filename(a, destination.c_str()) != ARCHIVE_OK) {
		throw archive_exception("Cannot open destination archive.");
	}

	for (auto &file : files) {
		entry = archive_entry_new();

		archive_entry_set_pathname(entry, (fs::path(destination).stem() / file.second).string().c_str());
		archive_entry_set_size(entry, fs::file_size(file.first));
		archive_entry_set_mtime(entry, fs::last_write_time(file.first), 0); //0 nanoseconds
		archive_entry_set_filetype(entry, AE_IFREG);
		archive_entry_set_perm(entry, 0644);

		r = archive_write_header(a, entry);
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(a));
		}

		std::ifstream ifs((file.first).string(), std::ios::in | std::ios::binary);
		if (ifs.is_open()) {
			// read data by small blocks to avoid memory overfill on possibly large files
			char buff[4096];

			while (true) {
				ifs.read(buff, sizeof(buff));

				auto read_len = ifs.gcount();
				if (ifs.eof() && read_len == 0) {
					break;
				}
				else if (read_len <= 0) {
					throw archive_exception("Error reading input file.");
				}

				r = archive_write_data(a, buff, static_cast<size_t>(ifs.gcount()));
				if (r < ARCHIVE_OK) {
					throw archive_exception(archive_error_string(a));
				}
			}
		} else {
			throw archive_exception("Cannot open file " + (file.first).string() + " for reading.");
		}
		archive_entry_free(entry);
	}
	archive_write_close(a);
	archive_write_free(a);
}

void archivator::decompress(const std::string &filename, const std::string &destination)
{
	archive *a;
	archive *ext;
	archive_entry *entry;
	int flags;
	int r;

	if (!fs::is_directory(destination)) {
		throw archive_exception("Destination '" + destination + "' is not a directory. Cannot decompress archive.");
	}
	if (!fs::is_regular_file(filename)) {
		throw archive_exception("Source archive '" + filename + "' not exists or is not a regular file.");
	}

	// Select which attributes we want to restore.
	flags = ARCHIVE_EXTRACT_TIME;
	flags |= ARCHIVE_EXTRACT_FFLAGS;
	// Don't allow ".." in any path within archive
	flags |= ARCHIVE_EXTRACT_SECURE_NODOTDOT;

	a = archive_read_new();
	archive_read_support_format_all(a);
	archive_read_support_compression_all(a);
	ext = archive_write_disk_new();
	archive_write_disk_set_options(ext, flags);
	archive_write_disk_set_standard_lookup(ext);

	r = archive_read_open_filename(a, filename.c_str(), 10240);
	if (r < ARCHIVE_OK) {
		throw archive_exception("Cannot open source archive.");
	}

	while (true) {
		r = archive_read_next_header(a, &entry);
		if (r == ARCHIVE_EOF) {
			break;
		}
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(a));
		}

		const char* current_file = archive_entry_pathname(entry);
		const std::string full_path = (fs::path(destination) / current_file).string();
		archive_entry_set_pathname(entry, full_path.c_str());
		auto filetype = archive_entry_filetype(entry);
		if (filetype == AE_IFREG) {
			archive_entry_set_perm(entry, 0644);
		} else if (filetype == AE_IFDIR) {
			archive_entry_set_perm(entry, 0755);
		} else {
			throw archive_exception("Unsupported archive entry filetype.");
		}

		r = archive_write_header(ext, entry);
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(ext));
		}

		if (archive_entry_size(entry) > 0) {
			copy_data(a, ext);
		}

		r = archive_write_finish_entry(ext);
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(ext));
		}
	}

	archive_read_close(a);
	archive_read_free(a);
	archive_write_close(ext);
	archive_write_free(ext);
}

void archivator::copy_data(archive *ar, archive *aw)
{
	int r;
	const void *buff;
	size_t size;
	int64_t offset;

	while (true) {
		r = archive_read_data_block(ar, &buff, &size, &offset);
		if (r == ARCHIVE_EOF) {
			break;
		}
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(ar));
		}
		r = archive_write_data_block(aw, buff, size, offset);
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(aw));
		}
	}
}
<commit_msg>Add some more checks to archivator.<commit_after>#include "archivator.h"
#include <map>
#include <algorithm>
#include <fstream>
#include <iostream>

void archivator::compress(const std::string &dir, const std::string &destination)
{
	archive *a;
	archive_entry *entry;
	int r;

	std::map<fs::path, fs::path> files;
	fs::path dir_path;
	try {
		dir_path = fs::canonical(fs::path(dir));
		if (fs::is_directory(dir_path)) {
			for (auto i = fs::recursive_directory_iterator(dir_path); i != fs::recursive_directory_iterator(); ++i) {
				fs::path file = *i;
				if (fs::is_regular_file(file)) {
					// find out where the two paths diverge - boost::filesystem::relative() is too new now to use it
					fs::path::const_iterator itr_dir = dir_path.begin();
					fs::path::const_iterator itr_file = file.begin();
					while (*itr_dir == *itr_file && itr_dir != dir_path.end()) {
						++itr_dir;
						++itr_file;
					}
					// result is what is left in second path (it's relative path to the first one)
					fs::path result;
					while (itr_file != file.end()) {
						result /= *itr_file;
						itr_file++;
					}
					files.insert({file, result});
				}
			}
		} else {
			throw archive_exception("Given path '" + dir + "' is not directory.");
		}
	} catch (fs::filesystem_error &e) {
		throw archive_exception(e.what());
	}

	a = archive_write_new();
	if (a == NULL) {
		throw archive_exception("Cannot create destination archive.");
	}
	if (archive_write_set_format_zip(a) != ARCHIVE_OK) {
		throw archive_exception("Cannot set ZIP format on destination archive.");
	}
	if (archive_write_open_filename(a, destination.c_str()) != ARCHIVE_OK) {
		throw archive_exception("Cannot open destination archive.");
	}

	for (auto &file : files) {
		entry = archive_entry_new();

		archive_entry_set_pathname(entry, (fs::path(destination).stem() / file.second).string().c_str());
		archive_entry_set_size(entry, fs::file_size(file.first));
		archive_entry_set_mtime(entry, fs::last_write_time(file.first), 0); //0 nanoseconds
		archive_entry_set_filetype(entry, AE_IFREG);
		archive_entry_set_perm(entry, 0644);

		r = archive_write_header(a, entry);
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(a));
		}

		std::ifstream ifs((file.first).string(), std::ios::in | std::ios::binary);
		if (ifs.is_open()) {
			// read data by small blocks to avoid memory overfill on possibly large files
			char buff[4096];

			while (true) {
				ifs.read(buff, sizeof(buff));

				auto read_len = ifs.gcount();
				if (ifs.eof() && read_len == 0) {
					break;
				}
				else if (read_len <= 0) {
					throw archive_exception("Error reading input file.");
				}

				r = archive_write_data(a, buff, static_cast<size_t>(ifs.gcount()));
				if (r < ARCHIVE_OK) {
					throw archive_exception(archive_error_string(a));
				}
			}
		} else {
			throw archive_exception("Cannot open file " + (file.first).string() + " for reading.");
		}
		archive_entry_free(entry);
	}
	archive_write_close(a);
	archive_write_free(a);
}

void archivator::decompress(const std::string &filename, const std::string &destination)
{
	archive *a;
	archive *ext;
	archive_entry *entry;
	int flags;
	int r;

	if (!fs::is_directory(destination)) {
		throw archive_exception("Destination '" + destination + "' is not a directory. Cannot decompress archive.");
	}
	if (!fs::is_regular_file(filename)) {
		throw archive_exception("Source archive '" + filename + "' not exists or is not a regular file.");
	}

	// Select which attributes we want to restore.
	flags = ARCHIVE_EXTRACT_TIME;
	flags |= ARCHIVE_EXTRACT_FFLAGS;
	// Don't allow ".." in any path within archive
	flags |= ARCHIVE_EXTRACT_SECURE_NODOTDOT;

	a = archive_read_new();
	if (a == NULL) {
		throw archive_exception("Cannot create source archive.");
	}
	if (archive_read_support_format_all(a) != ARCHIVE_OK) {
		throw archive_exception("Cannot set formats for source archive.");
	}
	if (archive_read_support_compression_all(a) != ARCHIVE_OK) {
		throw archive_exception("Cannot set compression methods for source archive.");
	}
	ext = archive_write_disk_new();
	if (ext == NULL) {
		throw archive_exception("Cannot allocate archive entry.");
	}
	if (archive_write_disk_set_options(ext, flags) != ARCHIVE_OK) {
		throw archive_exception("Cannot set options for writing to disk.");
	}
	if (archive_write_disk_set_standard_lookup(ext) != ARCHIVE_OK) {
		throw archive_exception("Cannot set lookup for writing to disk.");
	}

	r = archive_read_open_filename(a, filename.c_str(), 10240);
	if (r < ARCHIVE_OK) {
		throw archive_exception("Cannot open source archive.");
	}

	while (true) {
		r = archive_read_next_header(a, &entry);
		if (r == ARCHIVE_EOF) {
			break;
		}
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(a));
		}

		const char* current_file = archive_entry_pathname(entry);
		const std::string full_path = (fs::path(destination) / current_file).string();
		archive_entry_set_pathname(entry, full_path.c_str());
		auto filetype = archive_entry_filetype(entry);
		if (filetype == AE_IFREG) {
			archive_entry_set_perm(entry, 0644);
		} else if (filetype == AE_IFDIR) {
			archive_entry_set_perm(entry, 0755);
		} else {
			throw archive_exception("Unsupported archive entry filetype.");
		}

		r = archive_write_header(ext, entry);
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(ext));
		}

		if (archive_entry_size(entry) > 0) {
			copy_data(a, ext);
		}

		r = archive_write_finish_entry(ext);
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(ext));
		}
	}

	archive_read_close(a);
	archive_read_free(a);
	archive_write_close(ext);
	archive_write_free(ext);
}

void archivator::copy_data(archive *ar, archive *aw)
{
	int r;
	const void *buff;
	size_t size;
	int64_t offset;

	while (true) {
		r = archive_read_data_block(ar, &buff, &size, &offset);
		if (r == ARCHIVE_EOF) {
			break;
		}
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(ar));
		}
		r = archive_write_data_block(aw, buff, size, offset);
		if (r < ARCHIVE_OK) {
			throw archive_exception(archive_error_string(aw));
		}
	}
}
<|endoftext|>