<commit_before>/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2011-2013 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "internal.h"
#include "packetutils.h"
#include "bucketconfig/clconfig.h"
#include "vbucket/aliases.h"
#include "sllist-inl.h"

#define LOGARGS(instance, lvl) (instance)->settings, "newconfig", LCB_LOG_##lvl, __FILE__, __LINE__
#define LOG(instance, lvlbase, msg) lcb_log(instance->settings, "newconfig", LCB_LOG_##lvlbase, __FILE__, __LINE__, msg)

#define SERVER_FMT "%s:%s (%p)"
#define SERVER_ARGS(s) (s)->get_host().host, (s)->get_host().port, (void *)s

typedef struct lcb_GUESSVB_st {
    time_t last_update; /**< Last time this vBucket was heuristically set */
    char newix; /**< New index, heuristically determined */
    char oldix; /**< Original index, according to map */
    char used; /**< Flag indicating whether or not this entry has been used */
} lcb_GUESSVB;

/* Ignore configuration updates for heuristically guessed vBuckets for a
 * maximum amount of [n] seconds */
#define MAX_KEEP_GUESS 20

static int
should_keep_guess(lcb_GUESSVB *guess, lcbvb_VBUCKET *vb)
{
    if (guess->newix == guess->oldix) {
        /* Heuristic position is the same as starting position */
        return 0;
    }
    if (vb->servers[0] != guess->oldix) {
        /* Previous master changed */
        return 0;
    }

    if (time(NULL) - guess->last_update > MAX_KEEP_GUESS) {
        /* Last usage too old */
        return 0;
    }

    return 1;
}

void
lcb_vbguess_newconfig(lcb_t instance, lcbvb_CONFIG *cfg, lcb_GUESSVB *guesses)
{
    unsigned ii;

    if (!guesses) {
        return;
    }

    for (ii = 0; ii < cfg->nvb; ii++) {
        lcb_GUESSVB *guess = guesses + ii;
        lcbvb_VBUCKET *vb = cfg->vbuckets + ii;

        if (!guess->used) {
            continue;
        }

        /* IF: Heuristically learned a new index, _and_ the old index (which is
         * known to be bad) is the same index stated by the new config */
        if (should_keep_guess(guess, vb)) {
            lcb_log(LOGARGS(instance, TRACE), "Keeping heuristically guessed index. VBID=%d. Current=%d. Old=%d.", ii, guess->newix, guess->oldix);
            vb->servers[0] = guess->newix;
        } else {
            /* We don't reassign to the guess structure here. The idea is that
             * we will simply use the new config. If this gives us problems, the
             * config will re-learn again. */
            lcb_log(LOGARGS(instance, TRACE), "Ignoring heuristically guessed index. VBID=%d. Current=%d. Old=%d. New=%d", ii, guess->newix, guess->oldix, vb->servers[0]);
            guess->used = 0;
        }
    }
}

int
lcb_vbguess_remap(lcb_t instance, int vbid, int bad)
{

    if (LCBT_SETTING(instance, vb_noguess)) {
        int newix = lcbvb_nmv_remap_ex(LCBT_VBCONFIG(instance), vbid, bad, 0);
        if (newix > -1 && newix != bad) {
            lcb_log(LOGARGS(instance, TRACE), "Got new index from ffmap. VBID=%d. Old=%d. New=%d", vbid, bad, newix);
        }
        return newix;

    } else {
        lcb_GUESSVB *guesses = instance->vbguess;
        lcb_GUESSVB *guess = guesses + vbid;
        int newix = lcbvb_nmv_remap_ex(LCBT_VBCONFIG(instance), vbid, bad, 1);
        if (!guesses) {
            guesses = instance->vbguess = reinterpret_cast<lcb_GUESSVB*>(calloc(
                LCBT_VBCONFIG(instance)->nvb, sizeof *guesses));
        }
        if (newix > -1 && newix != bad) {
            guess->newix = newix;
            guess->oldix = bad;
            guess->used = 1;
            guess->last_update = time(NULL);
            lcb_log(LOGARGS(instance, TRACE), "Guessed new heuristic index VBID=%d. Old=%d. New=%d", vbid, bad, newix);
        }
        return newix;
    }
}

/**
 * Finds the index of an older server using the current config.
 *
 * This function is used to help reuse the server structures for memcached
 * packets.
 *
 * @param oldconfig The old configuration. This is the configuration the
 * old server is bound to
 * @param newconfig The new configuration. This will be inspected for new
 * nodes which may have been added, and ones which may have been removed.
 * @param server The server to match
 * @return The new index, or -1 if the current server is not present in the new
 * config.
 */
static int
find_new_data_index(lcbvb_CONFIG *oldconfig, lcbvb_CONFIG* newconfig,
    lcb::Server *server)
{
    const char *old_datahost = lcbvb_get_hostport(oldconfig,
        server->get_index(), LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN);

    if (!old_datahost) {
        /* Old server had no data service */
        return -1;
    }

    for (size_t ii = 0; ii < LCBVB_NSERVERS(newconfig); ii++) {
        const char *new_datahost = lcbvb_get_hostport(newconfig, ii,
            LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN);
        if (new_datahost && strcmp(new_datahost, old_datahost) == 0) {
            return ii;
        }
    }
    return -1;
}

static void
log_vbdiff(lcb_t instance, lcbvb_CONFIGDIFF *diff)
{
    lcb_log(LOGARGS(instance, INFO), "Config Diff: [ vBuckets Modified=%d ], [Sequence Changed=%d]", diff->n_vb_changes, diff->sequence_changed);
    if (diff->servers_added) {
        for (char **curserver = diff->servers_added; *curserver; curserver++) {
            lcb_log(LOGARGS(instance, INFO), "Detected server %s added", *curserver);
        }
    }
    if (diff->servers_removed) {
        for (char **curserver = diff->servers_removed; *curserver; curserver++) {
            lcb_log(LOGARGS(instance, INFO), "Detected server %s removed", *curserver);
        }
    }
}

/**
 * This callback is invoked for packet relocation twice. It tries to relocate
 * commands to their destination server. Some commands may not be relocated
 * either because they have no explicit "Relocation Information" (i.e. no
 * specific vbucket) or because the command is tied to a specific server (i.e.
 * CMD_STAT).
 *
 * Note that `KEEP_PACKET` here doesn't mean to "Save" the packet, but rather
 * to keep the packet in the current queue (so that if the server ends up
 * being removed, the command will fail); rather than being relocated to
 * another server.
 */
static int
iterwipe_cb(mc_CMDQUEUE *cq, mc_PIPELINE *oldpl, mc_PACKET *oldpkt, void *)
{
    protocol_binary_request_header hdr;
    lcb::Server *srv = static_cast<lcb::Server *>(oldpl);
    int newix;

    mcreq_read_hdr(oldpkt, &hdr);

    if (!lcb_should_retry(srv->get_settings(), oldpkt, LCB_MAX_ERROR)) {
        return MCREQ_KEEP_PACKET;
    }

    if (LCBVB_DISTTYPE(cq->config) == LCBVB_DIST_VBUCKET) {
        newix = lcbvb_vbmaster(cq->config, ntohs(hdr.request.vbucket));

    } else {
        const void *key = NULL;
        lcb_SIZE nkey = 0;
        int tmpid;

        /* XXX: We ignore hashkey. This is going away soon, and is probably
         * better than simply failing the items. */
        mcreq_get_key(oldpkt, &key, &nkey);
        lcbvb_map_key(cq->config, key, nkey, &tmpid, &newix);
    }

    if (newix < 0 || newix > (int)cq->npipelines-1) {
        return MCREQ_KEEP_PACKET;
    }

    mc_PIPELINE *newpl = cq->pipelines[newix];
    if (newpl == oldpl || newpl == NULL) {
        return MCREQ_KEEP_PACKET;
    }

    lcb_log(LOGARGS((lcb_t)cq->cqdata, DEBUG), "Remapped packet %p (SEQ=%u) from "SERVER_FMT " to " SERVER_FMT,
        (void*)oldpkt, oldpkt->opaque, SERVER_ARGS((lcb::Server*)oldpl), SERVER_ARGS((lcb::Server*)newpl));

    /** Otherwise, copy over the packet and find the new vBucket to map to */
    mc_PACKET *newpkt = mcreq_renew_packet(oldpkt);
    newpkt->flags &= ~MCREQ_STATE_FLAGS;
    mcreq_reenqueue_packet(newpl, newpkt);
    mcreq_packet_handled(oldpl, oldpkt);
    return MCREQ_REMOVE_PACKET;
}

static lcb_configuration_t
replace_config(lcb_t instance, lcbvb_CONFIG *oldconfig, lcbvb_CONFIG *newconfig)
{
    mc_CMDQUEUE *cq = &instance->cmdq;
    mc_PIPELINE **ppold, **ppnew;
    unsigned ii, nold, nnew;

    assert(LCBT_VBCONFIG(instance) == newconfig);

    nnew = LCBVB_NSERVERS(newconfig);
    ppnew = reinterpret_cast<mc_PIPELINE**>(calloc(nnew, sizeof(*ppnew)));
    ppold = mcreq_queue_take_pipelines(cq, &nold);

    /**
     * Determine which existing servers are still part of the new cluster config
     * and place it inside the new list.
     */
    for (ii = 0; ii < nold; ii++) {
        lcb::Server *cur = static_cast<lcb::Server *>(ppold[ii]);
        int newix = find_new_data_index(oldconfig, newconfig, cur);
        if (newix > -1) {
            cur->set_new_index(newix);
            ppnew[newix] = cur;
            ppold[ii] = NULL;
            lcb_log(LOGARGS(instance, INFO), "Reusing server "SERVER_FMT". OldIndex=%d. NewIndex=%d", SERVER_ARGS(cur), ii, newix);
        }
    }

    /**
     * Once we've moved the kept servers to the new list, allocate new lcb::Server
     * structures for slots that don't have an existing lcb::Server. We must do
     * this before add_pipelines() is called, so that there are no holes inside
     * ppnew
     */
    for (ii = 0; ii < nnew; ii++) {
        if (!ppnew[ii]) {
            ppnew[ii] = new lcb::Server(instance, ii);
        }
    }

    /**
     * Once we have all the server structures in place for the new config,
     * transfer the new config along with the new list over to the CQ structure.
     */
    mcreq_queue_add_pipelines(cq, ppnew, nnew, newconfig);
    for (ii = 0; ii < nnew; ii++) {
        mcreq_iterwipe(cq, ppnew[ii], iterwipe_cb, NULL);
    }

    /**
     * Go through all the servers that are to be removed and relocate commands
     * from their queues into the new queues
     */
    for (ii = 0; ii < nold; ii++) {
        if (!ppold[ii]) {
            continue;
        }

        mcreq_iterwipe(cq, ppold[ii], iterwipe_cb, NULL);
        static_cast<lcb::Server*>(ppold[ii])->purge(LCB_MAP_CHANGED);
        static_cast<lcb::Server*>(ppold[ii])->close();
    }

    for (ii = 0; ii < nnew; ii++) {
        if (static_cast<lcb::Server*>(ppnew[ii])->has_pending()) {
            ppnew[ii]->flush_start(ppnew[ii]);
        }
    }

    free(ppnew);
    free(ppold);
    return LCB_CONFIGURATION_CHANGED;
}

void lcb_update_vbconfig(lcb_t instance, clconfig_info *config)
{
    lcb_configuration_t change_status;
    clconfig_info *old_config = instance->cur_configinfo;
    mc_CMDQUEUE *q = &instance->cmdq;

    instance->cur_configinfo = config;
    lcb_clconfig_incref(config);
    q->config = instance->cur_configinfo->vbc;
    q->cqdata = instance;

    if (old_config) {
        lcbvb_CONFIGDIFF *diff = lcbvb_compare(old_config->vbc, config->vbc);

        if (diff) {
            log_vbdiff(instance, diff);
            lcbvb_free_diff(diff);
        }

        /* Apply the vb guesses */
        lcb_vbguess_newconfig(instance, config->vbc, instance->vbguess);

        change_status = replace_config(instance, old_config->vbc, config->vbc);
        if (change_status == -1) {
            LOG(instance, ERR, "Couldn't replace config");
            return;
        }
        lcb_clconfig_decref(old_config);
    } else {
        size_t nservers = VB_NSERVERS(config->vbc);
        std::vector<mc_PIPELINE*> servers;

        for (size_t ii = 0; ii < nservers; ii++) {
            servers.push_back(new lcb::Server(instance, ii));
        }

        mcreq_queue_add_pipelines(q, &servers[0], nservers, config->vbc);
        change_status = LCB_CONFIGURATION_NEW;
    }

    /* Update the list of nodes here for server list */
    instance->ht_nodes->clear();
    for (size_t ii = 0; ii < LCBVB_NSERVERS(config->vbc); ++ii) {
        const char *hp = lcbvb_get_hostport(config->vbc, ii,
            LCBVB_SVCTYPE_MGMT, LCBVB_SVCMODE_PLAIN);
        if (hp) {
            instance->ht_nodes->add(hp, LCB_CONFIG_HTTP_PORT);
        }
    }

    instance->callbacks.configuration(instance, change_status);
    lcb_maybe_breakout(instance);
}
<commit_msg>newconfig: fix compiler warning<commit_after>/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2011-2013 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "internal.h"
#include "packetutils.h"
#include "bucketconfig/clconfig.h"
#include "vbucket/aliases.h"
#include "sllist-inl.h"

#define LOGARGS(instance, lvl) (instance)->settings, "newconfig", LCB_LOG_##lvl, __FILE__, __LINE__
#define LOG(instance, lvlbase, msg) lcb_log(instance->settings, "newconfig", LCB_LOG_##lvlbase, __FILE__, __LINE__, msg)

#define SERVER_FMT "%s:%s (%p)"
#define SERVER_ARGS(s) (s)->get_host().host, (s)->get_host().port, (void *)s

typedef struct lcb_GUESSVB_st {
    time_t last_update; /**< Last time this vBucket was heuristically set */
    char newix; /**< New index, heuristically determined */
    char oldix; /**< Original index, according to map */
    char used; /**< Flag indicating whether or not this entry has been used */
} lcb_GUESSVB;

/* Ignore configuration updates for heuristically guessed vBuckets for a
 * maximum amount of [n] seconds */
#define MAX_KEEP_GUESS 20

static int
should_keep_guess(lcb_GUESSVB *guess, lcbvb_VBUCKET *vb)
{
    if (guess->newix == guess->oldix) {
        /* Heuristic position is the same as starting position */
        return 0;
    }
    if (vb->servers[0] != guess->oldix) {
        /* Previous master changed */
        return 0;
    }

    if (time(NULL) - guess->last_update > MAX_KEEP_GUESS) {
        /* Last usage too old */
        return 0;
    }

    return 1;
}

void
lcb_vbguess_newconfig(lcb_t instance, lcbvb_CONFIG *cfg, lcb_GUESSVB *guesses)
{
    unsigned ii;

    if (!guesses) {
        return;
    }

    for (ii = 0; ii < cfg->nvb; ii++) {
        lcb_GUESSVB *guess = guesses + ii;
        lcbvb_VBUCKET *vb = cfg->vbuckets + ii;

        if (!guess->used) {
            continue;
        }

        /* IF: Heuristically learned a new index, _and_ the old index (which is
         * known to be bad) is the same index stated by the new config */
        if (should_keep_guess(guess, vb)) {
            lcb_log(LOGARGS(instance, TRACE), "Keeping heuristically guessed index. VBID=%d. Current=%d. Old=%d.", ii, guess->newix, guess->oldix);
            vb->servers[0] = guess->newix;
        } else {
            /* We don't reassign to the guess structure here. The idea is that
             * we will simply use the new config. If this gives us problems, the
             * config will re-learn again. */
            lcb_log(LOGARGS(instance, TRACE), "Ignoring heuristically guessed index. VBID=%d. Current=%d. Old=%d. New=%d", ii, guess->newix, guess->oldix, vb->servers[0]);
            guess->used = 0;
        }
    }
}

int
lcb_vbguess_remap(lcb_t instance, int vbid, int bad)
{

    if (LCBT_SETTING(instance, vb_noguess)) {
        int newix = lcbvb_nmv_remap_ex(LCBT_VBCONFIG(instance), vbid, bad, 0);
        if (newix > -1 && newix != bad) {
            lcb_log(LOGARGS(instance, TRACE), "Got new index from ffmap. VBID=%d. Old=%d. New=%d", vbid, bad, newix);
        }
        return newix;

    } else {
        lcb_GUESSVB *guesses = instance->vbguess;
        lcb_GUESSVB *guess = guesses + vbid;
        int newix = lcbvb_nmv_remap_ex(LCBT_VBCONFIG(instance), vbid, bad, 1);
        if (!guesses) {
            guesses = instance->vbguess = reinterpret_cast<lcb_GUESSVB*>(calloc(
                LCBT_VBCONFIG(instance)->nvb, sizeof *guesses));
        }
        if (newix > -1 && newix != bad) {
            guess->newix = newix;
            guess->oldix = bad;
            guess->used = 1;
            guess->last_update = time(NULL);
            lcb_log(LOGARGS(instance, TRACE), "Guessed new heuristic index VBID=%d. Old=%d. New=%d", vbid, bad, newix);
        }
        return newix;
    }
}

/**
 * Finds the index of an older server using the current config.
 *
 * This function is used to help reuse the server structures for memcached
 * packets.
 *
 * @param oldconfig The old configuration. This is the configuration the
 * old server is bound to
 * @param newconfig The new configuration. This will be inspected for new
 * nodes which may have been added, and ones which may have been removed.
 * @param server The server to match
 * @return The new index, or -1 if the current server is not present in the new
 * config.
 */
static int
find_new_data_index(lcbvb_CONFIG *oldconfig, lcbvb_CONFIG* newconfig,
    lcb::Server *server)
{
    const char *old_datahost = lcbvb_get_hostport(oldconfig,
        server->get_index(), LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN);

    if (!old_datahost) {
        /* Old server had no data service */
        return -1;
    }

    for (size_t ii = 0; ii < LCBVB_NSERVERS(newconfig); ii++) {
        const char *new_datahost = lcbvb_get_hostport(newconfig, ii,
            LCBVB_SVCTYPE_DATA, LCBVB_SVCMODE_PLAIN);
        if (new_datahost && strcmp(new_datahost, old_datahost) == 0) {
            return ii;
        }
    }
    return -1;
}

static void
log_vbdiff(lcb_t instance, lcbvb_CONFIGDIFF *diff)
{
    lcb_log(LOGARGS(instance, INFO), "Config Diff: [ vBuckets Modified=%d ], [Sequence Changed=%d]", diff->n_vb_changes, diff->sequence_changed);
    if (diff->servers_added) {
        for (char **curserver = diff->servers_added; *curserver; curserver++) {
            lcb_log(LOGARGS(instance, INFO), "Detected server %s added", *curserver);
        }
    }
    if (diff->servers_removed) {
        for (char **curserver = diff->servers_removed; *curserver; curserver++) {
            lcb_log(LOGARGS(instance, INFO), "Detected server %s removed", *curserver);
        }
    }
}

/**
 * This callback is invoked for packet relocation twice. It tries to relocate
 * commands to their destination server. Some commands may not be relocated
 * either because they have no explicit "Relocation Information" (i.e. no
 * specific vbucket) or because the command is tied to a specific server (i.e.
 * CMD_STAT).
 *
 * Note that `KEEP_PACKET` here doesn't mean to "Save" the packet, but rather
 * to keep the packet in the current queue (so that if the server ends up
 * being removed, the command will fail); rather than being relocated to
 * another server.
 */
static int
iterwipe_cb(mc_CMDQUEUE *cq, mc_PIPELINE *oldpl, mc_PACKET *oldpkt, void *)
{
    protocol_binary_request_header hdr;
    lcb::Server *srv = static_cast<lcb::Server *>(oldpl);
    int newix;

    mcreq_read_hdr(oldpkt, &hdr);

    if (!lcb_should_retry(srv->get_settings(), oldpkt, LCB_MAX_ERROR)) {
        return MCREQ_KEEP_PACKET;
    }

    if (LCBVB_DISTTYPE(cq->config) == LCBVB_DIST_VBUCKET) {
        newix = lcbvb_vbmaster(cq->config, ntohs(hdr.request.vbucket));

    } else {
        const void *key = NULL;
        lcb_SIZE nkey = 0;
        int tmpid;

        /* XXX: We ignore hashkey. This is going away soon, and is probably
         * better than simply failing the items. */
        mcreq_get_key(oldpkt, &key, &nkey);
        lcbvb_map_key(cq->config, key, nkey, &tmpid, &newix);
    }

    if (newix < 0 || newix > (int)cq->npipelines-1) {
        return MCREQ_KEEP_PACKET;
    }

    mc_PIPELINE *newpl = cq->pipelines[newix];
    if (newpl == oldpl || newpl == NULL) {
        return MCREQ_KEEP_PACKET;
    }

    lcb_log(LOGARGS((lcb_t)cq->cqdata, DEBUG), "Remapped packet %p (SEQ=%u) from "SERVER_FMT " to " SERVER_FMT,
        (void*)oldpkt, oldpkt->opaque, SERVER_ARGS((lcb::Server*)oldpl), SERVER_ARGS((lcb::Server*)newpl));

    /** Otherwise, copy over the packet and find the new vBucket to map to */
    mc_PACKET *newpkt = mcreq_renew_packet(oldpkt);
    newpkt->flags &= ~MCREQ_STATE_FLAGS;
    mcreq_reenqueue_packet(newpl, newpkt);
    mcreq_packet_handled(oldpl, oldpkt);
    return MCREQ_REMOVE_PACKET;
}

static void
replace_config(lcb_t instance, lcbvb_CONFIG *oldconfig, lcbvb_CONFIG *newconfig)
{
    mc_CMDQUEUE *cq = &instance->cmdq;
    mc_PIPELINE **ppold, **ppnew;
    unsigned ii, nold, nnew;

    assert(LCBT_VBCONFIG(instance) == newconfig);

    nnew = LCBVB_NSERVERS(newconfig);
    ppnew = reinterpret_cast<mc_PIPELINE**>(calloc(nnew, sizeof(*ppnew)));
    ppold = mcreq_queue_take_pipelines(cq, &nold);

    /**
     * Determine which existing servers are still part of the new cluster config
     * and place it inside the new list.
     */
    for (ii = 0; ii < nold; ii++) {
        lcb::Server *cur = static_cast<lcb::Server *>(ppold[ii]);
        int newix = find_new_data_index(oldconfig, newconfig, cur);
        if (newix > -1) {
            cur->set_new_index(newix);
            ppnew[newix] = cur;
            ppold[ii] = NULL;
            lcb_log(LOGARGS(instance, INFO), "Reusing server "SERVER_FMT". OldIndex=%d. NewIndex=%d", SERVER_ARGS(cur), ii, newix);
        }
    }

    /**
     * Once we've moved the kept servers to the new list, allocate new lcb::Server
     * structures for slots that don't have an existing lcb::Server. We must do
     * this before add_pipelines() is called, so that there are no holes inside
     * ppnew
     */
    for (ii = 0; ii < nnew; ii++) {
        if (!ppnew[ii]) {
            ppnew[ii] = new lcb::Server(instance, ii);
        }
    }

    /**
     * Once we have all the server structures in place for the new config,
     * transfer the new config along with the new list over to the CQ structure.
     */
    mcreq_queue_add_pipelines(cq, ppnew, nnew, newconfig);
    for (ii = 0; ii < nnew; ii++) {
        mcreq_iterwipe(cq, ppnew[ii], iterwipe_cb, NULL);
    }

    /**
     * Go through all the servers that are to be removed and relocate commands
     * from their queues into the new queues
     */
    for (ii = 0; ii < nold; ii++) {
        if (!ppold[ii]) {
            continue;
        }

        mcreq_iterwipe(cq, ppold[ii], iterwipe_cb, NULL);
        static_cast<lcb::Server*>(ppold[ii])->purge(LCB_MAP_CHANGED);
        static_cast<lcb::Server*>(ppold[ii])->close();
    }

    for (ii = 0; ii < nnew; ii++) {
        if (static_cast<lcb::Server*>(ppnew[ii])->has_pending()) {
            ppnew[ii]->flush_start(ppnew[ii]);
        }
    }

    free(ppnew);
    free(ppold);
}

void lcb_update_vbconfig(lcb_t instance, clconfig_info *config)
{
    lcb_configuration_t change_status;
    clconfig_info *old_config = instance->cur_configinfo;
    mc_CMDQUEUE *q = &instance->cmdq;

    instance->cur_configinfo = config;
    lcb_clconfig_incref(config);
    q->config = instance->cur_configinfo->vbc;
    q->cqdata = instance;

    if (old_config) {
        lcbvb_CONFIGDIFF *diff = lcbvb_compare(old_config->vbc, config->vbc);

        if (diff) {
            log_vbdiff(instance, diff);
            lcbvb_free_diff(diff);
        }

        /* Apply the vb guesses */
        lcb_vbguess_newconfig(instance, config->vbc, instance->vbguess);

        replace_config(instance, old_config->vbc, config->vbc);
        lcb_clconfig_decref(old_config);
        change_status = LCB_CONFIGURATION_CHANGED;
    } else {
        size_t nservers = VB_NSERVERS(config->vbc);
        std::vector<mc_PIPELINE*> servers;

        for (size_t ii = 0; ii < nservers; ii++) {
            servers.push_back(new lcb::Server(instance, ii));
        }

        mcreq_queue_add_pipelines(q, &servers[0], nservers, config->vbc);
        change_status = LCB_CONFIGURATION_NEW;
    }

    /* Update the list of nodes here for server list */
    instance->ht_nodes->clear();
    for (size_t ii = 0; ii < LCBVB_NSERVERS(config->vbc); ++ii) {
        const char *hp = lcbvb_get_hostport(config->vbc, ii,
            LCBVB_SVCTYPE_MGMT, LCBVB_SVCMODE_PLAIN);
        if (hp) {
            instance->ht_nodes->add(hp, LCB_CONFIG_HTTP_PORT);
        }
    }

    instance->callbacks.configuration(instance, change_status);
    lcb_maybe_breakout(instance);
}
<|endoftext|>


<commit_before>#include "eval.hh"
#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
#include "local-fs-store.hh"

#include <nlohmann/json.hpp>

using namespace nix;

struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
{
    Path outLink = "result";
    BuildMode buildMode = bmNormal;

    CmdBuild()
    {
        addFlag({
            .longName = "out-link",
            .shortName = 'o',
            .description = "Use *path* as prefix for the symlinks to the build results. It defaults to `result`.",
            .labels = {"path"},
            .handler = {&outLink},
            .completer = completePath
        });

        addFlag({
            .longName = "no-link",
            .description = "Do not create symlinks to the build results.",
            .handler = {&outLink, Path("")},
        });

        addFlag({
            .longName = "rebuild",
            .description = "Rebuild an already built package and compare the result to the existing store paths.",
            .handler = {&buildMode, bmCheck},
        });
    }

    std::string description() override
    {
        return "build a derivation or fetch a store path";
    }

    std::string doc() override
    {
        return
          #include "build.md"
          ;
    }

    void run(ref<Store> store) override
    {
        auto buildables = build(store, dryRun ? Realise::Nothing : Realise::Outputs, installables, buildMode);

        if (json) logger->cout("%s", derivedPathsWithHintsToJSON(buildables, store).dump());

        if (dryRun) return;

        if (outLink != "")
            if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
                for (const auto & [_i, buildable] : enumerate(buildables)) {
                    auto i = _i;
                    std::visit(overloaded {
                        [&](BuiltPath::Opaque bo) {
                            std::string symlink = outLink;
                            if (i) symlink += fmt("-%d", i);
                            store2->addPermRoot(bo.path, absPath(symlink));
                        },
                        [&](BuiltPath::Built bfd) {
                            auto builtOutputs = store->queryDerivationOutputMap(bfd.drvPath);
                            for (auto & output : builtOutputs) {
                                std::string symlink = outLink;
                                if (i) symlink += fmt("-%d", i);
                                if (output.first != "out") symlink += fmt("-%s", output.first);
                                store2->addPermRoot(output.second, absPath(symlink));
                            }
                        },
                    }, buildable.raw());
                }

        updateProfile(buildables);
    }
};

static auto rCmdBuild = registerCommand<CmdBuild>("build");
<commit_msg>Only symlink the requested outputs in `nix build`<commit_after>#include "eval.hh"
#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
#include "local-fs-store.hh"

#include <nlohmann/json.hpp>

using namespace nix;

struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
{
    Path outLink = "result";
    BuildMode buildMode = bmNormal;

    CmdBuild()
    {
        addFlag({
            .longName = "out-link",
            .shortName = 'o',
            .description = "Use *path* as prefix for the symlinks to the build results. It defaults to `result`.",
            .labels = {"path"},
            .handler = {&outLink},
            .completer = completePath
        });

        addFlag({
            .longName = "no-link",
            .description = "Do not create symlinks to the build results.",
            .handler = {&outLink, Path("")},
        });

        addFlag({
            .longName = "rebuild",
            .description = "Rebuild an already built package and compare the result to the existing store paths.",
            .handler = {&buildMode, bmCheck},
        });
    }

    std::string description() override
    {
        return "build a derivation or fetch a store path";
    }

    std::string doc() override
    {
        return
          #include "build.md"
          ;
    }

    void run(ref<Store> store) override
    {
        auto buildables = build(store, dryRun ? Realise::Nothing : Realise::Outputs, installables, buildMode);

        if (json) logger->cout("%s", derivedPathsWithHintsToJSON(buildables, store).dump());

        if (dryRun) return;

        if (outLink != "")
            if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
                for (const auto & [_i, buildable] : enumerate(buildables)) {
                    auto i = _i;
                    std::visit(overloaded {
                        [&](BuiltPath::Opaque bo) {
                            std::string symlink = outLink;
                            if (i) symlink += fmt("-%d", i);
                            store2->addPermRoot(bo.path, absPath(symlink));
                        },
                        [&](BuiltPath::Built bfd) {
                            for (auto & output : bfd.outputs) {
                                std::string symlink = outLink;
                                if (i) symlink += fmt("-%d", i);
                                if (output.first != "out") symlink += fmt("-%s", output.first);
                                store2->addPermRoot(output.second, absPath(symlink));
                            }
                        },
                    }, buildable.raw());
                }

        updateProfile(buildables);
    }
};

static auto rCmdBuild = registerCommand<CmdBuild>("build");
<|endoftext|>


<commit_before>#include "src/operation.h"
#include "src/internal.h"
#include "src/graph.h"
#include "src/tensor.h"

using namespace v8;

namespace TensorflowNode {

#define OP_PROPERTY_GETTER(NAME)                                                    \
  TensorflowNode::Operation* operation =                                            \
    ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());                     \
  if (operation->_oper == NULL)                                                     \
    return Nan::ThrowTypeError("operation is not created yet.");                    \
  Local<String> val =                                                               \
    Nan::New<String>(TF_Operation ## NAME(operation->_oper)).ToLocalChecked();      \
  info.GetReturnValue().Set(val);                                                   \

NAN_MODULE_INIT(Operation::Init) {
  Local<FunctionTemplate> tmpl = Nan::New<FunctionTemplate>(New);
  tmpl->SetClassName(Nan::New("Operation").ToLocalChecked());
  tmpl->InstanceTemplate()->SetInternalFieldCount(1);

  Nan::SetAccessor(tmpl->InstanceTemplate(),
    Nan::New<String>("name").ToLocalChecked(), NameGetter);
  Nan::SetAccessor(tmpl->InstanceTemplate(),
    Nan::New<String>("type").ToLocalChecked(), TypeGetter);
  Nan::SetAccessor(tmpl->InstanceTemplate(),
    Nan::New<String>("device").ToLocalChecked(), DeviceGetter);
  Nan::SetAccessor(tmpl->InstanceTemplate(),
    Nan::New<String>("outputs").ToLocalChecked(), OutputsGetter);

  Nan::SetPrototypeMethod(tmpl, "setAttrType", SetAttrType);
  Nan::SetPrototypeMethod(tmpl, "SetAttrBool", SetAttrBool);
  Nan::SetPrototypeMethod(tmpl, "SetAttrInt", SetAttrInt);
  Nan::SetPrototypeMethod(tmpl, "SetAttrFloat", SetAttrFloat);
  Nan::SetPrototypeMethod(tmpl, "setAttrString", SetAttrString);
  Nan::SetPrototypeMethod(tmpl, "setAttrShape", SetAttrShape);
  Nan::SetPrototypeMethod(tmpl, "setAttrTensor", SetAttrTensor);
  Nan::SetPrototypeMethod(tmpl, "addInput", AddInput);
  Nan::SetPrototypeMethod(tmpl, "addInputList", AddInputList);
  Nan::SetPrototypeMethod(tmpl, "addControlInput", AddControlInput);
  Nan::SetPrototypeMethod(tmpl, "finish", Finish);

  Local<Function> func = Nan::GetFunction(tmpl).ToLocalChecked();
  constructor().Reset(func);
  Nan::Set(target, Nan::New("Operation").ToLocalChecked(), func);
}

NAN_METHOD(Operation::New) {
  if (info.Length() == 0) {
    return Nan::ThrowTypeError("should call with graph, type and name.");
  }
  TensorflowNode::Graph* graph = ObjectWrap::Unwrap<TensorflowNode::Graph>(info[0]->ToObject());
  TensorflowNode::Operation* operation;

  if (info.Length() >= 3) {
    V8_STRING_TO_CSTR(type, info[1]);
    V8_STRING_TO_CSTR(name, info[2]);
    operation = new TensorflowNode::Operation(graph->_graph, type, name);
  } else {
    operation = new TensorflowNode::Operation();
  }
  operation->Wrap(info.This());

  // this._graph = graph
  Nan::Set(info.This(), Nan::New<String>("_graph").ToLocalChecked(), info[0]);
  info.GetReturnValue().Set(info.This());
}

NAN_PROPERTY_GETTER(Operation::NameGetter) {
  OP_PROPERTY_GETTER(Name);
}

NAN_PROPERTY_GETTER(Operation::TypeGetter) {
  OP_PROPERTY_GETTER(OpType);
}

NAN_PROPERTY_GETTER(Operation::DeviceGetter) {
  OP_PROPERTY_GETTER(Device);
}

NAN_PROPERTY_GETTER(Operation::OutputsGetter) {
  Local<Object> graphObj = Nan::Get(info.This(), Nan::New<String>("_graph").ToLocalChecked()).ToLocalChecked()->ToObject();
  TensorflowNode::Graph* graph = ObjectWrap::Unwrap<TensorflowNode::Graph>(graphObj);
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  if (operation->_oper == NULL)
    return Nan::ThrowTypeError("operation is not created yet.");
  int numOfOutputs = TF_OperationNumOutputs(operation->_oper);
  Local<Array> outputsObj = Nan::New<Array>(numOfOutputs);

  for (int i = 0; i < numOfOutputs; i++) {
    TF_Output output = TF_Output{operation->_oper, i};
    int numOfDims = TF_GraphGetTensorNumDims(graph->_graph, output, status);
    if (TF_GetCode(status) != TF_OK) {
      ThrowStatusError();
      return;
    }
    int64_t dims[numOfDims];
    TF_GraphGetTensorShape(graph->_graph, output, dims, numOfDims, status);
    if (TF_GetCode(status) != TF_OK) {
      ThrowStatusError();
      return;
    }

    TF_DataType type = TF_OperationOutputType(output);
    Local<Object> item = Nan::New<Object>();
    Local<Array> shape = Nan::New<Array>(numOfDims);
    for (int j = 0; j < numOfDims; j++) {
      Nan::Set(shape, j, Nan::New<Number>(dims[i]));
    }
    Nan::Set(item, Nan::New<String>("shape").ToLocalChecked(), shape);
    Nan::Set(item, Nan::New<String>("type").ToLocalChecked(), Nan::New<Number>((int)type));
    Nan::Set(outputsObj, i, item);
  }
  info.GetReturnValue().Set(outputsObj);
}

NAN_METHOD(Operation::SetAttrType) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and type are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  TF_DataType type = (TF_DataType)info[1]->Uint32Value();
  TF_SetAttrType(operation->_description, name, type);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrBool) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and value are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  bool value = info[1]->BooleanValue();
  TF_SetAttrBool(operation->_description, name, value);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrInt) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and value are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  int64_t value = info[1]->IntegerValue();
  TF_SetAttrInt(operation->_description, name, value);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrFloat) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and value are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  double value = info[1]->NumberValue();
  TF_SetAttrFloat(operation->_description, name, (float)value);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrString) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and value are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  V8_STRING_TO_CSTR(value, info[1]);
  size_t len = info[1]->ToString()->Length();
  TF_SetAttrString(operation->_description, name, value, len);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrShape) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and dims/shape are required");
  }

  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  Local<Object> maybeDims = info[1]->ToObject();
  if (!maybeDims->IsArray()) {
    Nan::ThrowError("The second parameter `shape` should be an array");
    return;
  }

  Local<String> lenstr = Nan::New("length").ToLocalChecked();
  size_t numOfDims = Nan::Get(maybeDims, lenstr).ToLocalChecked()->Uint32Value();
  int64_t dims[numOfDims];

  for (size_t i = 0; i < numOfDims; i++) {
    dims[i] = Nan::Get(maybeDims, i).ToLocalChecked()->Int32Value();
  }

  TF_SetAttrShape(operation->_description, name, dims, numOfDims);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrTensor) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and tensor are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  TensorflowNode::Tensor* tensor = ObjectWrap::Unwrap<TensorflowNode::Tensor>(info[1]->ToObject());
  TF_SetAttrTensor(operation->_description, name, tensor->_tensor, status);
  if (TF_GetCode(status) != TF_OK) {
    return ThrowStatusError();
  }
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::AddInput) {
  if (info.Length() == 0) {
    Nan::ThrowTypeError("input is required");
    return;
  }

  TF_Output input;
  {
    int index = info[0]->Int32Value();
    TensorflowNode::Operation* op = ObjectWrap::Unwrap<TensorflowNode::Operation>(info[1]->ToObject());
    input = TF_Output{op->_oper, index};
  }

  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  TF_AddInput(operation->_description, input);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::AddInputList) {
  if (info.Length() == 0 || !info[0]->IsArray()) {
    Nan::ThrowTypeError("inputs is required and be an Array");
    return;
  }

  Local<Array> list = Local<Array>::Cast(info[0]);
  size_t len = list->Length();
  TF_Output inputs[len];

  for (size_t i = 0; i < len; i++) {
    Local<Object> item = Nan::Get(list, i).ToLocalChecked()->ToObject();
    Local<Value> indexKey = Nan::New<String>("index").ToLocalChecked();
    Local<Value> opKey = Nan::New<String>("op").ToLocalChecked();

    int index = Nan::Get(item, indexKey).ToLocalChecked()->Int32Value();
    Local<Object> op = Nan::Get(item, opKey).ToLocalChecked()->ToObject();

    TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(op);
    inputs[i] = TF_Output{operation->_oper, index};
  }

  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  TF_AddInputList(operation->_description, inputs, len);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::AddControlInput) {
  if (info.Length() == 0) {
    Nan::ThrowTypeError("input is required");
    return;
  }
  TensorflowNode::Operation* input = ObjectWrap::Unwrap<TensorflowNode::Operation>(info[0]->ToObject());
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  TF_AddControlInput(operation->_description, input->_oper);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::Finish) {
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  if (operation->_oper != NULL) {
    return Nan::ThrowError("finish is can be called once");
  }

  TF_Operation* oper = TF_FinishOperation(operation->_description, status);
  if (TF_GetCode(status) == TF_OK) {
    operation->_oper = oper;
  } else {
    ThrowStatusError();
  }
}

Local<Object>
Operation::NewFromOperation(Local<Object> graph, TF_Operation* oper) {
  Nan::EscapableHandleScope scope;
  Local<Function> tmpl = Nan::New(constructor());
  Local<Value> argv[1] = { graph };
  Local<Object> target = Nan::NewInstance(tmpl, 1, argv).ToLocalChecked();
  TensorflowNode::Operation *operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(target);
  operation->_oper = oper;
  return scope.Escape(target);
}

Operation::Operation() {
  // Placeholder
}

Operation::Operation(TF_Graph* graph, const char* type, const char* name) {
  _description = TF_NewOperation(graph, type, name);
  _oper = NULL;
}

Operation::~Operation() {
  // TODO
}

inline Nan::Persistent<v8::Function>&
Operation::constructor() {
  static Nan::Persistent<v8::Function> my_constructor;
  return my_constructor;
}

}<commit_msg>operation: consider numOfDims(-1) in outputs getter<commit_after>#include "src/operation.h"
#include "src/internal.h"
#include "src/graph.h"
#include "src/tensor.h"

using namespace v8;

namespace TensorflowNode {

#define OP_PROPERTY_GETTER(NAME)                                                    \
  TensorflowNode::Operation* operation =                                            \
    ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());                     \
  if (operation->_oper == NULL)                                                     \
    return Nan::ThrowTypeError("operation is not created yet.");                    \
  Local<String> val =                                                               \
    Nan::New<String>(TF_Operation ## NAME(operation->_oper)).ToLocalChecked();      \
  info.GetReturnValue().Set(val);                                                   \

NAN_MODULE_INIT(Operation::Init) {
  Local<FunctionTemplate> tmpl = Nan::New<FunctionTemplate>(New);
  tmpl->SetClassName(Nan::New("Operation").ToLocalChecked());
  tmpl->InstanceTemplate()->SetInternalFieldCount(1);

  Nan::SetAccessor(tmpl->InstanceTemplate(),
    Nan::New<String>("name").ToLocalChecked(), NameGetter);
  Nan::SetAccessor(tmpl->InstanceTemplate(),
    Nan::New<String>("type").ToLocalChecked(), TypeGetter);
  Nan::SetAccessor(tmpl->InstanceTemplate(),
    Nan::New<String>("device").ToLocalChecked(), DeviceGetter);
  Nan::SetAccessor(tmpl->InstanceTemplate(),
    Nan::New<String>("outputs").ToLocalChecked(), OutputsGetter);

  Nan::SetPrototypeMethod(tmpl, "setAttrType", SetAttrType);
  Nan::SetPrototypeMethod(tmpl, "SetAttrBool", SetAttrBool);
  Nan::SetPrototypeMethod(tmpl, "SetAttrInt", SetAttrInt);
  Nan::SetPrototypeMethod(tmpl, "SetAttrFloat", SetAttrFloat);
  Nan::SetPrototypeMethod(tmpl, "setAttrString", SetAttrString);
  Nan::SetPrototypeMethod(tmpl, "setAttrShape", SetAttrShape);
  Nan::SetPrototypeMethod(tmpl, "setAttrTensor", SetAttrTensor);
  Nan::SetPrototypeMethod(tmpl, "addInput", AddInput);
  Nan::SetPrototypeMethod(tmpl, "addInputList", AddInputList);
  Nan::SetPrototypeMethod(tmpl, "addControlInput", AddControlInput);
  Nan::SetPrototypeMethod(tmpl, "finish", Finish);

  Local<Function> func = Nan::GetFunction(tmpl).ToLocalChecked();
  constructor().Reset(func);
  Nan::Set(target, Nan::New("Operation").ToLocalChecked(), func);
}

NAN_METHOD(Operation::New) {
  if (info.Length() == 0) {
    return Nan::ThrowTypeError("should call with graph, type and name.");
  }
  TensorflowNode::Graph* graph = ObjectWrap::Unwrap<TensorflowNode::Graph>(info[0]->ToObject());
  TensorflowNode::Operation* operation;

  if (info.Length() >= 3) {
    V8_STRING_TO_CSTR(type, info[1]);
    V8_STRING_TO_CSTR(name, info[2]);
    operation = new TensorflowNode::Operation(graph->_graph, type, name);
  } else {
    operation = new TensorflowNode::Operation();
  }
  operation->Wrap(info.This());

  // this._graph = graph
  Nan::Set(info.This(), Nan::New<String>("_graph").ToLocalChecked(), info[0]);
  info.GetReturnValue().Set(info.This());
}

NAN_PROPERTY_GETTER(Operation::NameGetter) {
  OP_PROPERTY_GETTER(Name);
}

NAN_PROPERTY_GETTER(Operation::TypeGetter) {
  OP_PROPERTY_GETTER(OpType);
}

NAN_PROPERTY_GETTER(Operation::DeviceGetter) {
  OP_PROPERTY_GETTER(Device);
}

NAN_PROPERTY_GETTER(Operation::OutputsGetter) {
  Local<Object> graphObj = Nan::Get(info.This(), Nan::New<String>("_graph").ToLocalChecked()).ToLocalChecked()->ToObject();
  TensorflowNode::Graph* graph = ObjectWrap::Unwrap<TensorflowNode::Graph>(graphObj);
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  if (operation->_oper == NULL)
    return Nan::ThrowTypeError("operation is not created yet.");
  int numOfOutputs = TF_OperationNumOutputs(operation->_oper);
  Local<Array> outputsObj = Nan::New<Array>(numOfOutputs);

  for (int i = 0; i < numOfOutputs; i++) {
    TF_Output output = TF_Output{operation->_oper, i};
    int numOfDims = TF_GraphGetTensorNumDims(graph->_graph, output, status);
    if (TF_GetCode(status) != TF_OK) {
      ThrowStatusError();
      return;
    }

    TF_DataType type = TF_OperationOutputType(output);
    Local<Object> item = Nan::New<Object>();
    Local<Array> shape;

    // The `numOfDims` possibly to be -1
    if (numOfDims > 0) {
      int64_t dims[numOfDims];
      TF_GraphGetTensorShape(graph->_graph, output, dims, numOfDims, status);
      if (TF_GetCode(status) != TF_OK) {
        ThrowStatusError();
        return;
      }
      shape = Nan::New<Array>(numOfDims);
    } else {
      // if the `numOfDims` is -1, shape should be [], as a scalar value.
      shape = Nan::New<Array>(0);
    }
    Nan::Set(item, Nan::New<String>("shape").ToLocalChecked(), shape);
    Nan::Set(item,
      Nan::New<String>("type").ToLocalChecked(),
      Nan::New<Number>((int)type));
    Nan::Set(outputsObj, i, item);
  }
  info.GetReturnValue().Set(outputsObj);
}

NAN_METHOD(Operation::SetAttrType) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and type are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  TF_DataType type = (TF_DataType)info[1]->Uint32Value();
  TF_SetAttrType(operation->_description, name, type);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrBool) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and value are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  bool value = info[1]->BooleanValue();
  TF_SetAttrBool(operation->_description, name, value);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrInt) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and value are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  int64_t value = info[1]->IntegerValue();
  TF_SetAttrInt(operation->_description, name, value);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrFloat) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and value are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  double value = info[1]->NumberValue();
  TF_SetAttrFloat(operation->_description, name, (float)value);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrString) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and value are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  V8_STRING_TO_CSTR(value, info[1]);
  size_t len = info[1]->ToString()->Length();
  TF_SetAttrString(operation->_description, name, value, len);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrShape) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and dims/shape are required");
  }

  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  Local<Object> maybeDims = info[1]->ToObject();
  if (!maybeDims->IsArray()) {
    Nan::ThrowError("The second parameter `shape` should be an array");
    return;
  }

  Local<String> lenstr = Nan::New("length").ToLocalChecked();
  size_t numOfDims = Nan::Get(maybeDims, lenstr).ToLocalChecked()->Uint32Value();
  int64_t dims[numOfDims];

  for (size_t i = 0; i < numOfDims; i++) {
    dims[i] = Nan::Get(maybeDims, i).ToLocalChecked()->Int32Value();
  }

  TF_SetAttrShape(operation->_description, name, dims, numOfDims);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::SetAttrTensor) {
  if (info.Length() != 2) {
    Nan::ThrowTypeError("attr name and tensor are required");
    return;
  }
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  V8_STRING_TO_CSTR(name, info[0]);
  TensorflowNode::Tensor* tensor = ObjectWrap::Unwrap<TensorflowNode::Tensor>(info[1]->ToObject());
  TF_SetAttrTensor(operation->_description, name, tensor->_tensor, status);
  if (TF_GetCode(status) != TF_OK) {
    return ThrowStatusError();
  }
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::AddInput) {
  if (info.Length() == 0) {
    Nan::ThrowTypeError("input is required");
    return;
  }

  TF_Output input;
  {
    int index = info[0]->Int32Value();
    TensorflowNode::Operation* op = ObjectWrap::Unwrap<TensorflowNode::Operation>(info[1]->ToObject());
    input = TF_Output{op->_oper, index};
  }

  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  TF_AddInput(operation->_description, input);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::AddInputList) {
  if (info.Length() == 0 || !info[0]->IsArray()) {
    Nan::ThrowTypeError("inputs is required and be an Array");
    return;
  }

  Local<Array> list = Local<Array>::Cast(info[0]);
  size_t len = list->Length();
  TF_Output inputs[len];

  for (size_t i = 0; i < len; i++) {
    Local<Object> item = Nan::Get(list, i).ToLocalChecked()->ToObject();
    Local<Value> indexKey = Nan::New<String>("index").ToLocalChecked();
    Local<Value> opKey = Nan::New<String>("op").ToLocalChecked();

    int index = Nan::Get(item, indexKey).ToLocalChecked()->Int32Value();
    Local<Object> op = Nan::Get(item, opKey).ToLocalChecked()->ToObject();

    TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(op);
    inputs[i] = TF_Output{operation->_oper, index};
  }

  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  TF_AddInputList(operation->_description, inputs, len);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::AddControlInput) {
  if (info.Length() == 0) {
    Nan::ThrowTypeError("input is required");
    return;
  }
  TensorflowNode::Operation* input = ObjectWrap::Unwrap<TensorflowNode::Operation>(info[0]->ToObject());
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  TF_AddControlInput(operation->_description, input->_oper);
  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(Operation::Finish) {
  TensorflowNode::Operation* operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(info.This());
  if (operation->_oper != NULL) {
    return Nan::ThrowError("finish is can be called once");
  }

  TF_Operation* oper = TF_FinishOperation(operation->_description, status);
  if (TF_GetCode(status) == TF_OK) {
    operation->_oper = oper;
  } else {
    ThrowStatusError();
  }
}

Local<Object>
Operation::NewFromOperation(Local<Object> graph, TF_Operation* oper) {
  Nan::EscapableHandleScope scope;
  Local<Function> tmpl = Nan::New(constructor());
  Local<Value> argv[1] = { graph };
  Local<Object> target = Nan::NewInstance(tmpl, 1, argv).ToLocalChecked();
  TensorflowNode::Operation *operation = ObjectWrap::Unwrap<TensorflowNode::Operation>(target);
  operation->_oper = oper;
  return scope.Escape(target);
}

Operation::Operation() {
  // Placeholder
}

Operation::Operation(TF_Graph* graph, const char* type, const char* name) {
  _description = TF_NewOperation(graph, type, name);
  _oper = NULL;
}

Operation::~Operation() {
  // TODO
}

inline Nan::Persistent<v8::Function>&
Operation::constructor() {
  static Nan::Persistent<v8::Function> my_constructor;
  return my_constructor;
}

}<|endoftext|>


<commit_before>#ifndef BUBBLE_SORT_H
#define BUBBLE_SORT_H 1

#ifndef MAX
#define MAX 60
#endif
#include <algorithm>
using namespace std;

void BubbleSort(int s[MAX], int beg, int end)
{
    for (int i = end-1; i >= beg; --i)
        for (int j = beg; j < i; ++j)
            if (s[j] > s[j+1])
                swap(s[j], s[j+1]);
}

#endif
<commit_msg>add comment<commit_after>#ifndef BUBBLE_SORT_H
#define BUBBLE_SORT_H 1

#ifndef MAX
#define MAX 60
#endif
#include <algorithm>
using namespace std;

/* ----------------------------------------------------------------------*/
/**
 * @brief BubbleSort
 * 冒泡排序
 *
 * @param s[MAX]        无序序列
 * @param beg           序列s的末尾下标加1，即左闭右开区间[beg, end)
 * @param end           序列s的末尾下标加1
 */
/* ----------------------------------------------------------------------*/
void BubbleSort(int s[MAX], int beg, int end)
{
    for (int i = end-1; i >= beg; --i)
        for (int j = beg; j < i; ++j)
            if (s[j] > s[j+1])
                swap(s[j], s[j+1]);
}

#endif
<|endoftext|>


<commit_before>//
//  Copyright 2013 Uncodin, Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

#include "parser.h"

using namespace std;

static void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque);
static void rndr_blockquote(struct buf *ob, struct buf *text, void *opaque);
static void rndr_header(struct buf *ob, struct buf *text, int level, void *opaque);
static void rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque);
static void rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque);
static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque);
static int rndr_codespan(struct buf *ob, struct buf *text, void *opaque);
static int rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_linebreak(struct buf *ob, void *opaque);
static int rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque);
static void rndr_normal_text(struct buf *ob, struct buf *text, void *opaque);

struct mkd_renderer mkd_callbacks = {
	/* document-level callbacks */
	NULL,                 // prolog
	NULL,                 // epilogue

	/* block-level callbacks */
	rndr_blockcode,       // block code
	rndr_blockquote,      // block quote
	NULL,                 // block html
	rndr_header,          // header
	NULL,                 // hrule
	rndr_list,            // list
	rndr_listitem,        // listitem
	rndr_paragraph,       // paragraph
	NULL,                 // table
	NULL,                 // table cell
	NULL,                 // table row

	/* span-level callbacks */
	NULL,                 // autolink
	rndr_codespan,        // codespan
	rndr_double_emphasis, // double emphasis
	rndr_emphasis,        // emphasis
	NULL,                 // image
	rndr_linebreak,       // line break
	rndr_link,            // link
	NULL,                 // raw html tag
	rndr_triple_emphasis, // triple emphasis

	/* low-level callbacks */
	NULL,                 // entity
	rndr_normal_text,     // normal text

	/* renderer data */
	64, // max stack
	"*_",
	NULL // opaque
};

namespace Bypass {

	const static std::string TWO_SPACES = "  ";
	const static std::string NEWLINE = "\n";

	Parser::Parser()
	: elementSoup()
	{
		elementCount = 1;
	}

	Parser::~Parser() {

	}

	Document Parser::parse(const char* mkd) {
		document = Document();

		if (mkd) {
			struct buf *ib, *ob;

			ib = bufnew(INPUT_UNIT);
			bufputs(ib, mkd);

			ob = bufnew(OUTPUT_UNIT);

			mkd_callbacks.opaque = this;

			//parse and assemble document
			markdown(ob, ib, &mkd_callbacks);

			for (std::map<int, Element>::iterator it = elementSoup.begin(); it != elementSoup.end(); ++it) {
				document.append(it->second);
			}

			bufrelease(ib);
			bufrelease(ob);
		}

		return document;
	}

	Document Parser::parse(const string& markdown) {
		return parse(markdown.c_str());
	}

	void Parser::eraseTrailingControlCharacters(const std::string& controlCharacters) {
		std::map<int, Element>::iterator it = elementSoup.find(elementCount);

		if ( it != elementSoup.end() ) {
			Element * element = &((*it).second);

			if (boost::ends_with(element->text, controlCharacters)) {
				boost::erase_tail(element->text, controlCharacters.size());
			}
		}
	}

	// Block Element Callbacks

	void Parser::handleBlock(Type type, struct buf *ob, struct buf *text, int extra) {
		Element block;
		block.setType(type);

		if (type == HEADER) {
			char levelStr[2];
			snprintf(levelStr, 2, "%d", extra);
			block.addAttribute("level", levelStr);
		}

		std::string textString(text->data, text->data + text->size);
		std::vector<std::string> strs;
		boost::split(strs, textString, boost::is_any_of("|"));

		for(vector<std::string>::iterator it = strs.begin(); it != strs.end(); it++) {
			int pos = atoi((*it).c_str());
			std::map<int, Element>::iterator elit = elementSoup.find(pos);

			if ( elit != elementSoup.end() ) {
				block.append((*elit).second);
				elementSoup.erase(pos);
			}
		}

		elementCount++;

		std::ostringstream oss;
		oss << elementCount;
		elementSoup[elementCount] = block;
		oss << '|';
		bufputs(ob, oss.str().c_str());
	}

	void Parser::parsedBlockCode(struct buf *ob, struct buf *text) {
		if(!text) return; // Analyze seems to believe that text can be null here
		parsedNormalText(ob, text);
		eraseTrailingControlCharacters(NEWLINE);

		std::ostringstream oss;
		oss << elementCount << '|';
		bufreset(text);
		bufputs(text, oss.str().c_str());
		handleBlock(BLOCK_CODE, ob, text);
	}

	void Parser::parsedBlockQuote(struct buf *ob, struct buf *text) {
		handleBlock(BLOCK_QUOTE, ob, text);
	}

	void Parser::parsedHeader(struct buf *ob, struct buf *text, int level) {
		handleBlock(HEADER, ob, text, level);
	}

	void Parser::parsedList(struct buf *ob, struct buf *text, int flags) {
		handleBlock(LIST, ob, text);
	}

	void Parser::parsedListItem(struct buf *ob, struct buf *text, int flags) {
		handleBlock(LIST_ITEM, ob, text);
	}

	void Parser::parsedParagraph(struct buf *ob, struct buf *text) {
		handleBlock(PARAGRAPH, ob, text);
	}

	// Span Element Callbacks

	void Parser::handleSpan(Type type, struct buf *ob, struct buf *text, struct buf *extra, struct buf *extra2) {

		std::vector<std::string> strs;
		std::string textString;
		if (text) {
			textString = std::string(text->data, text->data + text->size);
			boost::split(strs, textString, boost::is_any_of("|"));
		}
		if (strs.size() > 0) {
			int pos = atoi(strs[0].c_str());
			std::map<int, Element>::iterator elit = elementSoup.find(pos);

			Element element = elit->second;
			element.setType(type);

			if (extra != NULL && extra->size) {
				if (element.getType() == LINK) {
					element.addAttribute("link", std::string(extra->data, extra->data + extra->size));
				}
			}

			if (extra2 != NULL && extra2->size) {
				if (element.getType() == LINK) {
					element.addAttribute("title", std::string(extra2->data, extra2->data + extra2->size));
				}
			}

			elementSoup.erase(pos);
			elementSoup[pos] = element;

			bufputs(ob, textString.c_str());
		}
		else {
			Element element;
			element.setType(type);

			createSpan(element, ob);
		}
	}

	void Parser::createSpan(const Element& element, struct buf *ob) {
		elementCount++;
		std::ostringstream oss;
		oss << elementCount;
		elementSoup[elementCount] = element;
		oss << '|';
		bufputs(ob, oss.str().c_str());
	}

	int Parser::parsedDoubleEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(DOUBLE_EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedTripleEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(TRIPLE_EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedLink(struct buf *ob, struct buf *link, struct buf *title, struct buf *content) {
		handleSpan(LINK, ob, content, link, title);
		return 1;
	}

	int Parser::parsedCodeSpan(struct buf *ob, struct buf *text) {
		if (text && text->size > 0) {
			Element codeSpan;
			codeSpan.setType(CODE_SPAN);
			codeSpan.text.assign(text->data, text->data + text->size);
			createSpan(codeSpan, ob);
		}
		return 1;
	}

	int Parser::parsedLinebreak(struct buf *ob) {
		eraseTrailingControlCharacters(TWO_SPACES);
		handleSpan(LINEBREAK, ob, NULL);
		return 1;
	}

	// Low Level Callbacks

	void Parser::parsedNormalText(struct buf *ob, struct buf *text) {
		// The parser will spuriously emit a text callback for an empty string
		// that butts up against a span-level element. This will ignore it.

		if (text && text->size > 0) {
			Element normalText;
			normalText.setType(TEXT);
			normalText.text.assign(text->data, text->data + text->size);
			createSpan(normalText, ob);
		}
	}

}

// Block Element callbacks

static void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedBlockCode(ob, text);
}

static void rndr_blockquote(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedBlockQuote(ob, text);
}

static void rndr_header(struct buf *ob, struct buf *text, int level, void *opaque) {
	((Bypass::Parser*) opaque)->parsedHeader(ob, text, level);
}

static void rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque) {
	((Bypass::Parser*) opaque)->parsedList(ob, text, flags);
}

static void rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) {
	((Bypass::Parser*) opaque)->parsedListItem(ob, text, flags);
}

static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedParagraph(ob, text);
}

// Span Element callbacks

static int rndr_codespan(struct buf *ob, struct buf *text, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedCodeSpan(ob, text);
}

static int rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedDoubleEmphasis(ob, text, c);
}

static int rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedEmphasis(ob, text, c);
}

static int rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedTripleEmphasis(ob, text, c);
}

static int rndr_linebreak(struct buf *ob, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedLinebreak(ob);
}

static int rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedLink(ob, link, title, content);
}

//	Low Level Callbacks

static void rndr_normal_text(struct buf *ob, struct buf *text, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedNormalText(ob, text);
}

<commit_msg>Workaround for potential crash of links without URL and title, e.g. [](/sometext)<commit_after>//
//  Copyright 2013 Uncodin, Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

#include "parser.h"

using namespace std;

static void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque);
static void rndr_blockquote(struct buf *ob, struct buf *text, void *opaque);
static void rndr_header(struct buf *ob, struct buf *text, int level, void *opaque);
static void rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque);
static void rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque);
static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque);
static int rndr_codespan(struct buf *ob, struct buf *text, void *opaque);
static int rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_linebreak(struct buf *ob, void *opaque);
static int rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque);
static void rndr_normal_text(struct buf *ob, struct buf *text, void *opaque);

struct mkd_renderer mkd_callbacks = {
	/* document-level callbacks */
	NULL,                 // prolog
	NULL,                 // epilogue

	/* block-level callbacks */
	rndr_blockcode,       // block code
	rndr_blockquote,      // block quote
	NULL,                 // block html
	rndr_header,          // header
	NULL,                 // hrule
	rndr_list,            // list
	rndr_listitem,        // listitem
	rndr_paragraph,       // paragraph
	NULL,                 // table
	NULL,                 // table cell
	NULL,                 // table row

	/* span-level callbacks */
	NULL,                 // autolink
	rndr_codespan,        // codespan
	rndr_double_emphasis, // double emphasis
	rndr_emphasis,        // emphasis
	NULL,                 // image
	rndr_linebreak,       // line break
	rndr_link,            // link
	NULL,                 // raw html tag
	rndr_triple_emphasis, // triple emphasis

	/* low-level callbacks */
	NULL,                 // entity
	rndr_normal_text,     // normal text

	/* renderer data */
	64, // max stack
	"*_",
	NULL // opaque
};

namespace Bypass {

	const static std::string TWO_SPACES = "  ";
	const static std::string NEWLINE = "\n";

	Parser::Parser()
	: elementSoup()
	{
		elementCount = 1;
	}

	Parser::~Parser() {

	}

	Document Parser::parse(const char* mkd) {
		document = Document();

		if (mkd) {
			struct buf *ib, *ob;

			ib = bufnew(INPUT_UNIT);
			bufputs(ib, mkd);

			ob = bufnew(OUTPUT_UNIT);

			mkd_callbacks.opaque = this;

			//parse and assemble document
			markdown(ob, ib, &mkd_callbacks);

			for (std::map<int, Element>::iterator it = elementSoup.begin(); it != elementSoup.end(); ++it) {
				document.append(it->second);
			}

			bufrelease(ib);
			bufrelease(ob);
		}

		return document;
	}

	Document Parser::parse(const string& markdown) {
		return parse(markdown.c_str());
	}

	void Parser::eraseTrailingControlCharacters(const std::string& controlCharacters) {
		std::map<int, Element>::iterator it = elementSoup.find(elementCount);

		if ( it != elementSoup.end() ) {
			Element * element = &((*it).second);

			if (boost::ends_with(element->text, controlCharacters)) {
				boost::erase_tail(element->text, controlCharacters.size());
			}
		}
	}

	// Block Element Callbacks

	void Parser::handleBlock(Type type, struct buf *ob, struct buf *text, int extra) {
		Element block;
		block.setType(type);

		if (type == HEADER) {
			char levelStr[2];
			snprintf(levelStr, 2, "%d", extra);
			block.addAttribute("level", levelStr);
		}

		std::string textString(text->data, text->data + text->size);
		std::vector<std::string> strs;
		boost::split(strs, textString, boost::is_any_of("|"));

		for(vector<std::string>::iterator it = strs.begin(); it != strs.end(); it++) {
			int pos = atoi((*it).c_str());
			std::map<int, Element>::iterator elit = elementSoup.find(pos);

			if ( elit != elementSoup.end() ) {
				block.append((*elit).second);
				elementSoup.erase(pos);
			}
		}

		elementCount++;

		std::ostringstream oss;
		oss << elementCount;
		elementSoup[elementCount] = block;
		oss << '|';
		bufputs(ob, oss.str().c_str());
	}

	void Parser::parsedBlockCode(struct buf *ob, struct buf *text) {
		if(!text) return; // Analyze seems to believe that text can be null here
		parsedNormalText(ob, text);
		eraseTrailingControlCharacters(NEWLINE);

		std::ostringstream oss;
		oss << elementCount << '|';
		bufreset(text);
		bufputs(text, oss.str().c_str());
		handleBlock(BLOCK_CODE, ob, text);
	}

	void Parser::parsedBlockQuote(struct buf *ob, struct buf *text) {
		handleBlock(BLOCK_QUOTE, ob, text);
	}

	void Parser::parsedHeader(struct buf *ob, struct buf *text, int level) {
		handleBlock(HEADER, ob, text, level);
	}

	void Parser::parsedList(struct buf *ob, struct buf *text, int flags) {
		handleBlock(LIST, ob, text);
	}

	void Parser::parsedListItem(struct buf *ob, struct buf *text, int flags) {
		handleBlock(LIST_ITEM, ob, text);
	}

	void Parser::parsedParagraph(struct buf *ob, struct buf *text) {
		handleBlock(PARAGRAPH, ob, text);
	}

	// Span Element Callbacks

	void Parser::handleSpan(Type type, struct buf *ob, struct buf *text, struct buf *extra, struct buf *extra2) {

		std::vector<std::string> strs;
		std::string textString;
		if (text) {
			textString = std::string(text->data, text->data + text->size);
			boost::split(strs, textString, boost::is_any_of("|"));
		}
		if (strs.size() > 0) {
            std::string str0 = strs[0];

            if (str0.length() > 0) {
                int pos = atoi(str0.c_str());
                std::map<int, Element>::iterator elit = elementSoup.find(pos);

                Element element = elit->second;
                element.setType(type);

                if (extra != NULL && extra->size) {
                    if (element.getType() == LINK) {
                        element.addAttribute("link", std::string(extra->data, extra->data + extra->size));
                    }
                }

                if (extra2 != NULL && extra2->size) {
                    if (element.getType() == LINK) {
                        element.addAttribute("title", std::string(extra2->data, extra2->data + extra2->size));
                    }
                }

                elementSoup.erase(pos);
                elementSoup[pos] = element;
            }

			bufputs(ob, textString.c_str());
		}
		else {
			Element element;
			element.setType(type);

			createSpan(element, ob);
		}
	}

	void Parser::createSpan(const Element& element, struct buf *ob) {
		elementCount++;
		std::ostringstream oss;
		oss << elementCount;
		elementSoup[elementCount] = element;
		oss << '|';
		bufputs(ob, oss.str().c_str());
	}

	int Parser::parsedDoubleEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(DOUBLE_EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedTripleEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(TRIPLE_EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedLink(struct buf *ob, struct buf *link, struct buf *title, struct buf *content) {
		handleSpan(LINK, ob, content, link, title);
		return 1;
	}

	int Parser::parsedCodeSpan(struct buf *ob, struct buf *text) {
		if (text && text->size > 0) {
			Element codeSpan;
			codeSpan.setType(CODE_SPAN);
			codeSpan.text.assign(text->data, text->data + text->size);
			createSpan(codeSpan, ob);
		}
		return 1;
	}

	int Parser::parsedLinebreak(struct buf *ob) {
		eraseTrailingControlCharacters(TWO_SPACES);
		handleSpan(LINEBREAK, ob, NULL);
		return 1;
	}

	// Low Level Callbacks

	void Parser::parsedNormalText(struct buf *ob, struct buf *text) {
		// The parser will spuriously emit a text callback for an empty string
		// that butts up against a span-level element. This will ignore it.

		if (text && text->size > 0) {
			Element normalText;
			normalText.setType(TEXT);
			normalText.text.assign(text->data, text->data + text->size);
			createSpan(normalText, ob);
		}
	}

}

// Block Element callbacks

static void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedBlockCode(ob, text);
}

static void rndr_blockquote(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedBlockQuote(ob, text);
}

static void rndr_header(struct buf *ob, struct buf *text, int level, void *opaque) {
	((Bypass::Parser*) opaque)->parsedHeader(ob, text, level);
}

static void rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque) {
	((Bypass::Parser*) opaque)->parsedList(ob, text, flags);
}

static void rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) {
	((Bypass::Parser*) opaque)->parsedListItem(ob, text, flags);
}

static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedParagraph(ob, text);
}

// Span Element callbacks

static int rndr_codespan(struct buf *ob, struct buf *text, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedCodeSpan(ob, text);
}

static int rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedDoubleEmphasis(ob, text, c);
}

static int rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedEmphasis(ob, text, c);
}

static int rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedTripleEmphasis(ob, text, c);
}

static int rndr_linebreak(struct buf *ob, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedLinebreak(ob);
}

static int rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedLink(ob, link, title, content);
}

//	Low Level Callbacks

static void rndr_normal_text(struct buf *ob, struct buf *text, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedNormalText(ob, text);
}

<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtest.h>
#include <QtTest/QSignalSpy>
#include <QtDeclarative/qdeclarativecomponent.h>
#include <QtDeclarative/qdeclarativecontext.h>
#include <QtDeclarative/qsgview.h>
#include <QtDeclarative/qsgitem.h>
#include "../../../shared/util.h"

#ifdef Q_OS_SYMBIAN
// In Symbian OS test data is located in applications private dir
#define SRCDIR "."
#endif

class tst_QSGView : public QObject

{
    Q_OBJECT
public:
    tst_QSGView();

private slots:
    void resizemodeitem();
    void errors();
};

tst_QSGView::tst_QSGView()
{
}

void tst_QSGView::resizemodeitem()
{
    QWidget window;
    QSGView *canvas = new QSGView(&window);
    QVERIFY(canvas);
    canvas->setResizeMode(QSGView::SizeRootObjectToView);
    QCOMPARE(QSize(0,0), canvas->initialSize());
    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/resizemodeitem.qml"));
    QSGItem* item = qobject_cast<QSGItem*>(canvas->rootObject());
    QVERIFY(item);
    window.show();

    // initial size from root object
    QCOMPARE(item->width(), 200.0);
    QCOMPARE(item->height(), 200.0);
    QCOMPARE(canvas->size(), QSize(200, 200));
    QCOMPARE(canvas->size(), canvas->sizeHint());
    QCOMPARE(canvas->size(), canvas->initialSize());

    // size update from view
    canvas->resize(QSize(80,100));
    QCOMPARE(item->width(), 80.0);
    QCOMPARE(item->height(), 100.0);
    QCOMPARE(canvas->size(), QSize(80, 100));
    QCOMPARE(canvas->size(), canvas->sizeHint());

    canvas->setResizeMode(QSGView::SizeViewToRootObject);

    // size update from view disabled
    canvas->resize(QSize(60,80));
    QCOMPARE(item->width(), 80.0);
    QCOMPARE(item->height(), 100.0);
    QCOMPARE(canvas->size(), QSize(60, 80));

    // size update from root object
    item->setWidth(250);
    item->setHeight(350);
    QCOMPARE(item->width(), 250.0);
    QCOMPARE(item->height(), 350.0);
    QTRY_COMPARE(canvas->size(), QSize(250, 350));
    QCOMPARE(canvas->size(), QSize(250, 350));
    QCOMPARE(canvas->size(), canvas->sizeHint());

    // reset canvas
    window.hide();
    delete canvas;
    canvas = new QSGView(&window);
    QVERIFY(canvas);
    canvas->setResizeMode(QSGView::SizeViewToRootObject);
    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/resizemodeitem.qml"));
    item = qobject_cast<QSGItem*>(canvas->rootObject());
    QVERIFY(item);
    window.show();

    // initial size for root object
    QCOMPARE(item->width(), 200.0);
    QCOMPARE(item->height(), 200.0);
    QCOMPARE(canvas->size(), canvas->sizeHint());
    QCOMPARE(canvas->size(), canvas->initialSize());

    // size update from root object
    item->setWidth(80);
    item->setHeight(100);
    QCOMPARE(item->width(), 80.0);
    QCOMPARE(item->height(), 100.0);
    QTRY_COMPARE(canvas->size(), QSize(80, 100));
    QCOMPARE(canvas->size(), QSize(80, 100));
    QCOMPARE(canvas->size(), canvas->sizeHint());

    // size update from root object disabled
    canvas->setResizeMode(QSGView::SizeRootObjectToView);
    item->setWidth(60);
    item->setHeight(80);
    QCOMPARE(canvas->width(), 80);
    QCOMPARE(canvas->height(), 100);
    QCOMPARE(QSize(item->width(), item->height()), canvas->sizeHint());

    // size update from view
    canvas->resize(QSize(200,300));
    QCOMPARE(item->width(), 200.0);
    QCOMPARE(item->height(), 300.0);
    QCOMPARE(canvas->size(), QSize(200, 300));
    QCOMPARE(canvas->size(), canvas->sizeHint());

    delete canvas;

    // if we set a specific size for the view then it should keep that size
    // for SizeRootObjectToView mode.
    canvas = new QSGView(&window);
    canvas->resize(300, 300);
    canvas->setResizeMode(QSGView::SizeRootObjectToView);
    QCOMPARE(QSize(0,0), canvas->initialSize());
    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/resizemodeitem.qml"));
    item = qobject_cast<QSGItem*>(canvas->rootObject());
    QVERIFY(item);
    window.show();

    // initial size from root object
    QCOMPARE(item->width(), 300.0);
    QCOMPARE(item->height(), 300.0);
    QCOMPARE(canvas->size(), QSize(300, 300));
    QCOMPARE(canvas->size(), canvas->sizeHint());
    QCOMPARE(canvas->initialSize(), QSize(200, 200)); // initial object size

    delete canvas;
}

static void silentErrorsMsgHandler(QtMsgType, const char *)
{
}

void tst_QSGView::errors()
{
    QSGView *canvas = new QSGView;
    QVERIFY(canvas);
    QtMsgHandler old = qInstallMsgHandler(silentErrorsMsgHandler);
    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/error1.qml"));
    qInstallMsgHandler(old);
    QVERIFY(canvas->status() == QSGView::Error);
    QVERIFY(canvas->errors().count() == 1);
    delete canvas;
}

QTEST_MAIN(tst_QSGView)

#include "tst_qsgview.moc"
<commit_msg>fixed resizemodelitem<commit_after>/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtest.h>
#include <QtTest/QSignalSpy>
#include <QtDeclarative/qdeclarativecomponent.h>
#include <QtDeclarative/qdeclarativecontext.h>
#include <QtDeclarative/qsgview.h>
#include <QtDeclarative/qsgitem.h>
#include "../../../shared/util.h"
#include <QtGui/QWindow>
#include <QtCore/QDebug>
#ifdef Q_OS_SYMBIAN
// In Symbian OS test data is located in applications private dir
#define SRCDIR "."
#endif

class tst_QSGView : public QObject

{
    Q_OBJECT
public:
    tst_QSGView();

private slots:
    void resizemodeitem();
    void errors();
};

tst_QSGView::tst_QSGView()
{
}

void tst_QSGView::resizemodeitem()
{
    QWindow window;
    window.setGeometry(0, 0, 400, 400);

    QSGView *canvas = new QSGView(&window);
    QVERIFY(canvas);
    canvas->setResizeMode(QSGView::SizeRootObjectToView);
    QCOMPARE(QSize(0,0), canvas->initialSize());
    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/resizemodeitem.qml"));
    QSGItem* item = qobject_cast<QSGItem*>(canvas->rootObject());
    QVERIFY(item);
    window.show();

    canvas->show();

    // initial size from root object
    QCOMPARE(item->width(), 200.0);
    QCOMPARE(item->height(), 200.0);
    QCOMPARE(canvas->size(), QSize(200, 200));
    QCOMPARE(canvas->size(), canvas->sizeHint());
    QCOMPARE(canvas->size(), canvas->initialSize());

    qDebug() << window.size();
    qDebug() << "canvas size:" << canvas->size();
    // size update from view
    canvas->resize(QSize(80,100));
    QTest::qWait(50);
    qDebug() << window.size();
    qDebug() << "canvas size:" << canvas->size();

    QCOMPARE(item->width(), 80.0);
    QCOMPARE(item->height(), 100.0);
    QCOMPARE(canvas->size(), QSize(80, 100));
    QCOMPARE(canvas->size(), canvas->sizeHint());

    canvas->setResizeMode(QSGView::SizeViewToRootObject);

    // size update from view disabled
    canvas->resize(QSize(60,80));
    QCOMPARE(item->width(), 80.0);
    QCOMPARE(item->height(), 100.0);
    QTest::qWait(50);
    QCOMPARE(canvas->size(), QSize(60, 80));

    // size update from root object
    item->setWidth(250);
    item->setHeight(350);
    QCOMPARE(item->width(), 250.0);
    QCOMPARE(item->height(), 350.0);
    QTRY_COMPARE(canvas->size(), QSize(250, 350));
    QCOMPARE(canvas->size(), QSize(250, 350));
    QCOMPARE(canvas->size(), canvas->sizeHint());

    // reset canvas
    window.hide();
    delete canvas;
    canvas = new QSGView(&window);
    QVERIFY(canvas);
    canvas->setResizeMode(QSGView::SizeViewToRootObject);
    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/resizemodeitem.qml"));
    item = qobject_cast<QSGItem*>(canvas->rootObject());
    QVERIFY(item);
    window.show();

    canvas->show();

    // initial size for root object
    QCOMPARE(item->width(), 200.0);
    QCOMPARE(item->height(), 200.0);
    QCOMPARE(canvas->size(), canvas->sizeHint());
    QCOMPARE(canvas->size(), canvas->initialSize());

    // size update from root object
    item->setWidth(80);
    item->setHeight(100);
    QCOMPARE(item->width(), 80.0);
    QCOMPARE(item->height(), 100.0);
    QTRY_COMPARE(canvas->size(), QSize(80, 100));
    QCOMPARE(canvas->size(), QSize(80, 100));
    QCOMPARE(canvas->size(), canvas->sizeHint());

    // size update from root object disabled
    canvas->setResizeMode(QSGView::SizeRootObjectToView);
    item->setWidth(60);
    item->setHeight(80);
    QCOMPARE(canvas->width(), 80);
    QCOMPARE(canvas->height(), 100);
    QCOMPARE(QSize(item->width(), item->height()), canvas->sizeHint());

    // size update from view
    canvas->resize(QSize(200,300));
    QTest::qWait(50);
    QCOMPARE(item->width(), 200.0);
    QCOMPARE(item->height(), 300.0);
    QCOMPARE(canvas->size(), QSize(200, 300));
    QCOMPARE(canvas->size(), canvas->sizeHint());

    window.hide();
    delete canvas;

    // if we set a specific size for the view then it should keep that size
    // for SizeRootObjectToView mode.
    canvas = new QSGView(&window);
    canvas->resize(300, 300);
    canvas->setResizeMode(QSGView::SizeRootObjectToView);
    QCOMPARE(QSize(0,0), canvas->initialSize());
    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/resizemodeitem.qml"));
    canvas->resize(300, 300);
    item = qobject_cast<QSGItem*>(canvas->rootObject());
    QVERIFY(item);
    window.show();

    canvas->show();
    QTest::qWait(50);

    // initial size from root object
    QCOMPARE(item->width(), 300.0);
    QCOMPARE(item->height(), 300.0);
    QCOMPARE(canvas->size(), QSize(300, 300));
    QCOMPARE(canvas->size(), canvas->sizeHint());
    QCOMPARE(canvas->initialSize(), QSize(200, 200)); // initial object size

    delete canvas;
}

static void silentErrorsMsgHandler(QtMsgType, const char *)
{
}

void tst_QSGView::errors()
{
    QSGView *canvas = new QSGView;
    QVERIFY(canvas);
    QtMsgHandler old = qInstallMsgHandler(silentErrorsMsgHandler);
    canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/error1.qml"));
    qInstallMsgHandler(old);
    QVERIFY(canvas->status() == QSGView::Error);
    QVERIFY(canvas->errors().count() == 1);
    delete canvas;
}

QTEST_MAIN(tst_QSGView)

#include "tst_qsgview.moc"
<|endoftext|>