<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#ifndef STATS_HH
#define STATS_HH 1

#include <map>
#include <memcached/engine.h>

#include "common.hh"
#include "atomic.hh"
#include "histo.hh"

#ifndef DEFAULT_MAX_DATA_SIZE
/* Something something something ought to be enough for anybody */
#define DEFAULT_MAX_DATA_SIZE (std::numeric_limits<size_t>::max())
#endif

static const hrtime_t ONE_SECOND(1000000);

/**
 * Global engine stats container.
 */
class EPStats {
public:

    EPStats() : maxDataSize(DEFAULT_MAX_DATA_SIZE),
                dirtyAgeHisto(GrowingWidthGenerator<hrtime_t>(0, ONE_SECOND, 1.4), 25),
                dataAgeHisto(GrowingWidthGenerator<hrtime_t>(0, ONE_SECOND, 1.4), 25),
                diskCommitHisto(GrowingWidthGenerator<hrtime_t>(0, ONE_SECOND, 1.4), 25) {}

    //! How long it took us to load the data from disk.
    Atomic<hrtime_t> warmupTime;
    //! Whether we're warming up.
    Atomic<bool> warmupComplete;
    //! Number of records warmed up.
    Atomic<size_t> warmedUp;
    //! Number of warmup failures due to duplicates
    Atomic<size_t> warmDups;
    //! Number of OOM failures at warmup time.
    Atomic<size_t> warmOOM;
    //! The tap throttle write queue cap
    Atomic<size_t> tapThrottleWriteQueueCap;

    //! size of the input queue
    Atomic<size_t> queue_size;
    //! Size of the in-process (output) queue.
    Atomic<size_t> flusher_todo;
    //! Number of transaction commits.
    Atomic<size_t> flusherCommits;
    //! Number of times the flusher was preempted for a read
    Atomic<size_t> flusherPreempts;
    //! Total time spent flushing.
    Atomic<size_t> cumulativeFlushTime;
    //! Total time spent committing.
    Atomic<size_t> cumulativeCommitTime;
    //! Objects that were rejected from persistence for being too fresh.
    Atomic<size_t> tooYoung;
    //! Objects that were forced into persistence for being too old.
    Atomic<size_t> tooOld;
    //! Number of items persisted.
    Atomic<size_t> totalPersisted;
    //! Cumulative number of items added to the queue.
    Atomic<size_t> totalEnqueued;
    //! Number of new items created in the DB.
    Atomic<size_t> newItems;
    //! Number of items removed from the DB.
    Atomic<size_t> delItems;
    //! Number of times an item flush failed.
    Atomic<size_t> flushFailed;
    //! Number of times an item is not flushed due to the item's expiry
    Atomic<size_t> flushExpired;
    //! Number of times an object was expired on access.
    Atomic<size_t> expired;
    //! Number of times we failed to start a transaction
    Atomic<size_t> beginFailed;
    //! Number of times a commit failed.
    Atomic<size_t> commitFailed;
    //! How long an object is dirty before written.
    Atomic<rel_time_t> dirtyAge;
    //! Oldest enqueued object we've seen while persisting.
    Atomic<rel_time_t> dirtyAgeHighWat;
    //! How old persisted data was when it hit the persistence layer
    Atomic<rel_time_t> dataAge;
    //! Oldest data we've seen while persisting.
    Atomic<rel_time_t> dataAgeHighWat;
    //! How long does it take to do an entire flush cycle.
    Atomic<rel_time_t> flushDuration;
    //! Longest flush cycle we've seen.
    Atomic<rel_time_t> flushDurationHighWat;
    //! Amount of time spent in the commit phase.
    Atomic<rel_time_t> commit_time;
    //! Number of times we deleted a vbucket.
    Atomic<size_t> vbucketDeletions;
    //! Number of times we failed to delete a vbucket.
    Atomic<size_t> vbucketDeletionFail;
    //! Beyond this point are config items
    //! Minimum data age before a record can be persisted
    Atomic<int> min_data_age;
    //! Maximum data age before a record is forced to be persisted
    Atomic<int> queue_age_cap;
    //! Number of times background fetches occurred.
    Atomic<size_t> bg_fetched;
    //! Number of times we needed to kick in the pager
    Atomic<size_t> pagerRuns;
    //! Number of times the expiry pager runs for purging expired items
    Atomic<size_t> expiryPagerRuns;
    //! Number of times the checkpoint remover runs for removing closed unreferenced checkpoints.
    Atomic<size_t> checkpointRemoverRuns;
    //! Number of items removed from closed unreferenced checkpoints.
    Atomic<size_t> itemsRemovedFromCheckpoints;
    //! Number of times a value is ejected
    Atomic<size_t> numValueEjects;
    //! Number of times a replica value is ejected
    Atomic<size_t> numReplicaEjects;
    //! Number of times a value could not be ejected
    Atomic<size_t> numFailedEjects;
    //! Number of times "Not my bucket" happened
    Atomic<size_t> numNotMyVBuckets;
    //! Whether the DB cleaner completes cleaning up invalid items with old vb versions
    Atomic<bool> dbCleanerComplete;
    //! Number of deleted items reverted from hot reload
    Atomic<size_t> numRevertDeletes;
    //! Number of new items reverted from hot reload
    Atomic<size_t> numRevertAdds;
    //! Number of updated items reverted from hot reload
    Atomic<size_t> numRevertUpdates;
    //! Max allowable memory size.
    Atomic<size_t> maxDataSize;
    //! Total size of stored objects.
    Atomic<size_t> currentSize;
    //! Total memory overhead to store values for resident keys.
    Atomic<size_t> totalValueSize;
    //! Amount of memory used to track items and what-not.
    Atomic<size_t> memOverhead;

    //! Pager low water mark.
    Atomic<size_t> mem_low_wat;
    //! Pager high water mark
    Atomic<size_t> mem_high_wat;

    //! Number of times unrecoverable oom errors happened while processing operations.
    Atomic<size_t> oom_errors;
    //! Number of times temporary oom errors encountered while processing operations.
    Atomic<size_t> tmp_oom_errors;

    //! Number of read related io operations
    Atomic<size_t> io_num_read;
    //! Number of write related io operations
    Atomic<size_t> io_num_write;
    //! Number of bytes read
    Atomic<size_t> io_read_bytes;
    //! Number of bytes written
    Atomic<size_t> io_write_bytes;

    //! Number of ops blocked on all vbuckets in pending state
    Atomic<size_t> pendingOps;
    //! Total number of ops ever blocked on all vbuckets in pending state
    Atomic<size_t> pendingOpsTotal;
    //! High water value for ops blocked for any individual pending vbucket
    Atomic<size_t> pendingOpsMax;
    //! High water value for time an op is blocked on a pending vbucket
    Atomic<hrtime_t> pendingOpsMaxDuration;

    //! Histogram of pending operation wait times.
    Histogram<hrtime_t> pendingOpsHisto;

    //! The number of samples the bgWaitDelta and bgLoadDelta contains of
    Atomic<size_t> bgNumOperations;
    /** The sum of the deltas (in usec) from an item was put in queue until
     *  the dispatcher started the work for this item
     */
    Atomic<hrtime_t> bgWait;
    //! The shortest wait time
    Atomic<hrtime_t> bgMinWait;
    //! The longest wait time
    Atomic<hrtime_t> bgMaxWait;

    //! Histogram of background wait times.
    Histogram<hrtime_t> bgWaitHisto;

    /** The sum of the deltas (in usec) from the dispatcher started to load
     *  item until was done
     */
    Atomic<hrtime_t> bgLoad;
    //! The shortest load time
    Atomic<hrtime_t> bgMinLoad;
    //! The longest load time
    Atomic<hrtime_t> bgMaxLoad;

    //! Max wall time of deleting a vbucket
    Atomic<hrtime_t> vbucketDelMaxWalltime;
    //! Total wall time of deleting vbuckets
    Atomic<hrtime_t> vbucketDelTotWalltime;

    //! Histogram of background wait loads.
    Histogram<hrtime_t> bgLoadHisto;

    /* TAP related stats */
    //! The total number of tap events sent (not including noops)
    Atomic<size_t> numTapFetched;
    //! Number of background fetched tap items
    Atomic<size_t> numTapBGFetched;
    //! Number of times a tap background fetch task is requeued
    Atomic<size_t> numTapBGFetchRequeued;
    //! Number of foreground fetched tap items
    Atomic<size_t> numTapFGFetched;
    //! Number of tap deletes.
    Atomic<size_t> numTapDeletes;
    //! The number of samples the tapBgWaitDelta and tapBgLoadDelta contains of
    Atomic<size_t> tapBgNumOperations;
    //! The number of tap notify messages throttled by TapThrottle.
    Atomic<size_t> tapThrottled;
    //! Percentage of memory in use before we throttle tap input
    Atomic<double> tapThrottleThreshold;

    /** The sum of the deltas (in usec) from a tap item was put in queue until
     *  the dispatcher started the work for this item
     */
    Atomic<hrtime_t> tapBgWait;
    //! The shortest tap bg wait time
    Atomic<hrtime_t> tapBgMinWait;
    //! The longest tap bg wait time
    Atomic<hrtime_t> tapBgMaxWait;

    //! Histogram of tap background wait loads.
    Histogram<hrtime_t> tapBgWaitHisto;

    /** The sum of the deltas (in usec) from the dispatcher started to load
     *  a tap item until was done
     */
    Atomic<hrtime_t> tapBgLoad;
    //! The shortest tap load time
    Atomic<hrtime_t> tapBgMinLoad;
    //! The longest tap load time
    Atomic<hrtime_t> tapBgMaxLoad;

    //! Histogram of tap background wait loads.
    Histogram<hrtime_t> tapBgLoadHisto;

    //! Histogram of queue processing dirty age.
    Histogram<hrtime_t> dirtyAgeHisto;
    //! Histogram of queue processing data age.
    Histogram<hrtime_t> dataAgeHisto;

    //
    // Command timers
    //

    //! Histogram of getvbucket timings
    Histogram<hrtime_t> getVbucketCmdHisto;

    //! Histogram of setvbucket timings
    Histogram<hrtime_t> setVbucketCmdHisto;

    //! Histogram of delvbucket timings
    Histogram<hrtime_t> delVbucketCmdHisto;

    //! Histogram of get commands.
    Histogram<hrtime_t> getCmdHisto;

    //! Histogram of store commands.
    Histogram<hrtime_t> storeCmdHisto;

    //! Histogram of arithmetic commands.
    Histogram<hrtime_t> arithCmdHisto;

    //! Histogram of tap VBucket reset timings
    Histogram<hrtime_t> tapVbucketResetHisto;

    //! Histogram of tap mutation timings.
    Histogram<hrtime_t> tapMutationHisto;

    //! Histogram of tap vbucket set timings.
    Histogram<hrtime_t> tapVbucketSetHisto;

    //! Time spent notifying completion of IO.
    Histogram<hrtime_t> notifyIOHisto;

    //
    // DB timers.
    //

    //! Histogram of insert disk writes
    Histogram<hrtime_t> diskInsertHisto;

    //! Histogram of update disk writes
    Histogram<hrtime_t> diskUpdateHisto;

    //! Histogram of delete disk writes
    Histogram<hrtime_t> diskDelHisto;

    //! Histogram of execution time of disk vbucket chunk deletions
    Histogram<hrtime_t> diskVBChunkDelHisto;

    //! Histogram of execution time of disk vbucket deletions
    Histogram<hrtime_t> diskVBDelHisto;

    //! Histogram of execution time of invalid vbucket table deletions from disk
    Histogram<hrtime_t> diskInvalidVBTableDelHisto;

    //! Histogram of disk commits
    Histogram<hrtime_t> diskCommitHisto;

    //! Histogram of purging a chunk of items with the old vbucket version from disk
    Histogram<hrtime_t> diskInvaidItemDelHisto;

    Histogram<hrtime_t> checkpointRevertHisto;

    //! Reset all stats to reasonable values.
    void reset() {
        tooYoung.set(0);
        tooOld.set(0);
        dirtyAge.set(0);
        dirtyAgeHighWat.set(0);
        flushDuration.set(0);
        flushDurationHighWat.set(0);
        commit_time.set(0);
        pagerRuns.set(0);
        checkpointRemoverRuns.set(0);
        itemsRemovedFromCheckpoints.set(0);
        numValueEjects.set(0);
        numFailedEjects.set(0);
        io_num_read.set(0);
        io_num_write.set(0);
        io_read_bytes.set(0);
        io_write_bytes.set(0);
        bgNumOperations.set(0);
        bgWait.set(0);
        bgLoad.set(0);
        bgMinWait.set(999999999);
        bgMaxWait.set(0);
        bgMinLoad.set(999999999);
        bgMaxLoad.set(0);
        tapBgNumOperations.set(0);
        tapBgWait.set(0);
        tapBgLoad.set(0);
        tapBgMinWait.set(999999999);
        tapBgMaxWait.set(0);
        tapBgMinLoad.set(999999999);
        tapBgMaxLoad.set(0);
        tapThrottled.set(0);
        pendingOps.set(0);
        pendingOpsTotal.set(0);
        pendingOpsMax.set(0);
        pendingOpsMaxDuration.set(0);
        numTapFetched.set(0);
        vbucketDelMaxWalltime.set(0);
        vbucketDelTotWalltime.set(0);

        pendingOpsHisto.reset();
        bgWaitHisto.reset();
        bgLoadHisto.reset();
        tapBgWaitHisto.reset();
        tapBgLoadHisto.reset();
        getVbucketCmdHisto.reset();
        setVbucketCmdHisto.reset();
        delVbucketCmdHisto.reset();
        getCmdHisto.reset();
        storeCmdHisto.reset();
        arithCmdHisto.reset();
        tapVbucketResetHisto.reset();
        tapMutationHisto.reset();
        tapVbucketSetHisto.reset();
        notifyIOHisto.reset();
        diskInsertHisto.reset();
        diskUpdateHisto.reset();
        diskDelHisto.reset();
        diskVBChunkDelHisto.reset();
        diskVBDelHisto.reset();
        diskInvalidVBTableDelHisto.reset();
        diskCommitHisto.reset();
        diskInvaidItemDelHisto.reset();

        dataAgeHisto.reset();
        dirtyAgeHisto.reset();
    }

private:

    DISALLOW_COPY_AND_ASSIGN(EPStats);
};

/**
 * Stats returned by key stats.
 */
struct key_stats {
    //! The item's CAS
    uint64_t cas;
    //! The expiration time of the itme
    rel_time_t exptime;
    //! When the item was dirtied (if applicable).
    rel_time_t dirtied;
    //! How long the item has been dirty (if applicable).
    rel_time_t data_age;
    //! Last modification time
    rel_time_t last_modification_time;
    //! The item's current flags.
    uint32_t flags;
    //! True if this item is dirty.
    bool dirty;
};

/**
 * Stats returned by the underlying memory allocator.
 */
class MemoryAllocatorStats {
public:
    static void getAllocatorStats(std::map<std::string, size_t> &allocator_stats) {
#if defined(HAVE_LIBTCMALLOC) || defined(HAVE_LIBTCMALLOC_MINIMAL)
        TCMallocStats::getStats(allocator_stats);
#else
        (void) allocator_stats;
#endif
    }
};

#endif /* STATS_HH */
<commit_msg>MB-3852: stats reset should reset ep_num_not_my_vbucket<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#ifndef STATS_HH
#define STATS_HH 1

#include <map>
#include <memcached/engine.h>

#include "common.hh"
#include "atomic.hh"
#include "histo.hh"

#ifndef DEFAULT_MAX_DATA_SIZE
/* Something something something ought to be enough for anybody */
#define DEFAULT_MAX_DATA_SIZE (std::numeric_limits<size_t>::max())
#endif

static const hrtime_t ONE_SECOND(1000000);

/**
 * Global engine stats container.
 */
class EPStats {
public:

    EPStats() : maxDataSize(DEFAULT_MAX_DATA_SIZE),
                dirtyAgeHisto(GrowingWidthGenerator<hrtime_t>(0, ONE_SECOND, 1.4), 25),
                dataAgeHisto(GrowingWidthGenerator<hrtime_t>(0, ONE_SECOND, 1.4), 25),
                diskCommitHisto(GrowingWidthGenerator<hrtime_t>(0, ONE_SECOND, 1.4), 25) {}

    //! How long it took us to load the data from disk.
    Atomic<hrtime_t> warmupTime;
    //! Whether we're warming up.
    Atomic<bool> warmupComplete;
    //! Number of records warmed up.
    Atomic<size_t> warmedUp;
    //! Number of warmup failures due to duplicates
    Atomic<size_t> warmDups;
    //! Number of OOM failures at warmup time.
    Atomic<size_t> warmOOM;
    //! The tap throttle write queue cap
    Atomic<size_t> tapThrottleWriteQueueCap;

    //! size of the input queue
    Atomic<size_t> queue_size;
    //! Size of the in-process (output) queue.
    Atomic<size_t> flusher_todo;
    //! Number of transaction commits.
    Atomic<size_t> flusherCommits;
    //! Number of times the flusher was preempted for a read
    Atomic<size_t> flusherPreempts;
    //! Total time spent flushing.
    Atomic<size_t> cumulativeFlushTime;
    //! Total time spent committing.
    Atomic<size_t> cumulativeCommitTime;
    //! Objects that were rejected from persistence for being too fresh.
    Atomic<size_t> tooYoung;
    //! Objects that were forced into persistence for being too old.
    Atomic<size_t> tooOld;
    //! Number of items persisted.
    Atomic<size_t> totalPersisted;
    //! Cumulative number of items added to the queue.
    Atomic<size_t> totalEnqueued;
    //! Number of new items created in the DB.
    Atomic<size_t> newItems;
    //! Number of items removed from the DB.
    Atomic<size_t> delItems;
    //! Number of times an item flush failed.
    Atomic<size_t> flushFailed;
    //! Number of times an item is not flushed due to the item's expiry
    Atomic<size_t> flushExpired;
    //! Number of times an object was expired on access.
    Atomic<size_t> expired;
    //! Number of times we failed to start a transaction
    Atomic<size_t> beginFailed;
    //! Number of times a commit failed.
    Atomic<size_t> commitFailed;
    //! How long an object is dirty before written.
    Atomic<rel_time_t> dirtyAge;
    //! Oldest enqueued object we've seen while persisting.
    Atomic<rel_time_t> dirtyAgeHighWat;
    //! How old persisted data was when it hit the persistence layer
    Atomic<rel_time_t> dataAge;
    //! Oldest data we've seen while persisting.
    Atomic<rel_time_t> dataAgeHighWat;
    //! How long does it take to do an entire flush cycle.
    Atomic<rel_time_t> flushDuration;
    //! Longest flush cycle we've seen.
    Atomic<rel_time_t> flushDurationHighWat;
    //! Amount of time spent in the commit phase.
    Atomic<rel_time_t> commit_time;
    //! Number of times we deleted a vbucket.
    Atomic<size_t> vbucketDeletions;
    //! Number of times we failed to delete a vbucket.
    Atomic<size_t> vbucketDeletionFail;
    //! Beyond this point are config items
    //! Minimum data age before a record can be persisted
    Atomic<int> min_data_age;
    //! Maximum data age before a record is forced to be persisted
    Atomic<int> queue_age_cap;
    //! Number of times background fetches occurred.
    Atomic<size_t> bg_fetched;
    //! Number of times we needed to kick in the pager
    Atomic<size_t> pagerRuns;
    //! Number of times the expiry pager runs for purging expired items
    Atomic<size_t> expiryPagerRuns;
    //! Number of times the checkpoint remover runs for removing closed unreferenced checkpoints.
    Atomic<size_t> checkpointRemoverRuns;
    //! Number of items removed from closed unreferenced checkpoints.
    Atomic<size_t> itemsRemovedFromCheckpoints;
    //! Number of times a value is ejected
    Atomic<size_t> numValueEjects;
    //! Number of times a replica value is ejected
    Atomic<size_t> numReplicaEjects;
    //! Number of times a value could not be ejected
    Atomic<size_t> numFailedEjects;
    //! Number of times "Not my bucket" happened
    Atomic<size_t> numNotMyVBuckets;
    //! Whether the DB cleaner completes cleaning up invalid items with old vb versions
    Atomic<bool> dbCleanerComplete;
    //! Number of deleted items reverted from hot reload
    Atomic<size_t> numRevertDeletes;
    //! Number of new items reverted from hot reload
    Atomic<size_t> numRevertAdds;
    //! Number of updated items reverted from hot reload
    Atomic<size_t> numRevertUpdates;
    //! Max allowable memory size.
    Atomic<size_t> maxDataSize;
    //! Total size of stored objects.
    Atomic<size_t> currentSize;
    //! Total memory overhead to store values for resident keys.
    Atomic<size_t> totalValueSize;
    //! Amount of memory used to track items and what-not.
    Atomic<size_t> memOverhead;

    //! Pager low water mark.
    Atomic<size_t> mem_low_wat;
    //! Pager high water mark
    Atomic<size_t> mem_high_wat;

    //! Number of times unrecoverable oom errors happened while processing operations.
    Atomic<size_t> oom_errors;
    //! Number of times temporary oom errors encountered while processing operations.
    Atomic<size_t> tmp_oom_errors;

    //! Number of read related io operations
    Atomic<size_t> io_num_read;
    //! Number of write related io operations
    Atomic<size_t> io_num_write;
    //! Number of bytes read
    Atomic<size_t> io_read_bytes;
    //! Number of bytes written
    Atomic<size_t> io_write_bytes;

    //! Number of ops blocked on all vbuckets in pending state
    Atomic<size_t> pendingOps;
    //! Total number of ops ever blocked on all vbuckets in pending state
    Atomic<size_t> pendingOpsTotal;
    //! High water value for ops blocked for any individual pending vbucket
    Atomic<size_t> pendingOpsMax;
    //! High water value for time an op is blocked on a pending vbucket
    Atomic<hrtime_t> pendingOpsMaxDuration;

    //! Histogram of pending operation wait times.
    Histogram<hrtime_t> pendingOpsHisto;

    //! The number of samples the bgWaitDelta and bgLoadDelta contains of
    Atomic<size_t> bgNumOperations;
    /** The sum of the deltas (in usec) from an item was put in queue until
     *  the dispatcher started the work for this item
     */
    Atomic<hrtime_t> bgWait;
    //! The shortest wait time
    Atomic<hrtime_t> bgMinWait;
    //! The longest wait time
    Atomic<hrtime_t> bgMaxWait;

    //! Histogram of background wait times.
    Histogram<hrtime_t> bgWaitHisto;

    /** The sum of the deltas (in usec) from the dispatcher started to load
     *  item until was done
     */
    Atomic<hrtime_t> bgLoad;
    //! The shortest load time
    Atomic<hrtime_t> bgMinLoad;
    //! The longest load time
    Atomic<hrtime_t> bgMaxLoad;

    //! Max wall time of deleting a vbucket
    Atomic<hrtime_t> vbucketDelMaxWalltime;
    //! Total wall time of deleting vbuckets
    Atomic<hrtime_t> vbucketDelTotWalltime;

    //! Histogram of background wait loads.
    Histogram<hrtime_t> bgLoadHisto;

    /* TAP related stats */
    //! The total number of tap events sent (not including noops)
    Atomic<size_t> numTapFetched;
    //! Number of background fetched tap items
    Atomic<size_t> numTapBGFetched;
    //! Number of times a tap background fetch task is requeued
    Atomic<size_t> numTapBGFetchRequeued;
    //! Number of foreground fetched tap items
    Atomic<size_t> numTapFGFetched;
    //! Number of tap deletes.
    Atomic<size_t> numTapDeletes;
    //! The number of samples the tapBgWaitDelta and tapBgLoadDelta contains of
    Atomic<size_t> tapBgNumOperations;
    //! The number of tap notify messages throttled by TapThrottle.
    Atomic<size_t> tapThrottled;
    //! Percentage of memory in use before we throttle tap input
    Atomic<double> tapThrottleThreshold;

    /** The sum of the deltas (in usec) from a tap item was put in queue until
     *  the dispatcher started the work for this item
     */
    Atomic<hrtime_t> tapBgWait;
    //! The shortest tap bg wait time
    Atomic<hrtime_t> tapBgMinWait;
    //! The longest tap bg wait time
    Atomic<hrtime_t> tapBgMaxWait;

    //! Histogram of tap background wait loads.
    Histogram<hrtime_t> tapBgWaitHisto;

    /** The sum of the deltas (in usec) from the dispatcher started to load
     *  a tap item until was done
     */
    Atomic<hrtime_t> tapBgLoad;
    //! The shortest tap load time
    Atomic<hrtime_t> tapBgMinLoad;
    //! The longest tap load time
    Atomic<hrtime_t> tapBgMaxLoad;

    //! Histogram of tap background wait loads.
    Histogram<hrtime_t> tapBgLoadHisto;

    //! Histogram of queue processing dirty age.
    Histogram<hrtime_t> dirtyAgeHisto;
    //! Histogram of queue processing data age.
    Histogram<hrtime_t> dataAgeHisto;

    //
    // Command timers
    //

    //! Histogram of getvbucket timings
    Histogram<hrtime_t> getVbucketCmdHisto;

    //! Histogram of setvbucket timings
    Histogram<hrtime_t> setVbucketCmdHisto;

    //! Histogram of delvbucket timings
    Histogram<hrtime_t> delVbucketCmdHisto;

    //! Histogram of get commands.
    Histogram<hrtime_t> getCmdHisto;

    //! Histogram of store commands.
    Histogram<hrtime_t> storeCmdHisto;

    //! Histogram of arithmetic commands.
    Histogram<hrtime_t> arithCmdHisto;

    //! Histogram of tap VBucket reset timings
    Histogram<hrtime_t> tapVbucketResetHisto;

    //! Histogram of tap mutation timings.
    Histogram<hrtime_t> tapMutationHisto;

    //! Histogram of tap vbucket set timings.
    Histogram<hrtime_t> tapVbucketSetHisto;

    //! Time spent notifying completion of IO.
    Histogram<hrtime_t> notifyIOHisto;

    //
    // DB timers.
    //

    //! Histogram of insert disk writes
    Histogram<hrtime_t> diskInsertHisto;

    //! Histogram of update disk writes
    Histogram<hrtime_t> diskUpdateHisto;

    //! Histogram of delete disk writes
    Histogram<hrtime_t> diskDelHisto;

    //! Histogram of execution time of disk vbucket chunk deletions
    Histogram<hrtime_t> diskVBChunkDelHisto;

    //! Histogram of execution time of disk vbucket deletions
    Histogram<hrtime_t> diskVBDelHisto;

    //! Histogram of execution time of invalid vbucket table deletions from disk
    Histogram<hrtime_t> diskInvalidVBTableDelHisto;

    //! Histogram of disk commits
    Histogram<hrtime_t> diskCommitHisto;

    //! Histogram of purging a chunk of items with the old vbucket version from disk
    Histogram<hrtime_t> diskInvaidItemDelHisto;

    Histogram<hrtime_t> checkpointRevertHisto;

    //! Reset all stats to reasonable values.
    void reset() {
        tooYoung.set(0);
        tooOld.set(0);
        dirtyAge.set(0);
        dirtyAgeHighWat.set(0);
        flushDuration.set(0);
        flushDurationHighWat.set(0);
        commit_time.set(0);
        pagerRuns.set(0);
        checkpointRemoverRuns.set(0);
        itemsRemovedFromCheckpoints.set(0);
        numValueEjects.set(0);
        numFailedEjects.set(0);
        numNotMyVBuckets.set(0);
        io_num_read.set(0);
        io_num_write.set(0);
        io_read_bytes.set(0);
        io_write_bytes.set(0);
        bgNumOperations.set(0);
        bgWait.set(0);
        bgLoad.set(0);
        bgMinWait.set(999999999);
        bgMaxWait.set(0);
        bgMinLoad.set(999999999);
        bgMaxLoad.set(0);
        tapBgNumOperations.set(0);
        tapBgWait.set(0);
        tapBgLoad.set(0);
        tapBgMinWait.set(999999999);
        tapBgMaxWait.set(0);
        tapBgMinLoad.set(999999999);
        tapBgMaxLoad.set(0);
        tapThrottled.set(0);
        pendingOps.set(0);
        pendingOpsTotal.set(0);
        pendingOpsMax.set(0);
        pendingOpsMaxDuration.set(0);
        numTapFetched.set(0);
        vbucketDelMaxWalltime.set(0);
        vbucketDelTotWalltime.set(0);

        pendingOpsHisto.reset();
        bgWaitHisto.reset();
        bgLoadHisto.reset();
        tapBgWaitHisto.reset();
        tapBgLoadHisto.reset();
        getVbucketCmdHisto.reset();
        setVbucketCmdHisto.reset();
        delVbucketCmdHisto.reset();
        getCmdHisto.reset();
        storeCmdHisto.reset();
        arithCmdHisto.reset();
        tapVbucketResetHisto.reset();
        tapMutationHisto.reset();
        tapVbucketSetHisto.reset();
        notifyIOHisto.reset();
        diskInsertHisto.reset();
        diskUpdateHisto.reset();
        diskDelHisto.reset();
        diskVBChunkDelHisto.reset();
        diskVBDelHisto.reset();
        diskInvalidVBTableDelHisto.reset();
        diskCommitHisto.reset();
        diskInvaidItemDelHisto.reset();

        dataAgeHisto.reset();
        dirtyAgeHisto.reset();
    }

private:

    DISALLOW_COPY_AND_ASSIGN(EPStats);
};

/**
 * Stats returned by key stats.
 */
struct key_stats {
    //! The item's CAS
    uint64_t cas;
    //! The expiration time of the itme
    rel_time_t exptime;
    //! When the item was dirtied (if applicable).
    rel_time_t dirtied;
    //! How long the item has been dirty (if applicable).
    rel_time_t data_age;
    //! Last modification time
    rel_time_t last_modification_time;
    //! The item's current flags.
    uint32_t flags;
    //! True if this item is dirty.
    bool dirty;
};

/**
 * Stats returned by the underlying memory allocator.
 */
class MemoryAllocatorStats {
public:
    static void getAllocatorStats(std::map<std::string, size_t> &allocator_stats) {
#if defined(HAVE_LIBTCMALLOC) || defined(HAVE_LIBTCMALLOC_MINIMAL)
        TCMallocStats::getStats(allocator_stats);
#else
        (void) allocator_stats;
#endif
    }
};

#endif /* STATS_HH */
<|endoftext|>


<commit_before>#ifndef NITRO_LOG_MIN_SEVERITY
#error "NITRO_LOG_MIN_SEVERITY should be set by the build system, but isn't!"
#endif

#define CATCH_CONFIG_MAIN
#include <catch.hpp>

#ifdef NITRO_LOG_MIN_SEVERITY
#undef NITRO_LOG_MIN_SEVERITY
#endif
#define NITRO_LOG_MIN_SEVERITY info

#include <nitro/log/attribute/severity.hpp>
#include <nitro/log/filter/severity_filter.hpp>
#include <nitro/log/log.hpp>
#include <nitro/log/sink/sequence.hpp>
#include <nitro/log/sink/stderr.hpp>
#include <nitro/log/sink/syslog.hpp>

namespace detail
{

using Sink = nitro::log::sink::sequence<nitro::log::sink::Syslog, nitro::log::sink::StdErr>;

typedef nitro::log::record<nitro::log::tag_attribute, nitro::log::message_attribute,
                           nitro::log::severity_attribute>
    record;

template <typename Record>
class log_formater
{
public:
    std::string format(Record& r)
    {
        std::stringstream s;

        s << "[";

        if (!r.tag().empty())
        {
            s << r.tag() << "][";
        }

        s << r.severity() << "]: " << r.message() << "\n";

        return s.str();
    }
};

template <typename Record>
using log_filter = nitro::log::filter::severity_filter<Record>;
}

using logging =
    nitro::log::logger<detail::record, detail::log_formater, detail::Sink, detail::log_filter>;

TEST_CASE("Without tag works", "[log]")
{
    SECTION("As one statement")
    {
        logging::fatal() << "Test";
        logging::error() << "Test";
        logging::warn() << "Test";
        logging::info() << "Test";
        logging::debug() << "Test";
        logging::trace() << "Test";
    }

    SECTION("As multiple statements")
    {
        {
            auto log = logging::fatal();
            log << "test";
        }

        {
            auto log = logging::error();
            log << "test";
        }

        {
            auto log = logging::warn();
            log << "test";
        }

        {
            auto log = logging::info();
            log << "test";
        }

        {
            auto log = logging::debug();
            log << "test";
        }

        {
            auto log = logging::trace();
            log << "test";
        }
    }
}

TEST_CASE("With tag works", "[log]")
{
    SECTION("As one statement")
    {
        logging::fatal("test tag") << "Test";
        logging::error("test tag") << "Test";
        logging::warn("test tag") << "Test";
        logging::info("test tag") << "Test";
        logging::debug("test tag") << "Test";
        logging::trace("test tag") << "Test";
    }

    SECTION("As multiple statements")
    {
        {
            auto log = logging::fatal("test tag");
            log << "test";
        }

        {
            auto log = logging::error("test tag");
            log << "test";
        }

        {
            auto log = logging::warn("test tag");
            log << "test";
        }

        {
            auto log = logging::info("test tag");
            log << "test";
        }

        {
            auto log = logging::debug("test tag");
            log << "test";
        }

        {
            auto log = logging::trace("test tag");
            log << "test";
        }
    }
}

TEST_CASE("Logging lambdas works", "[log]")
{
    SECTION("As one statement")
    {
        logging::fatal() << []() { return "test"; };
        logging::error() << []() { return "test"; };
        logging::warn() << []() { return "test"; };
        logging::info() << []() { return "test"; };
        logging::debug() << []() { return "test"; };
        logging::trace() << []() { return "test"; };
    }

    SECTION("As multiple statements")
    {
        {
            auto log = logging::fatal();
            log << []() { return "test"; };
        }

        {
            auto log = logging::error();
            log << []() { return "test"; };
        }

        {
            auto log = logging::warn();
            log << []() { return "test"; };
        }

        {
            auto log = logging::info();
            log << []() { return "test"; };
        }

        {
            auto log = logging::debug();
            log << []() { return "test"; };
        }

        {
            auto log = logging::trace();
            log << []() { return "test"; };
        }
    }

    SECTION("Evaluation of the lambda is lazy")
    {
        int i = 0;

        logging::fatal() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 1);

        logging::error() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 2);

        logging::warn() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 3);

        logging::info() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 4);

        logging::debug() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 4);

        logging::trace() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 4);
    }
}
<commit_msg>Kann das auch Vindovs?<commit_after>#ifndef NITRO_LOG_MIN_SEVERITY
#error "NITRO_LOG_MIN_SEVERITY should be set by the build system, but isn't!"
#endif

#define CATCH_CONFIG_MAIN
#include <catch.hpp>

#ifdef NITRO_LOG_MIN_SEVERITY
#undef NITRO_LOG_MIN_SEVERITY
#endif
#define NITRO_LOG_MIN_SEVERITY info

#include <nitro/log/attribute/severity.hpp>
#include <nitro/log/filter/severity_filter.hpp>
#include <nitro/log/log.hpp>
#include <nitro/log/sink/sequence.hpp>
#include <nitro/log/sink/stderr.hpp>
#include <nitro/log/sink/stdout.hpp>

namespace detail
{

using Sink = nitro::log::sink::sequence<nitro::log::sink::StdOut, nitro::log::sink::StdErr>;

typedef nitro::log::record<nitro::log::tag_attribute, nitro::log::message_attribute,
                           nitro::log::severity_attribute>
    record;

template <typename Record>
class log_formater
{
public:
    std::string format(Record& r)
    {
        std::stringstream s;

        s << "[";

        if (!r.tag().empty())
        {
            s << r.tag() << "][";
        }

        s << r.severity() << "]: " << r.message() << "\n";

        return s.str();
    }
};

template <typename Record>
using log_filter = nitro::log::filter::severity_filter<Record>;
}

using logging =
    nitro::log::logger<detail::record, detail::log_formater, detail::Sink, detail::log_filter>;

TEST_CASE("Without tag works", "[log]")
{
    SECTION("As one statement")
    {
        logging::fatal() << "Test";
        logging::error() << "Test";
        logging::warn() << "Test";
        logging::info() << "Test";
        logging::debug() << "Test";
        logging::trace() << "Test";
    }

    SECTION("As multiple statements")
    {
        {
            auto log = logging::fatal();
            log << "test";
        }

        {
            auto log = logging::error();
            log << "test";
        }

        {
            auto log = logging::warn();
            log << "test";
        }

        {
            auto log = logging::info();
            log << "test";
        }

        {
            auto log = logging::debug();
            log << "test";
        }

        {
            auto log = logging::trace();
            log << "test";
        }
    }
}

TEST_CASE("With tag works", "[log]")
{
    SECTION("As one statement")
    {
        logging::fatal("test tag") << "Test";
        logging::error("test tag") << "Test";
        logging::warn("test tag") << "Test";
        logging::info("test tag") << "Test";
        logging::debug("test tag") << "Test";
        logging::trace("test tag") << "Test";
    }

    SECTION("As multiple statements")
    {
        {
            auto log = logging::fatal("test tag");
            log << "test";
        }

        {
            auto log = logging::error("test tag");
            log << "test";
        }

        {
            auto log = logging::warn("test tag");
            log << "test";
        }

        {
            auto log = logging::info("test tag");
            log << "test";
        }

        {
            auto log = logging::debug("test tag");
            log << "test";
        }

        {
            auto log = logging::trace("test tag");
            log << "test";
        }
    }
}

TEST_CASE("Logging lambdas works", "[log]")
{
    SECTION("As one statement")
    {
        logging::fatal() << []() { return "test"; };
        logging::error() << []() { return "test"; };
        logging::warn() << []() { return "test"; };
        logging::info() << []() { return "test"; };
        logging::debug() << []() { return "test"; };
        logging::trace() << []() { return "test"; };
    }

    SECTION("As multiple statements")
    {
        {
            auto log = logging::fatal();
            log << []() { return "test"; };
        }

        {
            auto log = logging::error();
            log << []() { return "test"; };
        }

        {
            auto log = logging::warn();
            log << []() { return "test"; };
        }

        {
            auto log = logging::info();
            log << []() { return "test"; };
        }

        {
            auto log = logging::debug();
            log << []() { return "test"; };
        }

        {
            auto log = logging::trace();
            log << []() { return "test"; };
        }
    }

    SECTION("Evaluation of the lambda is lazy")
    {
        int i = 0;

        logging::fatal() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 1);

        logging::error() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 2);

        logging::warn() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 3);

        logging::info() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 4);

        logging::debug() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 4);

        logging::trace() << [&i]() {
            ++i;
            return "test";
        };

        CHECK(i == 4);
    }
}
<|endoftext|>


<commit_before>/**
 * @file tests/commands_test.cpp
 * @brief Mega SDK unit tests for megaapi
 *
 * (c) 2019 by Mega Limited, Wellsford, New Zealand
 *
 * This file is part of the MEGA SDK - Client Access Engine.
 *
 * Applications using the MEGA API must present a valid application key
 * and comply with the the rules set forth in the Terms of Service.
 *
 * The MEGA SDK 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.
 *
 * @copyright Simplified (2-clause) BSD License.
 *
 * You should have received a copy of the license along with this
 * program.
 */

// Note: The tests in this module are meant to be pure unit tests: Fast tests without I/O.

#include <memory>

#include <gtest/gtest.h>

#include <megaapi.h>
#include <megaapi_impl.h>

using namespace std;
using namespace mega;

namespace {

unique_ptr<MegaStringList> createMegaStringList(const vector<const char*>& data)
{
    return unique_ptr<MegaStringList>{
        new MegaStringListPrivate{const_cast<char**>(data.data()), static_cast<int>(data.size())}};
}

} // anonymous

TEST(megaapi, MegaStringList_append_and_get_happyPath)
{
    auto stringList = MegaStringList::createInstance();
    stringList->append("foo");
    stringList->append("bar");
    ASSERT_EQ(2, stringList->size());
    ASSERT_EQ(string{"foo"}, string{stringList->get(0)});
    ASSERT_EQ(string{"bar"}, string{stringList->get(1)});
    ASSERT_EQ(nullptr, stringList->get(2));
}

TEST(megaapi, MegaStringList_get_and_size_happyPath)
{
    const vector<const char*> data{
        "foo",
        "bar",
    };
    auto stringList = createMegaStringList(data);
    ASSERT_EQ(2, stringList->size());
    ASSERT_EQ(string{"foo"}, string{stringList->get(0)});
    ASSERT_EQ(string{"bar"}, string{stringList->get(1)});
    ASSERT_EQ(nullptr, stringList->get(2));
}

TEST(megaapi, MegaStringList_get_and_size_emptyStringList)
{
    const vector<const char*> data{
    };
    auto stringList = createMegaStringList(data);
    ASSERT_EQ(0, stringList->size());
    ASSERT_EQ(nullptr, stringList->get(0));
}

TEST(megaapi, MegaStringList_copy_happyPath)
{
    const vector<const char*> data{
        "foo",
        "bar",
    };
    auto stringList = createMegaStringList(data);
    auto copiedStringList = unique_ptr<MegaStringList>{stringList->copy()};
    ASSERT_EQ(2, copiedStringList->size());
    ASSERT_EQ(string{"foo"}, string{copiedStringList->get(0)});
    ASSERT_EQ(string{"bar"}, string{copiedStringList->get(1)});
    ASSERT_EQ(nullptr, copiedStringList->get(2));
}

TEST(megaapi, MegaStringList_copy_emptyStringList)
{
    const vector<const char*> data{
    };
    auto stringList = createMegaStringList(data);
    auto copiedStringList = unique_ptr<MegaStringList>{stringList->copy()};
    ASSERT_EQ(0, copiedStringList->size());
    ASSERT_EQ(nullptr, copiedStringList->get(0));
}

TEST(megaapi, MegaStringList_default_constructor)
{
    auto stringList = unique_ptr<MegaStringList>{new MegaStringListPrivate};
    ASSERT_EQ(0, stringList->size());
    ASSERT_EQ(nullptr, stringList->get(0));
}

TEST(megaapi, MegaStringListMap_set_and_get_happyPath)
{
    auto stringListMap = unique_ptr<MegaStringListMap>{MegaStringListMap::createInstance()};
    auto stringList1 = createMegaStringList({"13", "42"});
    auto stringList2 = createMegaStringList({"awesome", "sweet", "cool"});
    stringListMap->set("foo", stringList1.get());
    stringListMap->set("bar", stringList2.get());
    ASSERT_EQ(2, stringListMap->size());
    ASSERT_EQ(*stringList1, *stringListMap->get("foo"));
    ASSERT_EQ(*stringList2, *stringListMap->get("bar"));
    ASSERT_EQ(nullptr, stringListMap->get("blah"));
}

TEST(megaapi, MegaStringListMap_get_emptyStringListMap)
{
    auto stringListMap = unique_ptr<MegaStringListMap>{MegaStringListMap::createInstance()};
    ASSERT_EQ(0, stringListMap->size());
    ASSERT_EQ(nullptr, stringListMap->get("blah"));
}

TEST(megaapi, MegaStringListMap_copy_happyPath)
{
    auto stringListMap = unique_ptr<MegaStringListMap>{MegaStringListMap::createInstance()};
    auto stringList1 = createMegaStringList({"13", "42"});
    auto stringList2 = createMegaStringList({"awesome", "sweet", "cool"});
    stringListMap->set("foo", stringList1.get());
    stringListMap->set("bar", stringList2.get());
    auto copiedStringListMap = unique_ptr<MegaStringListMap>{stringListMap->copy()};
    ASSERT_EQ(2, copiedStringListMap->size());
    ASSERT_EQ(*stringList1, *copiedStringListMap->get("foo"));
    ASSERT_EQ(*stringList2, *copiedStringListMap->get("bar"));
    ASSERT_EQ(nullptr, copiedStringListMap->get("blah"));
}

TEST(megaapi, MegaStringListMap_copy_emptyStringListMap)
{
    auto stringListMap = unique_ptr<MegaStringListMap>{MegaStringListMap::createInstance()};
    auto copiedStringListMap = unique_ptr<MegaStringListMap>{stringListMap->copy()};
    ASSERT_EQ(0, copiedStringListMap->size());
    ASSERT_EQ(nullptr, copiedStringListMap->get("blah"));
}

TEST(megaapi, MegaStringTable_append_and_get_happyPath)
{
    auto stringListTable = unique_ptr<MegaStringTable>{MegaStringTable::createInstance()};
    auto stringList1 = createMegaStringList({"13", "42"});
    auto stringList2 = createMegaStringList({"awesome", "sweet", "cool"});
    stringListTable->append(stringList1.get());
    stringListTable->append(stringList2.get());
    ASSERT_EQ(2, stringListTable->size());
    ASSERT_EQ(*stringList1, *stringListTable->get(0));
    ASSERT_EQ(*stringList2, *stringListTable->get(1));
    ASSERT_EQ(nullptr, stringListTable->get(2));
}

TEST(megaapi, MegaStringTable_get_emptyStringTable)
{
    auto stringListTable = unique_ptr<MegaStringTable>{MegaStringTable::createInstance()};
    ASSERT_EQ(0, stringListTable->size());
    ASSERT_EQ(nullptr, stringListTable->get(0));
}

TEST(megaapi, MegaStringTable_copy_happyPath)
{
    auto stringListTable = unique_ptr<MegaStringTable>{MegaStringTable::createInstance()};
    auto stringList1 = createMegaStringList({"13", "42"});
    auto stringList2 = createMegaStringList({"awesome", "sweet", "cool"});
    stringListTable->append(stringList1.get());
    stringListTable->append(stringList2.get());
    auto copiedStringTable = unique_ptr<MegaStringTable>{stringListTable->copy()};
    ASSERT_EQ(2, copiedStringTable->size());
    ASSERT_EQ(*stringList1, *copiedStringTable->get(0));
    ASSERT_EQ(*stringList2, *copiedStringTable->get(1));
    ASSERT_EQ(nullptr, copiedStringTable->get(2));
}

TEST(megaapi, MegaStringTable_copy_emptyStringTable)
{
    auto stringListTable = unique_ptr<MegaStringTable>{MegaStringTable::createInstance()};
    auto copiedStringTable = unique_ptr<MegaStringTable>{stringListTable->copy()};
    ASSERT_EQ(0, copiedStringTable->size());
    ASSERT_EQ(nullptr, copiedStringTable->get(0));
}
<commit_msg>Fix build of megaapi unit tests<commit_after>/**
 * @file tests/commands_test.cpp
 * @brief Mega SDK unit tests for megaapi
 *
 * (c) 2019 by Mega Limited, Wellsford, New Zealand
 *
 * This file is part of the MEGA SDK - Client Access Engine.
 *
 * Applications using the MEGA API must present a valid application key
 * and comply with the the rules set forth in the Terms of Service.
 *
 * The MEGA SDK 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.
 *
 * @copyright Simplified (2-clause) BSD License.
 *
 * You should have received a copy of the license along with this
 * program.
 */

// Note: The tests in this module are meant to be pure unit tests: Fast tests without I/O.

#include <memory>

#include <gtest/gtest.h>

#include <mega/types.h>
#include <megaapi.h>
#include <megaapi_impl.h>

using namespace std;
using namespace mega;

namespace {

unique_ptr<MegaStringList> createMegaStringList(const vector<const char*>& data)
{
    return unique_ptr<MegaStringList>{
        new MegaStringListPrivate{const_cast<char**>(data.data()), static_cast<int>(data.size())}};
}

} // anonymous

TEST(megaapi, MegaStringList_append_and_get_happyPath)
{
    auto stringList = MegaStringList::createInstance();
    stringList->append("foo");
    stringList->append("bar");
    ASSERT_EQ(2, stringList->size());
    ASSERT_EQ(string{"foo"}, string{stringList->get(0)});
    ASSERT_EQ(string{"bar"}, string{stringList->get(1)});
    ASSERT_EQ(nullptr, stringList->get(2));
}

TEST(megaapi, MegaStringList_get_and_size_happyPath)
{
    const vector<const char*> data{
        "foo",
        "bar",
    };
    auto stringList = createMegaStringList(data);
    ASSERT_EQ(2, stringList->size());
    ASSERT_EQ(string{"foo"}, string{stringList->get(0)});
    ASSERT_EQ(string{"bar"}, string{stringList->get(1)});
    ASSERT_EQ(nullptr, stringList->get(2));
}

TEST(megaapi, MegaStringList_get_and_size_emptyStringList)
{
    const vector<const char*> data{
    };
    auto stringList = createMegaStringList(data);
    ASSERT_EQ(0, stringList->size());
    ASSERT_EQ(nullptr, stringList->get(0));
}

TEST(megaapi, MegaStringList_copy_happyPath)
{
    const vector<const char*> data{
        "foo",
        "bar",
    };
    auto stringList = createMegaStringList(data);
    auto copiedStringList = unique_ptr<MegaStringList>{stringList->copy()};
    ASSERT_EQ(2, copiedStringList->size());
    ASSERT_EQ(string{"foo"}, string{copiedStringList->get(0)});
    ASSERT_EQ(string{"bar"}, string{copiedStringList->get(1)});
    ASSERT_EQ(nullptr, copiedStringList->get(2));
}

TEST(megaapi, MegaStringList_copy_emptyStringList)
{
    const vector<const char*> data{
    };
    auto stringList = createMegaStringList(data);
    auto copiedStringList = unique_ptr<MegaStringList>{stringList->copy()};
    ASSERT_EQ(0, copiedStringList->size());
    ASSERT_EQ(nullptr, copiedStringList->get(0));
}

TEST(megaapi, MegaStringList_default_constructor)
{
    auto stringList = unique_ptr<MegaStringList>{new MegaStringListPrivate};
    ASSERT_EQ(0, stringList->size());
    ASSERT_EQ(nullptr, stringList->get(0));
}

TEST(megaapi, MegaStringListMap_set_and_get_happyPath)
{
    auto stringListMap = unique_ptr<MegaStringListMap>{MegaStringListMap::createInstance()};
    auto stringList1 = createMegaStringList({"13", "42"});
    auto stringList2 = createMegaStringList({"awesome", "sweet", "cool"});
    stringListMap->set("foo", stringList1.get());
    stringListMap->set("bar", stringList2.get());
    ASSERT_EQ(2, stringListMap->size());
    ASSERT_EQ(*stringList1, *stringListMap->get("foo"));
    ASSERT_EQ(*stringList2, *stringListMap->get("bar"));
    ASSERT_EQ(nullptr, stringListMap->get("blah"));
}

TEST(megaapi, MegaStringListMap_get_emptyStringListMap)
{
    auto stringListMap = unique_ptr<MegaStringListMap>{MegaStringListMap::createInstance()};
    ASSERT_EQ(0, stringListMap->size());
    ASSERT_EQ(nullptr, stringListMap->get("blah"));
}

TEST(megaapi, MegaStringListMap_copy_happyPath)
{
    auto stringListMap = unique_ptr<MegaStringListMap>{MegaStringListMap::createInstance()};
    auto stringList1 = createMegaStringList({"13", "42"});
    auto stringList2 = createMegaStringList({"awesome", "sweet", "cool"});
    stringListMap->set("foo", stringList1.get());
    stringListMap->set("bar", stringList2.get());
    auto copiedStringListMap = unique_ptr<MegaStringListMap>{stringListMap->copy()};
    ASSERT_EQ(2, copiedStringListMap->size());
    ASSERT_EQ(*stringList1, *copiedStringListMap->get("foo"));
    ASSERT_EQ(*stringList2, *copiedStringListMap->get("bar"));
    ASSERT_EQ(nullptr, copiedStringListMap->get("blah"));
}

TEST(megaapi, MegaStringListMap_copy_emptyStringListMap)
{
    auto stringListMap = unique_ptr<MegaStringListMap>{MegaStringListMap::createInstance()};
    auto copiedStringListMap = unique_ptr<MegaStringListMap>{stringListMap->copy()};
    ASSERT_EQ(0, copiedStringListMap->size());
    ASSERT_EQ(nullptr, copiedStringListMap->get("blah"));
}

TEST(megaapi, MegaStringTable_append_and_get_happyPath)
{
    auto stringListTable = unique_ptr<MegaStringTable>{MegaStringTable::createInstance()};
    auto stringList1 = createMegaStringList({"13", "42"});
    auto stringList2 = createMegaStringList({"awesome", "sweet", "cool"});
    stringListTable->append(stringList1.get());
    stringListTable->append(stringList2.get());
    ASSERT_EQ(2, stringListTable->size());
    ASSERT_EQ(*stringList1, *stringListTable->get(0));
    ASSERT_EQ(*stringList2, *stringListTable->get(1));
    ASSERT_EQ(nullptr, stringListTable->get(2));
}

TEST(megaapi, MegaStringTable_get_emptyStringTable)
{
    auto stringListTable = unique_ptr<MegaStringTable>{MegaStringTable::createInstance()};
    ASSERT_EQ(0, stringListTable->size());
    ASSERT_EQ(nullptr, stringListTable->get(0));
}

TEST(megaapi, MegaStringTable_copy_happyPath)
{
    auto stringListTable = unique_ptr<MegaStringTable>{MegaStringTable::createInstance()};
    auto stringList1 = createMegaStringList({"13", "42"});
    auto stringList2 = createMegaStringList({"awesome", "sweet", "cool"});
    stringListTable->append(stringList1.get());
    stringListTable->append(stringList2.get());
    auto copiedStringTable = unique_ptr<MegaStringTable>{stringListTable->copy()};
    ASSERT_EQ(2, copiedStringTable->size());
    ASSERT_EQ(*stringList1, *copiedStringTable->get(0));
    ASSERT_EQ(*stringList2, *copiedStringTable->get(1));
    ASSERT_EQ(nullptr, copiedStringTable->get(2));
}

TEST(megaapi, MegaStringTable_copy_emptyStringTable)
{
    auto stringListTable = unique_ptr<MegaStringTable>{MegaStringTable::createInstance()};
    auto copiedStringTable = unique_ptr<MegaStringTable>{stringListTable->copy()};
    ASSERT_EQ(0, copiedStringTable->size());
    ASSERT_EQ(nullptr, copiedStringTable->get(0));
}
<|endoftext|>


<commit_before>// Floating Temple
// Copyright 2015 Derek S. Snyder
//
// 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 "lua/table_local_object.h"

#include <cstddef>
#include <memory>
#include <string>
#include <vector>

#include "base/escape.h"
#include "base/logging.h"
#include "include/c++/value.h"
#include "lua/convert_value.h"
#include "lua/proto/serialization.pb.h"
#include "third_party/lua-5.2.3/src/lgc.h"
#include "third_party/lua-5.2.3/src/llimits.h"
#include "third_party/lua-5.2.3/src/lmem.h"
#include "third_party/lua-5.2.3/src/lobject.h"
#include "third_party/lua-5.2.3/src/lopcodes.h"
#include "third_party/lua-5.2.3/src/lstate.h"
#include "third_party/lua-5.2.3/src/ltable.h"
#include "third_party/lua-5.2.3/src/lua.h"
#include "third_party/lua-5.2.3/src/lvm.h"
#include "util/math_util.h"

using std::size_t;
using std::string;
using std::vector;

namespace floating_temple {
namespace lua {
namespace {

int GetTableNodeIndex(const Table* table, const Node* node) {
  return node - table->node;
}

}  // namespace

TableLocalObject::TableLocalObject(lua_State* lua_state)
    : lua_state_(CHECK_NOTNULL(lua_state)),
      lua_table_(new TValue()) {
  // This code is mostly copy-pasted from the luaH_new function in
  // "third_party/lua-5.2.3/src/ltable.c".
  GCObject* gc_list = nullptr;
  Table* const table =
      &luaC_newobj(lua_state, LUA_TTABLE, sizeof (Table), &gc_list, 0)->h;

  Node* const node = const_cast<Node*>(luaH_getdummynode());

  table->flags = static_cast<lu_byte>(~0);
  table->lsizenode = static_cast<lu_byte>(~0);
  // TODO(dss): Support metatables.
  table->metatable = nullptr;
  table->array = nullptr;
  table->node = node;
  table->lastfree = node;
  table->gclist = nullptr;
  table->sizearray = 0;

  sethvalue(lua_state, lua_table_.get(), table);
}

TableLocalObject::~TableLocalObject() {
  luaH_free(lua_state_, hvalue(lua_table_.get()));
}

void TableLocalObject::InvokeMethod(Thread* thread,
                                    ObjectReference* object_reference,
                                    const string& method_name,
                                    const vector<Value>& parameters,
                                    Value* return_value) {
  CHECK(return_value != nullptr);

  if (method_name == "gettable") {
    // TODO(dss): Fail gracefully if a remote peer sends a method with the wrong
    // number of parameters.
    CHECK_EQ(parameters.size(), 1u);

    TValue lua_key;
    ValueToLuaValue(lua_state_, parameters[0], &lua_key);

    TValue lua_value;
    luaV_gettable(lua_state_, lua_table_.get(), &lua_key, &lua_value);

    LuaValueToValue(&lua_value, return_value);
  } else if (method_name == "settable") {
    CHECK_EQ(parameters.size(), 2u);

    TValue lua_key;
    ValueToLuaValue(lua_state_, parameters[0], &lua_key);

    TValue lua_value;
    ValueToLuaValue(lua_state_, parameters[1], &lua_value);

    luaV_settable(lua_state_, lua_table_.get(), &lua_key, &lua_value);

    return_value->set_empty(LUA_TNIL);
  } else if (method_name == "len") {
    CHECK_EQ(parameters.size(), 0u);

    TValue lua_length;
    luaV_objlen(lua_state_, &lua_length, lua_table_.get());

    LuaValueToValue(&lua_length, return_value);
  } else if (method_name == "setlist") {
    CHECK_EQ(parameters.size(), 2u);

    int n = static_cast<int>(parameters[0].int64_value());
    const int c = static_cast<int>(parameters[1].int64_value());

    // This code is mostly copy-pasted from the luaV_execute function in
    // "third_party/lua-5.2.3/src/lvm.c".
    Table* const h = hvalue(lua_table_.get());
    int last = ((c - 1) * LFIELDS_PER_FLUSH) + n;
    if (last > h->sizearray) {
      luaH_resizearray(lua_state_, h, last);
    }
    for (; n > 0; n--) {
      // TODO(dss): [BUG] lua_table_ is not an array.
      TValue* const val = lua_table_.get() + n;
      luaH_setint(lua_state_, h, last--, val);
      luaC_barrierback(lua_state_, obj2gco(h), val);
    }

    return_value->set_empty(LUA_TNIL);
  } else {
    // TODO(dss): Fail gracefully if a remote peer sends an invalid method name.
    LOG(FATAL) << "Unexpected method name \"" << CEscape(method_name) << "\"";
  }
}

VersionedLocalObject* TableLocalObject::Clone() const {
  const Table* const old_table = hvalue(lua_table_.get());

  GCObject* gc_list = nullptr;
  Table* const new_table =
      &luaC_newobj(lua_state_, LUA_TTABLE, sizeof (Table), &gc_list, 0)->h;

  const lu_byte lsizenode = old_table->lsizenode;
  const int sizearray = old_table->sizearray;

  new_table->flags = old_table->flags;
  new_table->lsizenode = lsizenode;
  new_table->metatable = nullptr;

  if (old_table->array == nullptr) {
    new_table->array = nullptr;
  } else {
    new_table->array = luaM_newvector(lua_state_, sizearray, TValue);
    for (int i = 0; i < sizearray; ++i) {
      setobj2t(&lua_state_, &new_table->array[i], &old_table->array[i]);
    }
  }

  const Node* const dummy_node = luaH_getdummynode();

  if (old_table->node == dummy_node) {
    Node* const node = const_cast<Node*>(dummy_node);
    new_table->node = node;
    new_table->lastfree = node;
  } else {
    const int size = twoto(static_cast<int>(lsizenode));
    new_table->node = luaM_newvector(lua_state_, size, Node);

    for (int i = 0; i < size; ++i) {
      const Node* const old_node = gnode(old_table, i);
      Node* const new_node = gnode(new_table, i);

      gnext(new_node) = gnode(new_table,
                              GetTableNodeIndex(old_table, gnext(old_node)));
      setobj2t(&lua_state_, gkey(new_node), gkey(old_node));
      setobj2t(&lua_state_, gval(new_node), gval(old_node));
    }

    new_table->lastfree = gnode(
        new_table, GetTableNodeIndex(old_table, old_table->lastfree));
  }

  new_table->gclist = nullptr;
  new_table->sizearray = sizearray;

  return new TableLocalObject(lua_state_, new_table);
}

size_t TableLocalObject::Serialize(void* buffer, size_t buffer_size,
                                   SerializationContext* context) const {
  const Table* const table = hvalue(lua_table_.get());
  TableProto table_proto;

  // Store the array part of the table in the protocol buffer.
  if (table->array != nullptr) {
    const int sizearray = table->sizearray;
    ArrayProto* const array_proto = table_proto.mutable_array();

    for (int i = 0; i < sizearray; ++i) {
      TValueProto* const tvalue_proto =
          array_proto->add_element()->mutable_value();
      LuaValueToValueProto(&table->array[i], tvalue_proto, context);
    }
  }

  // Store the hashtable part of the table in the protocol buffer.
  if (table->node != luaH_getdummynode()) {
    const int size = twoto(static_cast<int>(table->lsizenode));
    HashtableProto* const hashtable_proto = table_proto.mutable_hashtable();

    hashtable_proto->set_size(size);

    for (int i = 0; i < size; ++i) {
      const Node* const node = &table->node[i];
      const TValue* const lua_key = gkey(node);
      const TValue* const lua_value = gval(node);

      if (!ttisnil(lua_key) || !ttisnil(lua_value)) {
        const Node* const next_node = gnext(node);
        HashtableNodeProto* const node_proto = hashtable_proto->add_node();

        node_proto->set_index(i);
        if (next_node != nullptr) {
          node_proto->set_next_index(GetTableNodeIndex(table, next_node));
        }
        LuaValueToValueProto(lua_key, node_proto->mutable_key(), context);
        LuaValueToValueProto(lua_value, node_proto->mutable_value(), context);
      }
    }

    hashtable_proto->set_last_free_index(GetTableNodeIndex(table,
                                                           table->lastfree));
  }

  // Serialize the protocol buffer to the output buffer.
  const size_t byte_size = static_cast<size_t>(table_proto.ByteSize());
  if (byte_size <= buffer_size) {
    table_proto.SerializeWithCachedSizesToArray(static_cast<uint8*>(buffer));
  }

  return byte_size;
}

void TableLocalObject::Dump(DumpContext* dc) const {
  CHECK(dc != nullptr);

  // TODO(dss): Implement this.
}

// static
TableLocalObject* TableLocalObject::Deserialize(
    lua_State* lua_state,
    const void* buffer,
    size_t buffer_size,
    DeserializationContext* context) {
  CHECK(lua_state != nullptr);
  CHECK(buffer != nullptr);

  // Parse the protocol buffer from the input buffer.
  TableProto table_proto;
  CHECK(table_proto.ParseFromArray(buffer, buffer_size));

  // Allocate the Table struct.
  GCObject* gc_list = nullptr;
  Table* const table =
      &luaC_newobj(lua_state, LUA_TTABLE, sizeof (Table), &gc_list, 0)->h;

  table->flags = static_cast<lu_byte>(~0);
  table->metatable = nullptr;

  // Read the array part of the table from the protocol buffer.
  if (table_proto.has_array()) {
    const ArrayProto& array_proto = table_proto.array();
    const int sizearray = array_proto.element_size();

    table->array = luaM_newvector(lua_state, sizearray, TValue);

    for (int i = 0; i < sizearray; ++i) {
      const ArrayElementProto& element_proto = array_proto.element(i);
      ValueProtoToLuaValue(lua_state, element_proto.value(), &table->array[i],
                           context);
    }

    table->sizearray = sizearray;
  } else {
    table->array = nullptr;
    table->sizearray = 0;
  }

  // Read the hashtable part of the table from the protocol buffer.
  if (table_proto.has_hashtable()) {
    const HashtableProto& hashtable_proto = table_proto.hashtable();

    const int size = hashtable_proto.size();
    CHECK(IsPowerOfTwo(static_cast<unsigned>(size)))
        << size << " is not a power of two.";

    table->lsizenode = static_cast<lu_byte>(
        luaO_ceillog2(static_cast<unsigned>(size)));
    table->node = luaM_newvector(lua_state, size, Node);

    const int node_count = hashtable_proto.node_size();
    int prev_node_index = 0;

    for (int i = 0; i < node_count; ++i) {
      const HashtableNodeProto& node_proto = hashtable_proto.node(i);
      const int node_index = node_proto.index();

      // Initialize unused hashtable nodes.
      for (int j = prev_node_index + 1; j < node_index; ++j) {
        Node* const node = gnode(table, j);
        gnext(node) = nullptr;
        setnilvalue(gkey(node));
        setnilvalue(gval(node));
      }

      Node* const node = gnode(table, node_index);

      if (node_proto.has_next_index()) {
        gnext(node) = gnode(table, node_proto.next_index());
      } else {
        gnext(node) = nullptr;
      }
      ValueProtoToLuaValue(lua_state, node_proto.key(), gkey(node), context);
      ValueProtoToLuaValue(lua_state, node_proto.value(), gval(node), context);

      prev_node_index = node_index;
    }

    // Initialize unused hashtable nodes.
    for (int j = prev_node_index + 1; j < size; ++j) {
      Node* const node = gnode(table, j);
      gnext(node) = nullptr;
      setnilvalue(gkey(node));
      setnilvalue(gval(node));
    }

    table->lastfree = gnode(table, hashtable_proto.last_free_index());
  } else {
    table->lsizenode = static_cast<lu_byte>(~0);
    Node* const node = const_cast<Node*>(luaH_getdummynode());
    table->node = node;
    table->lastfree = node;
  }

  table->gclist = nullptr;

  return new TableLocalObject(lua_state, table);
}

TableLocalObject::TableLocalObject(lua_State* lua_state, Table* table)
    : lua_state_(CHECK_NOTNULL(lua_state)),
      lua_table_(new TValue()) {
  CHECK(table != nullptr);
  sethvalue(lua_state, lua_table_.get(), table);
}

}  // namespace lua
}  // namespace floating_temple
<commit_msg>Fix a bug in the implementation of the "setlist" method for table objects.<commit_after>// Floating Temple
// Copyright 2015 Derek S. Snyder
//
// 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 "lua/table_local_object.h"

#include <cstddef>
#include <memory>
#include <string>
#include <vector>

#include "base/escape.h"
#include "base/logging.h"
#include "include/c++/value.h"
#include "lua/convert_value.h"
#include "lua/proto/serialization.pb.h"
#include "third_party/lua-5.2.3/src/lgc.h"
#include "third_party/lua-5.2.3/src/llimits.h"
#include "third_party/lua-5.2.3/src/lmem.h"
#include "third_party/lua-5.2.3/src/lobject.h"
#include "third_party/lua-5.2.3/src/lopcodes.h"
#include "third_party/lua-5.2.3/src/lstate.h"
#include "third_party/lua-5.2.3/src/ltable.h"
#include "third_party/lua-5.2.3/src/lua.h"
#include "third_party/lua-5.2.3/src/lvm.h"
#include "util/math_util.h"

using std::size_t;
using std::string;
using std::vector;

namespace floating_temple {
namespace lua {
namespace {

int GetTableNodeIndex(const Table* table, const Node* node) {
  return node - table->node;
}

}  // namespace

TableLocalObject::TableLocalObject(lua_State* lua_state)
    : lua_state_(CHECK_NOTNULL(lua_state)),
      lua_table_(new TValue()) {
  // This code is mostly copy-pasted from the luaH_new function in
  // "third_party/lua-5.2.3/src/ltable.c".
  GCObject* gc_list = nullptr;
  Table* const table =
      &luaC_newobj(lua_state, LUA_TTABLE, sizeof (Table), &gc_list, 0)->h;

  Node* const node = const_cast<Node*>(luaH_getdummynode());

  table->flags = static_cast<lu_byte>(~0);
  table->lsizenode = static_cast<lu_byte>(~0);
  // TODO(dss): Support metatables.
  table->metatable = nullptr;
  table->array = nullptr;
  table->node = node;
  table->lastfree = node;
  table->gclist = nullptr;
  table->sizearray = 0;

  sethvalue(lua_state, lua_table_.get(), table);
}

TableLocalObject::~TableLocalObject() {
  luaH_free(lua_state_, hvalue(lua_table_.get()));
}

void TableLocalObject::InvokeMethod(Thread* thread,
                                    ObjectReference* object_reference,
                                    const string& method_name,
                                    const vector<Value>& parameters,
                                    Value* return_value) {
  CHECK(return_value != nullptr);

  if (method_name == "gettable") {
    // TODO(dss): Fail gracefully if a remote peer sends a method with the wrong
    // number of parameters.
    CHECK_EQ(parameters.size(), 1u);

    TValue lua_key;
    ValueToLuaValue(lua_state_, parameters[0], &lua_key);

    TValue lua_value;
    luaV_gettable(lua_state_, lua_table_.get(), &lua_key, &lua_value);

    LuaValueToValue(&lua_value, return_value);
  } else if (method_name == "settable") {
    CHECK_EQ(parameters.size(), 2u);

    TValue lua_key;
    ValueToLuaValue(lua_state_, parameters[0], &lua_key);

    TValue lua_value;
    ValueToLuaValue(lua_state_, parameters[1], &lua_value);

    luaV_settable(lua_state_, lua_table_.get(), &lua_key, &lua_value);

    return_value->set_empty(LUA_TNIL);
  } else if (method_name == "len") {
    CHECK_EQ(parameters.size(), 0u);

    TValue lua_length;
    luaV_objlen(lua_state_, &lua_length, lua_table_.get());

    LuaValueToValue(&lua_length, return_value);
  } else if (method_name == "setlist") {
    CHECK_GE(parameters.size(), 1u);

    int n = static_cast<int>(parameters.size() - 1u);
    const int c = static_cast<int>(parameters[0].int64_value());

    // This code is mostly copy-pasted from the luaV_execute function in
    // "third_party/lua-5.2.3/src/lvm.c".
    Table* const h = hvalue(lua_table_.get());
    int last = ((c - 1) * LFIELDS_PER_FLUSH) + n;
    if (last > h->sizearray) {
      luaH_resizearray(lua_state_, h, last);
    }
    for (; n > 0; n--) {
      TValue lua_value;
      ValueToLuaValue(lua_state_, parameters[n], &lua_value);

      luaH_setint(lua_state_, h, last--, &lua_value);
      luaC_barrierback(lua_state_, obj2gco(h), &lua_value);
    }

    return_value->set_empty(LUA_TNIL);
  } else {
    // TODO(dss): Fail gracefully if a remote peer sends an invalid method name.
    LOG(FATAL) << "Unexpected method name \"" << CEscape(method_name) << "\"";
  }
}

VersionedLocalObject* TableLocalObject::Clone() const {
  const Table* const old_table = hvalue(lua_table_.get());

  GCObject* gc_list = nullptr;
  Table* const new_table =
      &luaC_newobj(lua_state_, LUA_TTABLE, sizeof (Table), &gc_list, 0)->h;

  const lu_byte lsizenode = old_table->lsizenode;
  const int sizearray = old_table->sizearray;

  new_table->flags = old_table->flags;
  new_table->lsizenode = lsizenode;
  new_table->metatable = nullptr;

  if (old_table->array == nullptr) {
    new_table->array = nullptr;
  } else {
    new_table->array = luaM_newvector(lua_state_, sizearray, TValue);
    for (int i = 0; i < sizearray; ++i) {
      setobj2t(&lua_state_, &new_table->array[i], &old_table->array[i]);
    }
  }

  const Node* const dummy_node = luaH_getdummynode();

  if (old_table->node == dummy_node) {
    Node* const node = const_cast<Node*>(dummy_node);
    new_table->node = node;
    new_table->lastfree = node;
  } else {
    const int size = twoto(static_cast<int>(lsizenode));
    new_table->node = luaM_newvector(lua_state_, size, Node);

    for (int i = 0; i < size; ++i) {
      const Node* const old_node = gnode(old_table, i);
      Node* const new_node = gnode(new_table, i);

      gnext(new_node) = gnode(new_table,
                              GetTableNodeIndex(old_table, gnext(old_node)));
      setobj2t(&lua_state_, gkey(new_node), gkey(old_node));
      setobj2t(&lua_state_, gval(new_node), gval(old_node));
    }

    new_table->lastfree = gnode(
        new_table, GetTableNodeIndex(old_table, old_table->lastfree));
  }

  new_table->gclist = nullptr;
  new_table->sizearray = sizearray;

  return new TableLocalObject(lua_state_, new_table);
}

size_t TableLocalObject::Serialize(void* buffer, size_t buffer_size,
                                   SerializationContext* context) const {
  const Table* const table = hvalue(lua_table_.get());
  TableProto table_proto;

  // Store the array part of the table in the protocol buffer.
  if (table->array != nullptr) {
    const int sizearray = table->sizearray;
    ArrayProto* const array_proto = table_proto.mutable_array();

    for (int i = 0; i < sizearray; ++i) {
      TValueProto* const tvalue_proto =
          array_proto->add_element()->mutable_value();
      LuaValueToValueProto(&table->array[i], tvalue_proto, context);
    }
  }

  // Store the hashtable part of the table in the protocol buffer.
  if (table->node != luaH_getdummynode()) {
    const int size = twoto(static_cast<int>(table->lsizenode));
    HashtableProto* const hashtable_proto = table_proto.mutable_hashtable();

    hashtable_proto->set_size(size);

    for (int i = 0; i < size; ++i) {
      const Node* const node = &table->node[i];
      const TValue* const lua_key = gkey(node);
      const TValue* const lua_value = gval(node);

      if (!ttisnil(lua_key) || !ttisnil(lua_value)) {
        const Node* const next_node = gnext(node);
        HashtableNodeProto* const node_proto = hashtable_proto->add_node();

        node_proto->set_index(i);
        if (next_node != nullptr) {
          node_proto->set_next_index(GetTableNodeIndex(table, next_node));
        }
        LuaValueToValueProto(lua_key, node_proto->mutable_key(), context);
        LuaValueToValueProto(lua_value, node_proto->mutable_value(), context);
      }
    }

    hashtable_proto->set_last_free_index(GetTableNodeIndex(table,
                                                           table->lastfree));
  }

  // Serialize the protocol buffer to the output buffer.
  const size_t byte_size = static_cast<size_t>(table_proto.ByteSize());
  if (byte_size <= buffer_size) {
    table_proto.SerializeWithCachedSizesToArray(static_cast<uint8*>(buffer));
  }

  return byte_size;
}

void TableLocalObject::Dump(DumpContext* dc) const {
  CHECK(dc != nullptr);

  // TODO(dss): Implement this.
}

// static
TableLocalObject* TableLocalObject::Deserialize(
    lua_State* lua_state,
    const void* buffer,
    size_t buffer_size,
    DeserializationContext* context) {
  CHECK(lua_state != nullptr);
  CHECK(buffer != nullptr);

  // Parse the protocol buffer from the input buffer.
  TableProto table_proto;
  CHECK(table_proto.ParseFromArray(buffer, buffer_size));

  // Allocate the Table struct.
  GCObject* gc_list = nullptr;
  Table* const table =
      &luaC_newobj(lua_state, LUA_TTABLE, sizeof (Table), &gc_list, 0)->h;

  table->flags = static_cast<lu_byte>(~0);
  table->metatable = nullptr;

  // Read the array part of the table from the protocol buffer.
  if (table_proto.has_array()) {
    const ArrayProto& array_proto = table_proto.array();
    const int sizearray = array_proto.element_size();

    table->array = luaM_newvector(lua_state, sizearray, TValue);

    for (int i = 0; i < sizearray; ++i) {
      const ArrayElementProto& element_proto = array_proto.element(i);
      ValueProtoToLuaValue(lua_state, element_proto.value(), &table->array[i],
                           context);
    }

    table->sizearray = sizearray;
  } else {
    table->array = nullptr;
    table->sizearray = 0;
  }

  // Read the hashtable part of the table from the protocol buffer.
  if (table_proto.has_hashtable()) {
    const HashtableProto& hashtable_proto = table_proto.hashtable();

    const int size = hashtable_proto.size();
    CHECK(IsPowerOfTwo(static_cast<unsigned>(size)))
        << size << " is not a power of two.";

    table->lsizenode = static_cast<lu_byte>(
        luaO_ceillog2(static_cast<unsigned>(size)));
    table->node = luaM_newvector(lua_state, size, Node);

    const int node_count = hashtable_proto.node_size();
    int prev_node_index = 0;

    for (int i = 0; i < node_count; ++i) {
      const HashtableNodeProto& node_proto = hashtable_proto.node(i);
      const int node_index = node_proto.index();

      // Initialize unused hashtable nodes.
      for (int j = prev_node_index + 1; j < node_index; ++j) {
        Node* const node = gnode(table, j);
        gnext(node) = nullptr;
        setnilvalue(gkey(node));
        setnilvalue(gval(node));
      }

      Node* const node = gnode(table, node_index);

      if (node_proto.has_next_index()) {
        gnext(node) = gnode(table, node_proto.next_index());
      } else {
        gnext(node) = nullptr;
      }
      ValueProtoToLuaValue(lua_state, node_proto.key(), gkey(node), context);
      ValueProtoToLuaValue(lua_state, node_proto.value(), gval(node), context);

      prev_node_index = node_index;
    }

    // Initialize unused hashtable nodes.
    for (int j = prev_node_index + 1; j < size; ++j) {
      Node* const node = gnode(table, j);
      gnext(node) = nullptr;
      setnilvalue(gkey(node));
      setnilvalue(gval(node));
    }

    table->lastfree = gnode(table, hashtable_proto.last_free_index());
  } else {
    table->lsizenode = static_cast<lu_byte>(~0);
    Node* const node = const_cast<Node*>(luaH_getdummynode());
    table->node = node;
    table->lastfree = node;
  }

  table->gclist = nullptr;

  return new TableLocalObject(lua_state, table);
}

TableLocalObject::TableLocalObject(lua_State* lua_state, Table* table)
    : lua_state_(CHECK_NOTNULL(lua_state)),
      lua_table_(new TValue()) {
  CHECK(table != nullptr);
  sethvalue(lua_state, lua_table_.get(), table);
}

}  // namespace lua
}  // namespace floating_temple
<|endoftext|>


<commit_before>#include "taskhook.h"
#include <cstdio>
#include <cstdint>

wchar_t const * const c_taskHookName = L"taskhook";
wchar_t const * const c_taskHook32Name = L"taskhook32";

#pragma data_seg(".shared")
TASKHOOK_API HHOOK g_taskHook = 0;
HWND g_bbHwnd = 0;
TASKHOOK_API unsigned g_WM_TaskHook = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.shared,RWS")

LRESULT CALLBACK taskManagerHookProc (int code, WPARAM wParam, LPARAM lParam)
{
	if (code >= 0)
	{
		PostMessage(g_bbHwnd, g_WM_TaskHook, code, wParam);
	}
	return CallNextHookEx(g_taskHook, code, wParam, lParam);
}

TASKHOOK_API void initTaskHook (HWND bb_hwnd)
{
	g_bbHwnd = bb_hwnd;
	g_WM_TaskHook = ::RegisterWindowMessage(c_taskHookName);
	wchar_t tmp[128];
	_snwprintf(tmp, 128, L"%ws.dll", c_taskHookName);
	HINSTANCE taskhook_hinst = ::GetModuleHandle(tmp);

	// @TODO: use WH_CBT
	g_taskHook = ::SetWindowsHookEx(WH_SHELL, (HOOKPROC)taskManagerHookProc, taskhook_hinst, 0);
	//RegisterTaskHookWindow

	if (!g_taskHook)
	{
		uint32_t const err = GetLastError();
	}
}

LRESULT CALLBACK taskManagerHookProc32 (int code, WPARAM wParam, LPARAM lParam)
{
	if (code >= 0)
	{
		::PostMessage(g_bbHwnd, g_WM_TaskHook, code, wParam);
	}
	return ::CallNextHookEx(g_taskHook, code, wParam, lParam);
}

TASKHOOK_API void initTaskHook32 (HWND bb_hwnd)
{
	g_bbHwnd = bb_hwnd;
	g_WM_TaskHook = ::RegisterWindowMessage(c_taskHook32Name);
	wchar_t tmp[128];
	_snwprintf(tmp, 128, L"%ws.dll", c_taskHook32Name);
	HINSTANCE taskhook_hinst = GetModuleHandle(tmp);
	g_taskHook = SetWindowsHookEx(WH_SHELL, (HOOKPROC)taskManagerHookProc, taskhook_hinst, 0);
	//g_taskHook = SetWindowsHookEx(WH_CBT, (HOOKPROC)taskManagerHookProc32, taskhook_hinst, 0);

	if (!g_taskHook)
	{
		uint32_t const err = GetLastError();
	}
}

TASKHOOK_API void doneTaskHook ()
{
	UnhookWindowsHookEx(g_taskHook);
	g_taskHook = nullptr;
}
<commit_msg>* crlf<commit_after>#include "taskhook.h"
#include <cstdio>
#include <cstdint>

wchar_t const * const c_taskHookName = L"taskhook";
wchar_t const * const c_taskHook32Name = L"taskhook32";

#pragma data_seg(".shared")
TASKHOOK_API HHOOK g_taskHook = 0;
HWND g_bbHwnd = 0;
TASKHOOK_API unsigned g_WM_TaskHook = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.shared,RWS")

LRESULT CALLBACK taskManagerHookProc (int code, WPARAM wParam, LPARAM lParam)
{
	if (code >= 0)
	{
		PostMessage(g_bbHwnd, g_WM_TaskHook, code, wParam);
	}
	return CallNextHookEx(g_taskHook, code, wParam, lParam);
}

TASKHOOK_API void initTaskHook (HWND bb_hwnd)
{
	g_bbHwnd = bb_hwnd;
	g_WM_TaskHook = ::RegisterWindowMessage(c_taskHookName);
	wchar_t tmp[128];
	_snwprintf(tmp, 128, L"%ws.dll", c_taskHookName);
	HINSTANCE taskhook_hinst = ::GetModuleHandle(tmp);

	// @TODO: use WH_CBT
	g_taskHook = ::SetWindowsHookEx(WH_SHELL, (HOOKPROC)taskManagerHookProc, taskhook_hinst, 0);
	//RegisterTaskHookWindow

	if (!g_taskHook)
	{
		uint32_t const err = GetLastError();
	}
}

LRESULT CALLBACK taskManagerHookProc32 (int code, WPARAM wParam, LPARAM lParam)
{
	if (code >= 0)
	{
		::PostMessage(g_bbHwnd, g_WM_TaskHook, code, wParam);
	}
	return ::CallNextHookEx(g_taskHook, code, wParam, lParam);
}

TASKHOOK_API void initTaskHook32 (HWND bb_hwnd)
{
	g_bbHwnd = bb_hwnd;
	g_WM_TaskHook = ::RegisterWindowMessage(c_taskHook32Name);
	wchar_t tmp[128];
	_snwprintf(tmp, 128, L"%ws.dll", c_taskHook32Name);
	HINSTANCE taskhook_hinst = GetModuleHandle(tmp);
	g_taskHook = SetWindowsHookEx(WH_SHELL, (HOOKPROC)taskManagerHookProc, taskhook_hinst, 0);
	//g_taskHook = SetWindowsHookEx(WH_CBT, (HOOKPROC)taskManagerHookProc32, taskhook_hinst, 0);

	if (!g_taskHook)
	{
		uint32_t const err = GetLastError();
	}
}

TASKHOOK_API void doneTaskHook ()
{
	UnhookWindowsHookEx(g_taskHook);
	g_taskHook = nullptr;
}
<|endoftext|>