<commit_before>/*
 * Copyright (c) 2018-2019 Inria
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Daniel Carvalho
 */

/** @file
 * Definition of a dictionary based cache compressor. Each entry is compared
 * against a dictionary to search for matches.
 *
 * The dictionary is composed of 32-bit entries, and the comparison is done
 * byte per byte.
 *
 * The patterns are implemented as individual classes that have a checking
 * function isPattern(), to determine if the data fits the pattern, and a
 * decompress() function, which decompresses the contents of a pattern.
 * Every new pattern must inherit from the Pattern class and be added to the
 * patternFactory.
 */

#ifndef __MEM_CACHE_COMPRESSORS_DICTIONARY_COMPRESSOR_HH__
#define __MEM_CACHE_COMPRESSORS_DICTIONARY_COMPRESSOR_HH__

#include <array>
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <vector>

#include "base/types.hh"
#include "mem/cache/compressors/base.hh"

struct BaseDictionaryCompressorParams;

class BaseDictionaryCompressor : public BaseCacheCompressor
{
  protected:
    /** Dictionary size. */
    const std::size_t dictionarySize;

    /** Number of valid entries in the dictionary. */
    std::size_t numEntries;

    /**
     * @defgroup CompressionStats Compression specific statistics.
     * @{
     */

    /** Number of data entries that were compressed to each pattern. */
    Stats::Vector patternStats;

    /**
     * @}
     */

    /**
     * Trick function to get the number of patterns.
     *
     * @return The number of defined patterns.
     */
    virtual uint64_t getNumPatterns() const = 0;

    /**
     * Get meta-name assigned to the given pattern.
     *
     * @param number The number of the pattern.
     * @return The meta-name of the pattern.
     */
    virtual std::string getName(int number) const = 0;

  public:
    typedef BaseDictionaryCompressorParams Params;
    BaseDictionaryCompressor(const Params *p);
    ~BaseDictionaryCompressor() = default;

    void regStats() override;
};

/**
 * A template version of the dictionary compressor that allows to choose the
 * dictionary size.
 *
 * @tparam The type of a dictionary entry (e.g., uint16_t, uint32_t, etc).
 */
template <class T>
class DictionaryCompressor : public BaseDictionaryCompressor
{
  protected:
    /** Convenience typedef for a dictionary entry. */
    typedef std::array<uint8_t, sizeof(T)> DictionaryEntry;

    /**
     * Compression data for the dictionary compressor. It consists of a vector
     * of patterns.
     */
    class CompData;

    // Forward declaration of a pattern
    class Pattern;
    class UncompressedPattern;
    template <T mask>
    class MaskedPattern;
    template <T value, T mask>
    class MaskedValuePattern;

    /**
     * Create a factory to determine if input matches a pattern. The if else
     * chains are constructed by recursion. The patterns should be explored
     * sorted by size for correct behaviour.
     */
    template <class Head, class... Tail>
    struct Factory
    {
        static std::unique_ptr<Pattern> getPattern(
            const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
            const int match_location)
        {
            // If match this pattern, instantiate it. If a negative match
            // location is used, the patterns that use the dictionary bytes
            // must return false. This is used when there are no dictionary
            // entries yet
            if (Head::isPattern(bytes, dict_bytes, match_location)) {
                return std::unique_ptr<Pattern>(
                            new Head(bytes, match_location));
            // Otherwise, go for next pattern
            } else {
                return Factory<Tail...>::getPattern(bytes, dict_bytes,
                                                    match_location);
            }
        }
    };

    /**
     * Specialization to end the recursion. This must be called when all
     * other patterns failed, and there is no choice but to leave data
     * uncompressed. As such, this pattern must inherit from the uncompressed
     * pattern.
     */
    template <class Head>
    struct Factory<Head>
    {
        static_assert(std::is_base_of<UncompressedPattern, Head>::value,
            "The last pattern must always be derived from the uncompressed "
            "pattern.");

        static std::unique_ptr<Pattern>
        getPattern(const DictionaryEntry& bytes,
            const DictionaryEntry& dict_bytes, const int match_location)
        {
            return std::unique_ptr<Pattern>(new Head(bytes, match_location));
        }
    };

    /** The dictionary. */
    std::vector<DictionaryEntry> dictionary;

    /**
     * Since the factory cannot be instantiated here, classes that inherit
     * from this base class have to implement the call to their factory's
     * getPattern.
     */
    virtual std::unique_ptr<Pattern>
    getPattern(const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
        const int match_location) const = 0;

    /**
     * Compress data.
     *
     * @param data Data to be compressed.
     * @return The pattern this data matches.
     */
    std::unique_ptr<Pattern> compressValue(const T data);

    /**
     * Decompress a pattern into a value that fits in a dictionary entry.
     *
     * @param pattern The pattern to be decompressed.
     * @return The decompressed word.
     */
    T decompressValue(const Pattern* pattern);

    /** Clear all dictionary entries. */
    void resetDictionary();

    /**
     * Add an entry to the dictionary.
     *
     * @param data The new entry.
     */
    virtual void addToDictionary(const DictionaryEntry data) = 0;

    /**
     * Apply compression.
     *
     * @param data The cache line to be compressed.
     * @return Cache line after compression.
     */
    std::unique_ptr<BaseCacheCompressor::CompressionData> compress(
        const uint64_t* data);

    /**
     * Decompress data.
     *
     * @param comp_data Compressed cache line.
     * @param data The cache line to be decompressed.
     */
    void decompress(const CompressionData* comp_data, uint64_t* data) override;

    /**
     * Turn a value into a dictionary entry.
     *
     * @param value The value to turn.
     * @return A dictionary entry containing the value.
     */
    static DictionaryEntry toDictionaryEntry(T value);

    /**
     * Turn a dictionary entry into a value.
     *
     * @param The dictionary entry to turn.
     * @return The value that the dictionary entry contained.
     */
    static T fromDictionaryEntry(const DictionaryEntry& entry);

  public:
    typedef BaseDictionaryCompressorParams Params;
    DictionaryCompressor(const Params *p);
    ~DictionaryCompressor() = default;
};

/**
 * The compressed data is composed of multiple pattern entries. To add a new
 * pattern one should inherit from this class and implement isPattern() and
 * decompress(). Then the new pattern must be added to the PatternFactory
 * declaration in crescent order of size (in the DictionaryCompressor class).
 */
template <class T>
class DictionaryCompressor<T>::Pattern
{
  protected:
    /** Pattern enum number. */
    const int patternNumber;

    /** Code associated to the pattern. */
    const uint8_t code;

    /** Length, in bits, of the code and match location. */
    const uint8_t length;

    /** Number of unmatched bytes. */
    const uint8_t numUnmatchedBytes;

    /** Index representing the the match location. */
    const int matchLocation;

    /** Wether the pattern allocates a dictionary entry or not. */
    const bool allocate;

  public:
    /**
     * Default constructor.
     *
     * @param number Pattern number.
     * @param code Code associated to this pattern.
     * @param metadata_length Length, in bits, of the code and match location.
     * @param num_unmatched_bytes Number of unmatched bytes.
     * @param match_location Index of the match location.
     */
    Pattern(const int number, const uint64_t code,
            const uint64_t metadata_length, const uint64_t num_unmatched_bytes,
            const int match_location, const bool allocate = true)
        : patternNumber(number), code(code), length(metadata_length),
          numUnmatchedBytes(num_unmatched_bytes),
          matchLocation(match_location), allocate(allocate)
    {
    }

    /** Default destructor. */
    virtual ~Pattern() = default;

    /**
     * Get enum number associated to this pattern.
     *
     * @return The pattern enum number.
     */
    int getPatternNumber() const { return patternNumber; };

    /**
     * Get code of this pattern.
     *
     * @return The code.
     */
    uint8_t getCode() const { return code; }

    /**
     * Get the index of the dictionary match location.
     *
     * @return The index of the match location.
     */
    uint8_t getMatchLocation() const { return matchLocation; }

    /**
     * Get size, in bits, of the pattern (excluding prefix). Corresponds to
     * unmatched_data_size + code_length.
     *
     * @return The size.
     */
    std::size_t
    getSizeBits() const
    {
        return numUnmatchedBytes*CHAR_BIT + length;
    }

    /**
     * Determine if pattern allocates a dictionary entry.
     *
     * @return True if should allocate a dictionary entry.
     */
    bool shouldAllocate() const { return allocate; }

    /**
     * Extract pattern's information to a string.
     *
     * @return A string containing the relevant pattern metadata.
     */
    std::string
    print() const
    {
        return csprintf("pattern %s (encoding %x, size %u bits)",
                        getPatternNumber(), getCode(), getSizeBits());
    }

    /**
     * Decompress the pattern. Each pattern has its own way of interpreting
     * its data.
     *
     * @param dict_bytes The bytes in the corresponding matching entry.
     * @return The decompressed pattern.
     */
    virtual DictionaryEntry decompress(
        const DictionaryEntry dict_bytes) const = 0;
};

template <class T>
class DictionaryCompressor<T>::CompData : public CompressionData
{
  public:
    /** The patterns matched in the original line. */
    std::vector<std::unique_ptr<Pattern>> entries;

    CompData();
    ~CompData() = default;

    /**
     * Add a pattern entry to the list of patterns.
     *
     * @param entry The new pattern entry.
     */
    virtual void addEntry(std::unique_ptr<Pattern>);
};

/**
 * A pattern containing the original uncompressed data. This should be the
 * worst case of every pattern factory, where if all other patterns fail,
 * an instance of this pattern is created.
 */
template <class T>
class DictionaryCompressor<T>::UncompressedPattern
    : public DictionaryCompressor<T>::Pattern
{
  private:
    /** A copy of the original data. */
    const DictionaryEntry data;

  public:
    UncompressedPattern(const int number,
        const uint64_t code,
        const uint64_t metadata_length,
        const int match_location,
        const DictionaryEntry bytes)
      : DictionaryCompressor<T>::Pattern(number, code, metadata_length,
            sizeof(T), match_location, true),
        data(bytes)
    {
    }

    static bool
    isPattern(const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
        const int match_location)
    {
        // An entry can always be uncompressed
        return true;
    }

    DictionaryEntry
    decompress(const DictionaryEntry dict_bytes) const override
    {
        return data;
    }
};

/**
 * A pattern that compares masked values against dictionary entries. If
 * the masked dictionary entry matches perfectly the masked value to be
 * compressed, there is a pattern match.
 *
 * For example, if the mask is 0xFF00 (that is, this pattern matches the MSB),
 * the value (V) 0xFF20 is being compressed, and the dictionary contains
 * the value (D) 0xFF03, this is a match (V & mask == 0xFF00 == D & mask),
 * and 0x0020 is added to the list of unmatched bits.
 *
 * @tparam mask A mask containing the bits that must match.
 */
template <class T>
template <T mask>
class DictionaryCompressor<T>::MaskedPattern
    : public DictionaryCompressor<T>::Pattern
{
  private:
    static_assert(mask != 0, "The pattern's value mask must not be zero. Use "
        "the uncompressed pattern instead.");

    /** A copy of the bits that do not belong to the mask. */
    const T bits;

  public:
    MaskedPattern(const int number,
        const uint64_t code,
        const uint64_t metadata_length,
        const int match_location,
        const DictionaryEntry bytes,
        const bool allocate = true)
      : DictionaryCompressor<T>::Pattern(number, code, metadata_length,
            popCount(~mask) / 8, match_location, allocate),
        bits(DictionaryCompressor<T>::fromDictionaryEntry(bytes) & ~mask)
    {
    }

    static bool
    isPattern(const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
        const int match_location)
    {
        const T masked_bytes =
            DictionaryCompressor<T>::fromDictionaryEntry(bytes) & mask;
        const T masked_dict_bytes =
            DictionaryCompressor<T>::fromDictionaryEntry(dict_bytes) & mask;
        return (match_location >= 0) && (masked_bytes == masked_dict_bytes);
    }

    DictionaryEntry
    decompress(const DictionaryEntry dict_bytes) const override
    {
        const T masked_dict_bytes =
            DictionaryCompressor<T>::fromDictionaryEntry(dict_bytes) & mask;
        return DictionaryCompressor<T>::toDictionaryEntry(
            bits | masked_dict_bytes);
    }
};

/**
 * A pattern that compares masked values to a masked portion of a fixed value.
 * If all the masked bits match the provided non-dictionary value, there is a
 * pattern match.
 *
 * For example, assume the mask is 0xFF00 (that is, this pattern matches the
 * MSB), and we are searching for data containing only ones (i.e., the fixed
 * value is 0xFFFF).
 * If the value (V) 0xFF20 is being compressed, this is a match (V & mask ==
 * 0xFF00 == 0xFFFF & mask), and 0x20 is added to the list of unmatched bits.
 * If the value (V2) 0x0120 is being compressed, this is not a match
 * ((V2 & mask == 0x0100) != (0xFF00 == 0xFFFF & mask).
 *
 * @tparam value The value that is being matched against.
 * @tparam mask A mask containing the bits that must match the given value.
 */
template <class T>
template <T value, T mask>
class DictionaryCompressor<T>::MaskedValuePattern
    : public MaskedPattern<mask>
{
  private:
    static_assert(mask != 0, "The pattern's value mask must not be zero.");

  public:
    MaskedValuePattern(const int number,
        const uint64_t code,
        const uint64_t metadata_length,
        const int match_location,
        const DictionaryEntry bytes,
        const bool allocate = false)
      : MaskedPattern<mask>(number, code, metadata_length, match_location,
            bytes, allocate)
    {
    }

    static bool
    isPattern(const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
        const int match_location)
    {
        // Compare the masked fixed value to the value being checked for
        // patterns. Since the dictionary is not being used the match_location
        // is irrelevant.
        const T masked_bytes =
            DictionaryCompressor<T>::fromDictionaryEntry(bytes) & mask;
        return ((value & mask) == masked_bytes);
    }

    DictionaryEntry
    decompress(const DictionaryEntry dict_bytes) const override
    {
        return MaskedPattern<mask>::decompress(
            DictionaryCompressor<T>::toDictionaryEntry(value));
    }
};

#endif //__MEM_CACHE_COMPRESSORS_DICTIONARY_COMPRESSOR_HH__
<commit_msg>mem-cache: Inform unused bits instead of bytes in compressor pattern<commit_after>/*
 * Copyright (c) 2018-2019 Inria
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Daniel Carvalho
 */

/** @file
 * Definition of a dictionary based cache compressor. Each entry is compared
 * against a dictionary to search for matches.
 *
 * The dictionary is composed of 32-bit entries, and the comparison is done
 * byte per byte.
 *
 * The patterns are implemented as individual classes that have a checking
 * function isPattern(), to determine if the data fits the pattern, and a
 * decompress() function, which decompresses the contents of a pattern.
 * Every new pattern must inherit from the Pattern class and be added to the
 * patternFactory.
 */

#ifndef __MEM_CACHE_COMPRESSORS_DICTIONARY_COMPRESSOR_HH__
#define __MEM_CACHE_COMPRESSORS_DICTIONARY_COMPRESSOR_HH__

#include <array>
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <vector>

#include "base/types.hh"
#include "mem/cache/compressors/base.hh"

struct BaseDictionaryCompressorParams;

class BaseDictionaryCompressor : public BaseCacheCompressor
{
  protected:
    /** Dictionary size. */
    const std::size_t dictionarySize;

    /** Number of valid entries in the dictionary. */
    std::size_t numEntries;

    /**
     * @defgroup CompressionStats Compression specific statistics.
     * @{
     */

    /** Number of data entries that were compressed to each pattern. */
    Stats::Vector patternStats;

    /**
     * @}
     */

    /**
     * Trick function to get the number of patterns.
     *
     * @return The number of defined patterns.
     */
    virtual uint64_t getNumPatterns() const = 0;

    /**
     * Get meta-name assigned to the given pattern.
     *
     * @param number The number of the pattern.
     * @return The meta-name of the pattern.
     */
    virtual std::string getName(int number) const = 0;

  public:
    typedef BaseDictionaryCompressorParams Params;
    BaseDictionaryCompressor(const Params *p);
    ~BaseDictionaryCompressor() = default;

    void regStats() override;
};

/**
 * A template version of the dictionary compressor that allows to choose the
 * dictionary size.
 *
 * @tparam The type of a dictionary entry (e.g., uint16_t, uint32_t, etc).
 */
template <class T>
class DictionaryCompressor : public BaseDictionaryCompressor
{
  protected:
    /** Convenience typedef for a dictionary entry. */
    typedef std::array<uint8_t, sizeof(T)> DictionaryEntry;

    /**
     * Compression data for the dictionary compressor. It consists of a vector
     * of patterns.
     */
    class CompData;

    // Forward declaration of a pattern
    class Pattern;
    class UncompressedPattern;
    template <T mask>
    class MaskedPattern;
    template <T value, T mask>
    class MaskedValuePattern;

    /**
     * Create a factory to determine if input matches a pattern. The if else
     * chains are constructed by recursion. The patterns should be explored
     * sorted by size for correct behaviour.
     */
    template <class Head, class... Tail>
    struct Factory
    {
        static std::unique_ptr<Pattern> getPattern(
            const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
            const int match_location)
        {
            // If match this pattern, instantiate it. If a negative match
            // location is used, the patterns that use the dictionary bytes
            // must return false. This is used when there are no dictionary
            // entries yet
            if (Head::isPattern(bytes, dict_bytes, match_location)) {
                return std::unique_ptr<Pattern>(
                            new Head(bytes, match_location));
            // Otherwise, go for next pattern
            } else {
                return Factory<Tail...>::getPattern(bytes, dict_bytes,
                                                    match_location);
            }
        }
    };

    /**
     * Specialization to end the recursion. This must be called when all
     * other patterns failed, and there is no choice but to leave data
     * uncompressed. As such, this pattern must inherit from the uncompressed
     * pattern.
     */
    template <class Head>
    struct Factory<Head>
    {
        static_assert(std::is_base_of<UncompressedPattern, Head>::value,
            "The last pattern must always be derived from the uncompressed "
            "pattern.");

        static std::unique_ptr<Pattern>
        getPattern(const DictionaryEntry& bytes,
            const DictionaryEntry& dict_bytes, const int match_location)
        {
            return std::unique_ptr<Pattern>(new Head(bytes, match_location));
        }
    };

    /** The dictionary. */
    std::vector<DictionaryEntry> dictionary;

    /**
     * Since the factory cannot be instantiated here, classes that inherit
     * from this base class have to implement the call to their factory's
     * getPattern.
     */
    virtual std::unique_ptr<Pattern>
    getPattern(const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
        const int match_location) const = 0;

    /**
     * Compress data.
     *
     * @param data Data to be compressed.
     * @return The pattern this data matches.
     */
    std::unique_ptr<Pattern> compressValue(const T data);

    /**
     * Decompress a pattern into a value that fits in a dictionary entry.
     *
     * @param pattern The pattern to be decompressed.
     * @return The decompressed word.
     */
    T decompressValue(const Pattern* pattern);

    /** Clear all dictionary entries. */
    void resetDictionary();

    /**
     * Add an entry to the dictionary.
     *
     * @param data The new entry.
     */
    virtual void addToDictionary(const DictionaryEntry data) = 0;

    /**
     * Apply compression.
     *
     * @param data The cache line to be compressed.
     * @return Cache line after compression.
     */
    std::unique_ptr<BaseCacheCompressor::CompressionData> compress(
        const uint64_t* data);

    /**
     * Decompress data.
     *
     * @param comp_data Compressed cache line.
     * @param data The cache line to be decompressed.
     */
    void decompress(const CompressionData* comp_data, uint64_t* data) override;

    /**
     * Turn a value into a dictionary entry.
     *
     * @param value The value to turn.
     * @return A dictionary entry containing the value.
     */
    static DictionaryEntry toDictionaryEntry(T value);

    /**
     * Turn a dictionary entry into a value.
     *
     * @param The dictionary entry to turn.
     * @return The value that the dictionary entry contained.
     */
    static T fromDictionaryEntry(const DictionaryEntry& entry);

  public:
    typedef BaseDictionaryCompressorParams Params;
    DictionaryCompressor(const Params *p);
    ~DictionaryCompressor() = default;
};

/**
 * The compressed data is composed of multiple pattern entries. To add a new
 * pattern one should inherit from this class and implement isPattern() and
 * decompress(). Then the new pattern must be added to the PatternFactory
 * declaration in crescent order of size (in the DictionaryCompressor class).
 */
template <class T>
class DictionaryCompressor<T>::Pattern
{
  protected:
    /** Pattern enum number. */
    const int patternNumber;

    /** Code associated to the pattern. */
    const uint8_t code;

    /** Length, in bits, of the code and match location. */
    const uint8_t length;

    /** Number of unmatched bits. */
    const uint8_t numUnmatchedBits;

    /** Index representing the the match location. */
    const int matchLocation;

    /** Wether the pattern allocates a dictionary entry or not. */
    const bool allocate;

  public:
    /**
     * Default constructor.
     *
     * @param number Pattern number.
     * @param code Code associated to this pattern.
     * @param metadata_length Length, in bits, of the code and match location.
     * @param num_unmatched_bits Number of unmatched bits.
     * @param match_location Index of the match location.
     */
    Pattern(const int number, const uint64_t code,
            const uint64_t metadata_length, const uint64_t num_unmatched_bits,
            const int match_location, const bool allocate = true)
        : patternNumber(number), code(code), length(metadata_length),
          numUnmatchedBits(num_unmatched_bits),
          matchLocation(match_location), allocate(allocate)
    {
    }

    /** Default destructor. */
    virtual ~Pattern() = default;

    /**
     * Get enum number associated to this pattern.
     *
     * @return The pattern enum number.
     */
    int getPatternNumber() const { return patternNumber; };

    /**
     * Get code of this pattern.
     *
     * @return The code.
     */
    uint8_t getCode() const { return code; }

    /**
     * Get the index of the dictionary match location.
     *
     * @return The index of the match location.
     */
    uint8_t getMatchLocation() const { return matchLocation; }

    /**
     * Get size, in bits, of the pattern (excluding prefix). Corresponds to
     * unmatched_data_size + code_length.
     *
     * @return The size.
     */
    std::size_t
    getSizeBits() const
    {
        return numUnmatchedBits + length;
    }

    /**
     * Determine if pattern allocates a dictionary entry.
     *
     * @return True if should allocate a dictionary entry.
     */
    bool shouldAllocate() const { return allocate; }

    /**
     * Extract pattern's information to a string.
     *
     * @return A string containing the relevant pattern metadata.
     */
    std::string
    print() const
    {
        return csprintf("pattern %s (encoding %x, size %u bits)",
                        getPatternNumber(), getCode(), getSizeBits());
    }

    /**
     * Decompress the pattern. Each pattern has its own way of interpreting
     * its data.
     *
     * @param dict_bytes The bytes in the corresponding matching entry.
     * @return The decompressed pattern.
     */
    virtual DictionaryEntry decompress(
        const DictionaryEntry dict_bytes) const = 0;
};

template <class T>
class DictionaryCompressor<T>::CompData : public CompressionData
{
  public:
    /** The patterns matched in the original line. */
    std::vector<std::unique_ptr<Pattern>> entries;

    CompData();
    ~CompData() = default;

    /**
     * Add a pattern entry to the list of patterns.
     *
     * @param entry The new pattern entry.
     */
    virtual void addEntry(std::unique_ptr<Pattern>);
};

/**
 * A pattern containing the original uncompressed data. This should be the
 * worst case of every pattern factory, where if all other patterns fail,
 * an instance of this pattern is created.
 */
template <class T>
class DictionaryCompressor<T>::UncompressedPattern
    : public DictionaryCompressor<T>::Pattern
{
  private:
    /** A copy of the original data. */
    const DictionaryEntry data;

  public:
    UncompressedPattern(const int number,
        const uint64_t code,
        const uint64_t metadata_length,
        const int match_location,
        const DictionaryEntry bytes)
      : DictionaryCompressor<T>::Pattern(number, code, metadata_length,
            sizeof(T) * 8, match_location, true),
        data(bytes)
    {
    }

    static bool
    isPattern(const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
        const int match_location)
    {
        // An entry can always be uncompressed
        return true;
    }

    DictionaryEntry
    decompress(const DictionaryEntry dict_bytes) const override
    {
        return data;
    }
};

/**
 * A pattern that compares masked values against dictionary entries. If
 * the masked dictionary entry matches perfectly the masked value to be
 * compressed, there is a pattern match.
 *
 * For example, if the mask is 0xFF00 (that is, this pattern matches the MSB),
 * the value (V) 0xFF20 is being compressed, and the dictionary contains
 * the value (D) 0xFF03, this is a match (V & mask == 0xFF00 == D & mask),
 * and 0x0020 is added to the list of unmatched bits.
 *
 * @tparam mask A mask containing the bits that must match.
 */
template <class T>
template <T mask>
class DictionaryCompressor<T>::MaskedPattern
    : public DictionaryCompressor<T>::Pattern
{
  private:
    static_assert(mask != 0, "The pattern's value mask must not be zero. Use "
        "the uncompressed pattern instead.");

    /** A copy of the bits that do not belong to the mask. */
    const T bits;

  public:
    MaskedPattern(const int number,
        const uint64_t code,
        const uint64_t metadata_length,
        const int match_location,
        const DictionaryEntry bytes,
        const bool allocate = true)
      : DictionaryCompressor<T>::Pattern(number, code, metadata_length,
            popCount(~mask), match_location, allocate),
        bits(DictionaryCompressor<T>::fromDictionaryEntry(bytes) & ~mask)
    {
    }

    static bool
    isPattern(const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
        const int match_location)
    {
        const T masked_bytes =
            DictionaryCompressor<T>::fromDictionaryEntry(bytes) & mask;
        const T masked_dict_bytes =
            DictionaryCompressor<T>::fromDictionaryEntry(dict_bytes) & mask;
        return (match_location >= 0) && (masked_bytes == masked_dict_bytes);
    }

    DictionaryEntry
    decompress(const DictionaryEntry dict_bytes) const override
    {
        const T masked_dict_bytes =
            DictionaryCompressor<T>::fromDictionaryEntry(dict_bytes) & mask;
        return DictionaryCompressor<T>::toDictionaryEntry(
            bits | masked_dict_bytes);
    }
};

/**
 * A pattern that compares masked values to a masked portion of a fixed value.
 * If all the masked bits match the provided non-dictionary value, there is a
 * pattern match.
 *
 * For example, assume the mask is 0xFF00 (that is, this pattern matches the
 * MSB), and we are searching for data containing only ones (i.e., the fixed
 * value is 0xFFFF).
 * If the value (V) 0xFF20 is being compressed, this is a match (V & mask ==
 * 0xFF00 == 0xFFFF & mask), and 0x20 is added to the list of unmatched bits.
 * If the value (V2) 0x0120 is being compressed, this is not a match
 * ((V2 & mask == 0x0100) != (0xFF00 == 0xFFFF & mask).
 *
 * @tparam value The value that is being matched against.
 * @tparam mask A mask containing the bits that must match the given value.
 */
template <class T>
template <T value, T mask>
class DictionaryCompressor<T>::MaskedValuePattern
    : public MaskedPattern<mask>
{
  private:
    static_assert(mask != 0, "The pattern's value mask must not be zero.");

  public:
    MaskedValuePattern(const int number,
        const uint64_t code,
        const uint64_t metadata_length,
        const int match_location,
        const DictionaryEntry bytes,
        const bool allocate = false)
      : MaskedPattern<mask>(number, code, metadata_length, match_location,
            bytes, allocate)
    {
    }

    static bool
    isPattern(const DictionaryEntry& bytes, const DictionaryEntry& dict_bytes,
        const int match_location)
    {
        // Compare the masked fixed value to the value being checked for
        // patterns. Since the dictionary is not being used the match_location
        // is irrelevant.
        const T masked_bytes =
            DictionaryCompressor<T>::fromDictionaryEntry(bytes) & mask;
        return ((value & mask) == masked_bytes);
    }

    DictionaryEntry
    decompress(const DictionaryEntry dict_bytes) const override
    {
        return MaskedPattern<mask>::decompress(
            DictionaryCompressor<T>::toDictionaryEntry(value));
    }
};

#endif //__MEM_CACHE_COMPRESSORS_DICTIONARY_COMPRESSOR_HH__
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2009-2014 Mark D. Hill and David A. Wood
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __MEM_RUBY_SLICC_INTERFACE_ABSTRACTCONTROLLER_HH__
#define __MEM_RUBY_SLICC_INTERFACE_ABSTRACTCONTROLLER_HH__

#include <exception>
#include <iostream>
#include <string>

#include "base/callback.hh"
#include "mem/protocol/AccessPermission.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Consumer.hh"
#include "mem/ruby/common/DataBlock.hh"
#include "mem/ruby/common/Histogram.hh"
#include "mem/ruby/common/MachineID.hh"
#include "mem/ruby/network/MessageBuffer.hh"
#include "mem/ruby/network/Network.hh"
#include "mem/ruby/system/CacheRecorder.hh"
#include "mem/packet.hh"
#include "mem/qport.hh"
#include "params/RubyController.hh"
#include "mem/mem_object.hh"

class Network;

// used to communicate that an in_port peeked the wrong message type
class RejectException: public std::exception
{
    virtual const char* what() const throw()
    { return "Port rejected message based on type"; }
};

class AbstractController : public MemObject, public Consumer
{
  public:
    typedef RubyControllerParams Params;
    AbstractController(const Params *p);
    void init();
    const Params *params() const { return (const Params *)_params; }

    const NodeID getVersion() const { return m_machineID.getNum(); }
    const MachineType getType() const { return m_machineID.getType(); }

    void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }

    // return instance name
    void blockOnQueue(Addr, MessageBuffer*);
    void unblock(Addr);

    virtual MessageBuffer* getMandatoryQueue() const = 0;
    virtual MessageBuffer* getMemoryQueue() const = 0;
    virtual AccessPermission getAccessPermission(const Addr &addr) = 0;

    virtual void print(std::ostream & out) const = 0;
    virtual void wakeup() = 0;
    virtual void resetStats() = 0;
    virtual void regStats();

    virtual void recordCacheTrace(int cntrl, CacheRecorder* tr) = 0;
    virtual Sequencer* getSequencer() const = 0;

    //! These functions are used by ruby system to read/write the data blocks
    //! that exist with in the controller.
    virtual void functionalRead(const Addr &addr, PacketPtr) = 0;
    void functionalMemoryRead(PacketPtr);
    //! The return value indicates the number of messages written with the
    //! data from the packet.
    virtual int functionalWriteBuffers(PacketPtr&) = 0;
    virtual int functionalWrite(const Addr &addr, PacketPtr) = 0;
    int functionalMemoryWrite(PacketPtr);

    //! Function for enqueuing a prefetch request
    virtual void enqueuePrefetch(const Addr &, const RubyRequestType&)
    { fatal("Prefetches not implemented!");}

    //! Function for collating statistics from all the controllers of this
    //! particular type. This function should only be called from the
    //! version 0 of this controller type.
    virtual void collateStats()
    {fatal("collateStats() should be overridden!");}

    //! Initialize the message buffers.
    virtual void initNetQueues() = 0;

    /** A function used to return the port associated with this bus object. */
    BaseMasterPort& getMasterPort(const std::string& if_name,
                                  PortID idx = InvalidPortID);

    void queueMemoryRead(const MachineID &id, Addr addr, Cycles latency);
    void queueMemoryWrite(const MachineID &id, Addr addr, Cycles latency,
                          const DataBlock &block);
    void queueMemoryWritePartial(const MachineID &id, Addr addr, Cycles latency,
                                 const DataBlock &block, int size);
    void recvTimingResp(PacketPtr pkt);

  public:
    MachineID getMachineID() const { return m_machineID; }

    Stats::Histogram& getDelayHist() { return m_delayHistogram; }
    Stats::Histogram& getDelayVCHist(uint32_t index)
    { return *(m_delayVCHistogram[index]); }

  protected:
    //! Profiles original cache requests including PUTs
    void profileRequest(const std::string &request);
    //! Profiles the delay associated with messages.
    void profileMsgDelay(uint32_t virtualNetwork, Cycles delay);

    void stallBuffer(MessageBuffer* buf, Addr addr);
    void wakeUpBuffers(Addr addr);
    void wakeUpAllBuffers(Addr addr);
    void wakeUpAllBuffers();

  protected:
    NodeID m_version;
    MachineID m_machineID;
    NodeID m_clusterID;

    // MasterID used by some components of gem5.
    MasterID m_masterId;

    Network* m_net_ptr;
    bool m_is_blocking;
    std::map<Addr, MessageBuffer*> m_block_map;

    typedef std::vector<MessageBuffer*> MsgVecType;
    typedef std::set<MessageBuffer*> MsgBufType;
    typedef std::map<Addr, MsgVecType* > WaitingBufType;
    WaitingBufType m_waiting_buffers;

    unsigned int m_in_ports;
    unsigned int m_cur_in_port;
    int m_number_of_TBEs;
    int m_transitions_per_cycle;
    unsigned int m_buffer_size;
    Cycles m_recycle_latency;

    //! Counter for the number of cycles when the transitions carried out
    //! were equal to the maximum allowed
    Stats::Scalar m_fully_busy_cycles;

    //! Histogram for profiling delay for the messages this controller
    //! cares for
    Stats::Histogram m_delayHistogram;
    std::vector<Stats::Histogram *> m_delayVCHistogram;

    //! Callback class used for collating statistics from all the
    //! controller of this type.
    class StatsCallback : public Callback
    {
      private:
        AbstractController *ctr;

      public:
        virtual ~StatsCallback() {}
        StatsCallback(AbstractController *_ctr) : ctr(_ctr) {}
        void process() {ctr->collateStats();}
    };

    /**
     * Port that forwards requests and receives responses from the
     * memory controller.  It has a queue of packets not yet sent.
     */
    class MemoryPort : public QueuedMasterPort
    {
      private:
        // Packet queues used to store outgoing requests and snoop responses.
        ReqPacketQueue reqQueue;
        SnoopRespPacketQueue snoopRespQueue;

        // Controller that operates this port.
        AbstractController *controller;

      public:
        MemoryPort(const std::string &_name, AbstractController *_controller,
                   const std::string &_label);

        // Function for receiving a timing response from the peer port.
        // Currently the pkt is handed to the coherence controller
        // associated with this port.
        bool recvTimingResp(PacketPtr pkt);
    };

    /* Master port to the memory controller. */
    MemoryPort memoryPort;

    // State that is stored in packets sent to the memory controller.
    struct SenderState : public Packet::SenderState
    {
        // Id of the machine from which the request originated.
        MachineID id;

        SenderState(MachineID _id) : id(_id)
        {}
    };
};

#endif // __MEM_RUBY_SLICC_INTERFACE_ABSTRACTCONTROLLER_HH__
<commit_msg>ruby: abstract controller: mark some variables as const<commit_after>/*
 * Copyright (c) 2009-2014 Mark D. Hill and David A. Wood
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __MEM_RUBY_SLICC_INTERFACE_ABSTRACTCONTROLLER_HH__
#define __MEM_RUBY_SLICC_INTERFACE_ABSTRACTCONTROLLER_HH__

#include <exception>
#include <iostream>
#include <string>

#include "base/callback.hh"
#include "mem/protocol/AccessPermission.hh"
#include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/Consumer.hh"
#include "mem/ruby/common/DataBlock.hh"
#include "mem/ruby/common/Histogram.hh"
#include "mem/ruby/common/MachineID.hh"
#include "mem/ruby/network/MessageBuffer.hh"
#include "mem/ruby/network/Network.hh"
#include "mem/ruby/system/CacheRecorder.hh"
#include "mem/packet.hh"
#include "mem/qport.hh"
#include "params/RubyController.hh"
#include "mem/mem_object.hh"

class Network;

// used to communicate that an in_port peeked the wrong message type
class RejectException: public std::exception
{
    virtual const char* what() const throw()
    { return "Port rejected message based on type"; }
};

class AbstractController : public MemObject, public Consumer
{
  public:
    typedef RubyControllerParams Params;
    AbstractController(const Params *p);
    void init();
    const Params *params() const { return (const Params *)_params; }

    const NodeID getVersion() const { return m_machineID.getNum(); }
    const MachineType getType() const { return m_machineID.getType(); }

    void initNetworkPtr(Network* net_ptr) { m_net_ptr = net_ptr; }

    // return instance name
    void blockOnQueue(Addr, MessageBuffer*);
    void unblock(Addr);

    virtual MessageBuffer* getMandatoryQueue() const = 0;
    virtual MessageBuffer* getMemoryQueue() const = 0;
    virtual AccessPermission getAccessPermission(const Addr &addr) = 0;

    virtual void print(std::ostream & out) const = 0;
    virtual void wakeup() = 0;
    virtual void resetStats() = 0;
    virtual void regStats();

    virtual void recordCacheTrace(int cntrl, CacheRecorder* tr) = 0;
    virtual Sequencer* getSequencer() const = 0;

    //! These functions are used by ruby system to read/write the data blocks
    //! that exist with in the controller.
    virtual void functionalRead(const Addr &addr, PacketPtr) = 0;
    void functionalMemoryRead(PacketPtr);
    //! The return value indicates the number of messages written with the
    //! data from the packet.
    virtual int functionalWriteBuffers(PacketPtr&) = 0;
    virtual int functionalWrite(const Addr &addr, PacketPtr) = 0;
    int functionalMemoryWrite(PacketPtr);

    //! Function for enqueuing a prefetch request
    virtual void enqueuePrefetch(const Addr &, const RubyRequestType&)
    { fatal("Prefetches not implemented!");}

    //! Function for collating statistics from all the controllers of this
    //! particular type. This function should only be called from the
    //! version 0 of this controller type.
    virtual void collateStats()
    {fatal("collateStats() should be overridden!");}

    //! Initialize the message buffers.
    virtual void initNetQueues() = 0;

    /** A function used to return the port associated with this bus object. */
    BaseMasterPort& getMasterPort(const std::string& if_name,
                                  PortID idx = InvalidPortID);

    void queueMemoryRead(const MachineID &id, Addr addr, Cycles latency);
    void queueMemoryWrite(const MachineID &id, Addr addr, Cycles latency,
                          const DataBlock &block);
    void queueMemoryWritePartial(const MachineID &id, Addr addr, Cycles latency,
                                 const DataBlock &block, int size);
    void recvTimingResp(PacketPtr pkt);

  public:
    MachineID getMachineID() const { return m_machineID; }

    Stats::Histogram& getDelayHist() { return m_delayHistogram; }
    Stats::Histogram& getDelayVCHist(uint32_t index)
    { return *(m_delayVCHistogram[index]); }

  protected:
    //! Profiles original cache requests including PUTs
    void profileRequest(const std::string &request);
    //! Profiles the delay associated with messages.
    void profileMsgDelay(uint32_t virtualNetwork, Cycles delay);

    void stallBuffer(MessageBuffer* buf, Addr addr);
    void wakeUpBuffers(Addr addr);
    void wakeUpAllBuffers(Addr addr);
    void wakeUpAllBuffers();

  protected:
    const NodeID m_version;
    MachineID m_machineID;
    const NodeID m_clusterID;

    // MasterID used by some components of gem5.
    const MasterID m_masterId;

    Network *m_net_ptr;
    bool m_is_blocking;
    std::map<Addr, MessageBuffer*> m_block_map;

    typedef std::vector<MessageBuffer*> MsgVecType;
    typedef std::set<MessageBuffer*> MsgBufType;
    typedef std::map<Addr, MsgVecType* > WaitingBufType;
    WaitingBufType m_waiting_buffers;

    unsigned int m_in_ports;
    unsigned int m_cur_in_port;
    const int m_number_of_TBEs;
    const int m_transitions_per_cycle;
    const unsigned int m_buffer_size;
    Cycles m_recycle_latency;

    //! Counter for the number of cycles when the transitions carried out
    //! were equal to the maximum allowed
    Stats::Scalar m_fully_busy_cycles;

    //! Histogram for profiling delay for the messages this controller
    //! cares for
    Stats::Histogram m_delayHistogram;
    std::vector<Stats::Histogram *> m_delayVCHistogram;

    //! Callback class used for collating statistics from all the
    //! controller of this type.
    class StatsCallback : public Callback
    {
      private:
        AbstractController *ctr;

      public:
        virtual ~StatsCallback() {}
        StatsCallback(AbstractController *_ctr) : ctr(_ctr) {}
        void process() {ctr->collateStats();}
    };

    /**
     * Port that forwards requests and receives responses from the
     * memory controller.  It has a queue of packets not yet sent.
     */
    class MemoryPort : public QueuedMasterPort
    {
      private:
        // Packet queues used to store outgoing requests and snoop responses.
        ReqPacketQueue reqQueue;
        SnoopRespPacketQueue snoopRespQueue;

        // Controller that operates this port.
        AbstractController *controller;

      public:
        MemoryPort(const std::string &_name, AbstractController *_controller,
                   const std::string &_label);

        // Function for receiving a timing response from the peer port.
        // Currently the pkt is handed to the coherence controller
        // associated with this port.
        bool recvTimingResp(PacketPtr pkt);
    };

    /* Master port to the memory controller. */
    MemoryPort memoryPort;

    // State that is stored in packets sent to the memory controller.
    struct SenderState : public Packet::SenderState
    {
        // Id of the machine from which the request originated.
        MachineID id;

        SenderState(MachineID _id) : id(_id)
        {}
    };
};

#endif // __MEM_RUBY_SLICC_INTERFACE_ABSTRACTCONTROLLER_HH__
<|endoftext|>


<commit_before>#include "TKGenerator.h"
#include "TKCell.h"
#include "Utility.h"

#include <Aurora\Tools\ForEach.hpp>
#include <iostream>

TKGenerator::TKGenerator()
{

}

TKGenerator::~TKGenerator()
{
}

void TKGenerator::clearCells()
{
	Utility::clearPointerVector<TKCell>(&_cells);
	Utility::clearPointerVector<TKCell>(&_emptyCells);
	Utility::clearPointerVector<TKCell>(&_corridors);
	Utility::clearPointerVector<TKCell>(&_filteredCells);
}

void TKGenerator::initGenerator(int cellCount, int tileSize, int minSize, int maxSize, int minCellThreshold, int maxCellThreshold)
{
	_initialized = true;
	createCells(cellCount, tileSize, minSize, maxSize);
	_cellCount = cellCount;
	_tileSize = tileSize;

	_minCellThreshold = minCellThreshold;
	_maxCellThreshold = maxCellThreshold;
}

void TKGenerator::createCells(int cellCount, int tileSize, int minSize, int maxSize)
{
	clearCells();

	int x, y, width, height;

	TKCell* cell;

	float wRadius = 512;
	float hRadius = 380;

	for (int i = 0; i < cellCount; ++i)
	{
		x = Utility::randomFloatRange(wRadius - (wRadius / 2), wRadius + (wRadius / 2));
		x = ((x / tileSize) + 1) * tileSize;

		y = Utility::randomFloatRange(hRadius - (hRadius / 2), hRadius + (hRadius / 2));
		y = ((y / tileSize) + 1) * tileSize;

		width = Utility::randomIntRange(minSize, maxSize);
		height = Utility::randomIntRange(minSize, maxSize);

		cell = new TKCell(tileSize, x, y, width, height);

		_cells.push_back(cell);
	}
}

Map* TKGenerator::generate()
{
	_doSeparation = true;
	if (_initialized)
	{
		while (_doSeparation)
		{
			seperate();
		}
	}

	return nullptr;
}

void TKGenerator::seperate()
{
	if (!_doSeparation)
	{
		return;
	}

	bool overlap = false;
	for (auto& cell : _cells)
	{
		sf::Vector2f v;
		bool separate = computeSeparation(cell, v);

		if (separate)
		{
			overlap = true;
			v = cell->getCenter() + v;

			//cell->setPosition(v);
		}
	}

	if (!overlap)
	{
		std::cout << "DONE SEPARATING!" << std::endl;

		fillEmptySpace();
		filterCells(_cells, _minCellThreshold, _maxCellThreshold);
		delunayTriangulation();
		_doSeparation = false;
	}
}

bool TKGenerator::computeSeparation(TKCell* currCell, sf::Vector2f& outPos)
{
	bool ret = false;
	sf::Vector2f cellPos;
	sf::Vector2f otherPos;

	sf::Vector2f newPos;

	for (auto& other : _cells)
	{
		if (other != currCell)
		{
			if (currCell->intersects(*other))
			{
				float y = 0.0f;
				float x = 0.0f;

				cellPos = currCell->getCenter();
				otherPos = other->getCenter();

				float xDiff = otherPos.x - cellPos.x;
				float yDiff = otherPos.y - cellPos.y;

				float distSqr = xDiff*xDiff + yDiff*yDiff;

				x = xDiff < 0.0f ? x + _tileSize : x - _tileSize;
				y = yDiff < 0.0f ? y + _tileSize : y - _tileSize;

				//x -= ((int)xDiff % _tileSize) + xDiff;
				//y -= ((int)yDiff % _tileSize) + yDiff;

				newPos += sf::Vector2f(x, y);

				currCell->setPosition(currCell->getCenter() + newPos);

				ret = true;
			}
		}
	}

	return ret;
}

void TKGenerator::fillEmptySpace()
{
	int xMin = std::numeric_limits<int>::max();
	int xMax = std::numeric_limits<int>::min();
	int yMin = std::numeric_limits<int>::max();
	int yMax = std::numeric_limits<int>::min();

	for (auto& c : _cells)
	{
		float left = c->getLeft();
		float right = c->getRight();
		float top = c->getTop();
		float bottom = c->getBottom();

		if (left < xMin)
		{
			xMin = left;
		}
		if (right > xMax)
		{
			xMax = right;
		}
		if (top < yMin)
		{
			yMin = top;
		}
		if (bottom > yMax)
		{
			yMax = bottom;
		}
	}

	for (int x = xMin; x < xMax; x += _tileSize)
	{
		for (int y = yMin; y < yMax; y += _tileSize)
		{
			bool shouldAddCell = true;

			TKCell tmpCell(_tileSize, x, y, 1, 1);
			for (auto& c : _cells)
			{
				if (c->getLeft() <= x && c->getTop() <= y &&
					c->getRight() > x && c->getBottom() > y)
				{

					shouldAddCell = false;
					break;
				}
			}

			if (shouldAddCell)
			{
				TKCell* cell = new TKCell(_tileSize, x, y, 1, 1);
				cell->setTileOutlineColor(sf::Color(255, 0, 0, 50));
				_emptyCells.push_back(cell);
			}
		}
	}
}

void TKGenerator::filterCells(Cells& cells, int minThreshold, int maxThreshold)
{
	minThreshold *= _tileSize;
	maxThreshold *= _tileSize;
	Cells::iterator it;

	TKCell* cell;
	for (it = cells.begin(); it != cells.end();)
	{
		cell = *it;
		int width = cell->getWidth();
		int height = cell->getHeight();
		if (width < minThreshold || width > maxThreshold ||
			height < minThreshold || height > maxThreshold)
		{
			(*it)->setTileOutlineColor(sf::Color(255, 255, 0, 128));
			++it;
		}
		else
		{
			_filteredCells.push_back(*it);
			it = cells.erase(it);
		}
	}
}

void TKGenerator::delunayTriangulation()
{
	_vertices.clear();
	for (auto& cell : _filteredCells)
	{
		_vertices.push_back(cell->getCenter());
	}
	thor::triangulate(_vertices.begin(), _vertices.end(), std::back_inserter(_triangles));

	constructGraph();
	constructMST();
}

void TKGenerator::constructGraph()
{
	int n = _vertices.size();
	_graph.init(n);

	for (int i = 0; i < n; ++i)
	{
		sf::Vector2f v1 = _vertices[i];
		for (int j = 0; j < n; ++j)
		{
			if (i == j)
				continue;

			sf::Vector2f v2 = _vertices[j];
			for (auto& tri : _triangles)
			{
				sf::Vector2f tv1 = tri[0];
				sf::Vector2f tv2 = tri[1];
				sf::Vector2f tv3 = tri[2];

				if ((v1 == tv1 || v1 == tv2 || v1 == tv3) && (v2 == tv1 || v2 == tv2 || v2 == tv3))
				{
					_graph.addEdge(i, j);
				}
			}
		}
	}
}

int minKey(std::vector<int>& key, std::vector<int>& mstSet)
{
	int min = INT_MAX, minIndex;

	for (int i = 0; i < key.size(); ++i)
	{
		if (!mstSet[i] && key[i] < min)
		{
			min = key[i];
			minIndex = i;
		}
	}

	return minIndex;
}

void TKGenerator::constructMST()
{
	const int n = _vertices.size();

	std::vector<int> parent(n);
	std::vector<int> key(n);
	std::vector<int> mstSet(n);

	for (int i = 0; i < n; ++i)
	{
		key[i] = INT_MAX;
		mstSet[i] = false;
	}

	key[0] = 0;
	parent[0] = -1;

	for (int count = 0; count < n - 1; ++count)
	{
		int u = minKey(key, mstSet);

		mstSet[u] = true;

		for (int v = 0; v < n; ++v)
		{
			bool isEdge = _graph.isEdge(u, v);
			int edge = _graph.getEdge(u, v);
			if (isEdge && !mstSet[v] && edge < key[v])
			{
				parent[v] = u;
				key[v] = edge;
			}
		}
	}

	_mst.init(n);

	for (int i = 1; i < n; ++i)
	{
		_mst.addEdge(parent[i], i);
	}
}

void TKGenerator::createCorridors()
{
	std::vector<sf::Vector2f> connections;
	for (int i = 0; i < _mst.getVertexCount(); ++i)
	{
		for (int j = 0; j < _mst.getVertexCount(); ++j)
		{
			int vert = _mst.getEdge(i, j);
			if (vert > 0)
			{
				connections.push_back(_vertices[i]);
			}
		}
	}
}

void TKGenerator::render(sf::RenderWindow* rw)
{
	for (auto& cell : _emptyCells)
	{
		cell->render(rw);
	}
	for (auto& cell : _cells)
	{
		cell->render(rw);
	}
	for (auto& cell : _filteredCells)
	{
		cell->render(rw);
	}

	for (int i = 0; i < _mst.getVertexCount(); ++i)
	{
		for (int j = 0; j < _mst.getVertexCount(); ++j)
		{
			if (_mst.isEdge(i, j))
			{
				sf::VertexArray line(sf::PrimitiveType::Lines, 2);

				line[0] = _vertices[i];
				line[1] = _vertices[j];

				line[0].color = sf::Color::Green;
				line[1].color = sf::Color::Green;

				rw->draw(line);

			}
		}
	}

	AURORA_FOREACH(const thor::Triangle<sf::Vector2f>& triangle, _triangles)
	{
		sf::ConvexShape shape;
		shape.setPointCount(3);
		shape.setFillColor(sf::Color::Transparent);

		shape.setOutlineColor(sf::Color(0, 255, 0, 128));
		shape.setOutlineThickness(3.f);

		for (unsigned int i = 0; i < 3; ++i)
			shape.setPoint(i, triangle[i]);

		//rw->draw(shape);
	}
}
<commit_msg>Nothing important<commit_after>#include "TKGenerator.h"
#include "TKCell.h"
#include "Utility.h"

#include <Aurora\Tools\ForEach.hpp>
#include <iostream>

TKGenerator::TKGenerator()
{

}

TKGenerator::~TKGenerator()
{
}

void TKGenerator::clearCells()
{
	Utility::clearPointerVector<TKCell>(&_cells);
	Utility::clearPointerVector<TKCell>(&_emptyCells);
	Utility::clearPointerVector<TKCell>(&_corridors);
	Utility::clearPointerVector<TKCell>(&_filteredCells);
}

void TKGenerator::initGenerator(int cellCount, int tileSize, int minSize, int maxSize, int minCellThreshold, int maxCellThreshold)
{
	_initialized = true;
	createCells(cellCount, tileSize, minSize, maxSize);
	_cellCount = cellCount;
	_tileSize = tileSize;

	_minCellThreshold = minCellThreshold;
	_maxCellThreshold = maxCellThreshold;
}

void TKGenerator::createCells(int cellCount, int tileSize, int minSize, int maxSize)
{
	clearCells();

	int x, y, width, height;

	TKCell* cell;

	float wRadius = 512;
	float hRadius = 380;

	for (int i = 0; i < cellCount; ++i)
	{
		x = Utility::randomFloatRange(wRadius - (wRadius / 2), wRadius + (wRadius / 2));
		x = ((x / tileSize) + 1) * tileSize;

		y = Utility::randomFloatRange(hRadius - (hRadius / 2), hRadius + (hRadius / 2));
		y = ((y / tileSize) + 1) * tileSize;

		width = Utility::randomIntRange(minSize, maxSize);
		height = Utility::randomIntRange(minSize, maxSize);

		cell = new TKCell(tileSize, x, y, width, height);

		_cells.push_back(cell);
	}
}

Map* TKGenerator::generate()
{
	_doSeparation = true;
	if (_initialized)
	{
		while (_doSeparation)
		{
			seperate();
		}
	}

	return nullptr;
}

void TKGenerator::seperate()
{
	if (!_doSeparation)
	{
		return;
	}

	bool overlap = false;
	for (auto& cell : _cells)
	{
		sf::Vector2f v;
		bool separate = computeSeparation(cell, v);

		if (separate)
		{
			overlap = true;
			v = cell->getCenter() + v;

			//cell->setPosition(v);
		}
	}

	if (!overlap)
	{
		std::cout << "DONE SEPARATING!" << std::endl;

		fillEmptySpace();
		filterCells(_cells, _minCellThreshold, _maxCellThreshold);
		delunayTriangulation();
		_doSeparation = false;
	}
}

bool TKGenerator::computeSeparation(TKCell* currCell, sf::Vector2f& outPos)
{
	bool ret = false;
	sf::Vector2f cellPos;
	sf::Vector2f otherPos;

	sf::Vector2f newPos;

	for (auto& other : _cells)
	{
		if (other != currCell)
		{
			if (currCell->intersects(*other))
			{
				float y = 0.0f;
				float x = 0.0f;

				cellPos = currCell->getCenter();
				otherPos = other->getCenter();

				float xDiff = otherPos.x - cellPos.x;
				float yDiff = otherPos.y - cellPos.y;

				float distSqr = xDiff*xDiff + yDiff*yDiff;

				x = xDiff < 0.0f ? x + _tileSize : x - _tileSize;
				y = yDiff < 0.0f ? y + _tileSize : y - _tileSize;

				//x -= ((int)xDiff % _tileSize) + xDiff;
				//y -= ((int)yDiff % _tileSize) + yDiff;

				newPos += sf::Vector2f(x, y);

				currCell->setPosition(currCell->getCenter() + newPos);

				ret = true;
			}
		}
	}

	return ret;
}

void TKGenerator::fillEmptySpace()
{
	int xMin = std::numeric_limits<int>::max();
	int xMax = std::numeric_limits<int>::min();
	int yMin = std::numeric_limits<int>::max();
	int yMax = std::numeric_limits<int>::min();

	for (auto& c : _cells)
	{
		float left = c->getLeft();
		float right = c->getRight();
		float top = c->getTop();
		float bottom = c->getBottom();

		if (left < xMin)
		{
			xMin = left;
		}
		if (right > xMax)
		{
			xMax = right;
		}
		if (top < yMin)
		{
			yMin = top;
		}
		if (bottom > yMax)
		{
			yMax = bottom;
		}
	}

	for (int x = xMin; x < xMax; x += _tileSize)
	{
		for (int y = yMin; y < yMax; y += _tileSize)
		{
			bool shouldAddCell = true;

			TKCell tmpCell(_tileSize, x, y, 1, 1);
			for (auto& c : _cells)
			{
				if (c->getLeft() <= x && c->getTop() <= y &&
					c->getRight() > x && c->getBottom() > y)
				{

					shouldAddCell = false;
					break;
				}
			}

			if (shouldAddCell)
			{
				TKCell* cell = new TKCell(_tileSize, x, y, 1, 1);
				cell->setTileOutlineColor(sf::Color(255, 0, 0, 50));
				_emptyCells.push_back(cell);
			}
		}
	}
}

void TKGenerator::filterCells(Cells& cells, int minThreshold, int maxThreshold)
{
	minThreshold *= _tileSize;
	maxThreshold *= _tileSize;
	Cells::iterator it;

	TKCell* cell;
	for (it = cells.begin(); it != cells.end();)
	{
		cell = *it;
		int width = cell->getWidth();
		int height = cell->getHeight();
		if (width < minThreshold || width > maxThreshold ||
			height < minThreshold || height > maxThreshold)
		{
			(*it)->setTileOutlineColor(sf::Color(255, 255, 0, 128));
			++it;
		}
		else
		{
			_filteredCells.push_back(*it);
			it = cells.erase(it);
		}
	}
}

void TKGenerator::delunayTriangulation()
{
	_vertices.clear();
	for (auto& cell : _filteredCells)
	{
		_vertices.push_back(cell->getCenter());
	}
	thor::triangulate(_vertices.begin(), _vertices.end(), std::back_inserter(_triangles));

	constructGraph();
	constructMST();
}

void TKGenerator::constructGraph()
{
	int n = _vertices.size();
	_graph.init(n);

	for (int i = 0; i < n; ++i)
	{
		sf::Vector2f v1 = _vertices[i];
		for (int j = 0; j < n; ++j)
		{
			if (i == j)
				continue;

			sf::Vector2f v2 = _vertices[j];
			for (auto& tri : _triangles)
			{
				sf::Vector2f tv1 = tri[0];
				sf::Vector2f tv2 = tri[1];
				sf::Vector2f tv3 = tri[2];

				if ((v1 == tv1 || v1 == tv2 || v1 == tv3) && (v2 == tv1 || v2 == tv2 || v2 == tv3))
				{
					_graph.addEdge(i, j);
				}
			}
		}
	}
}

int minKey(std::vector<int>& key, std::vector<int>& mstSet)
{
	int min = INT_MAX, minIndex;

	for (int i = 0; i < key.size(); ++i)
	{
		if (!mstSet[i] && key[i] < min)
		{
			min = key[i];
			minIndex = i;
		}
	}

	return minIndex;
}

void TKGenerator::constructMST()
{
	const int n = _vertices.size();

	std::vector<int> parent(n);
	std::vector<int> key(n);
	std::vector<int> mstSet(n);

	for (int i = 0; i < n; ++i)
	{
		key[i] = INT_MAX;
		mstSet[i] = false;
	}

	key[0] = 0;
	parent[0] = -1;

	for (int count = 0; count < n - 1; ++count)
	{
		int u = minKey(key, mstSet);

		mstSet[u] = true;

		for (int v = 0; v < n; ++v)
		{
			bool isEdge = _graph.isEdge(u, v);
			int edge = _graph.getEdge(u, v);
			if (isEdge && !mstSet[v] && edge < key[v])
			{
				parent[v] = u;
				key[v] = edge;
			}
		}
	}

	_mst.init(n);

	for (int i = 1; i < n; ++i)
	{
		_mst.addEdge(parent[i], i);
	}
}

void TKGenerator::createCorridors()
{
	std::vector<sf::FloatRect> connections;
	for (int i = 0; i < _mst.getVertexCount(); ++i)
	{
		for (int j = 0; j < _mst.getVertexCount(); ++j)
		{
			int edge = _mst.getEdge(i, j);
			if (edge > 0)
			{
				sf::Vector2f diff = _vertices[i] - _vertices[j];
				float x = std::abs(diff.x);
				float y = std::abs(diff.y);

			}
		}
	}
}

void TKGenerator::render(sf::RenderWindow* rw)
{
	for (auto& cell : _emptyCells)
	{
		cell->render(rw);
	}
	for (auto& cell : _cells)
	{
		cell->render(rw);
	}
	for (auto& cell : _filteredCells)
	{
		cell->render(rw);
	}

	for (int i = 0; i < _mst.getVertexCount(); ++i)
	{
		for (int j = 0; j < _mst.getVertexCount(); ++j)
		{
			if (_mst.isEdge(i, j))
			{
				sf::VertexArray line(sf::PrimitiveType::Lines, 2);

				line[0] = _vertices[i];
				line[1] = _vertices[j];

				line[0].color = sf::Color::Green;
				line[1].color = sf::Color::Green;

				rw->draw(line);

			}
		}
	}

	AURORA_FOREACH(const thor::Triangle<sf::Vector2f>& triangle, _triangles)
	{
		sf::ConvexShape shape;
		shape.setPointCount(3);
		shape.setFillColor(sf::Color::Transparent);

		shape.setOutlineColor(sf::Color(0, 255, 0, 128));
		shape.setOutlineThickness(3.f);

		for (unsigned int i = 0; i < 3; ++i)
			shape.setPoint(i, triangle[i]);

		//rw->draw(shape);
	}
}
<|endoftext|>


<commit_before>//
//  yas_processing_math_modules.cpp
//

#include "yas_processing_math_modules.h"
#include "yas_processing_send_signal_processor.h"
#include "yas_processing_receive_signal_processor.h"
#include "yas_processing_module.h"
#include "yas_processing_signal_event.h"
#include "yas_fast_each.h"

using namespace yas;

namespace yas {
namespace processing {
    namespace math {
        struct context {
            signal_event left_signal;
            signal_event right_signal;
            time left_time;
            time right_time;

            context(signal_event &&left_signal, signal_event &&right_signal)
                : left_signal(std::move(left_signal)), right_signal(std::move(right_signal)) {
            }

            void reset() {
                left_signal.resize(0);
                right_signal.resize(0);
                left_time = nullptr;
                right_time = nullptr;
            }
        };

        using context_sptr = std::shared_ptr<context>;

        template <typename T>
        context_sptr make_context() {
            return std::make_shared<context>(make_signal_event<T>(0, reserved_signal_size),
                                             make_signal_event<T>(0, reserved_signal_size));
        }

        processor_f make_prepare_processor(context_sptr &context) {
            return [context](time::range const &, connector_map_t const &, connector_map_t const &, stream &) mutable {
                context->reset();
            };
        }

        template <typename T>
        processor_f make_receive_signal_processor(context_sptr const &context) {
            return processing::make_receive_signal_processor<T>(
                [context](processing::time::range const &time_range, channel_index_t const,
                          connector_index_t const con_idx, T const *const signal_ptr) mutable {
                    if (con_idx == to_connector_index(input_key::left_in)) {
                        context->left_time = time_range;
                        context->left_signal.copy_from(signal_ptr, time_range.length);
                    } else if (con_idx == to_connector_index(input_key::right_in)) {
                        context->right_time = time_range;
                        context->right_signal.copy_from(signal_ptr, time_range.length);
                    }
                });
        }
    }
}
}

template <typename T>
processing::module processing::math::make_plus_signal_module() {
    auto context = make_context<T>();

    auto prepare_processor = make_prepare_processor(context);

    auto receive_processor = make_receive_signal_processor<T>(context);

    auto send_processor = processing::make_send_signal_processor<T>(
        [context](processing::time::range const &time_range, channel_index_t const, connector_index_t const con_idx,
                  T *const signal_ptr) {
            if (con_idx == to_connector_index(output_key::out)) {
                auto out_each = make_fast_each(signal_ptr, time_range.length);
                processing::signal_event &left_signal = context->left_signal;
                processing::signal_event &right_signal = context->right_signal;
                auto const *left_ptr = left_signal.data<T>();
                auto const *right_ptr = right_signal.data<T>();
                processing::time const &left_time = context->left_time;
                processing::time const &right_time = context->right_time;
                auto const left_offset = left_time ? time_range.frame - left_time.get<time::range>().frame : 0;
                auto const right_offset = right_time ? time_range.frame - right_time.get<time::range>().frame : 0;
                auto const &left_length = left_time ? left_time.get<time::range>().length : 0;
                auto const &right_length = right_time ? right_time.get<time::range>().length : 0;

                while (yas_fast_each_next(out_each)) {
                    auto const &idx = yas_fast_each_index(out_each);
                    auto const left_idx = idx + left_offset;
                    auto const right_idx = idx + right_offset;
                    auto const &left_value = (left_idx >= 0 && left_idx < left_length) ? left_ptr[left_idx] : 0;
                    auto const &right_value = (right_idx >= 0 && right_idx < right_length) ? right_ptr[right_idx] : 0;
                    yas_fast_each_value(out_each) = left_value + right_value;
                }
            }
        });

    return processing::module{{std::move(prepare_processor), std::move(receive_processor), std::move(send_processor)}};
}

template processing::module processing::math::make_plus_signal_module<double>();
template processing::module processing::math::make_plus_signal_module<float>();
template processing::module processing::math::make_plus_signal_module<int64_t>();
template processing::module processing::math::make_plus_signal_module<int32_t>();
template processing::module processing::math::make_plus_signal_module<int16_t>();
template processing::module processing::math::make_plus_signal_module<int8_t>();
template processing::module processing::math::make_plus_signal_module<uint64_t>();
template processing::module processing::math::make_plus_signal_module<uint32_t>();
template processing::module processing::math::make_plus_signal_module<uint16_t>();
template processing::module processing::math::make_plus_signal_module<uint8_t>();

template <typename T>
processing::module processing::math::make_minus_signal_module() {
    auto context = make_context<T>();

    auto prepare_processor = make_prepare_processor(context);

    auto receive_processor = make_receive_signal_processor<T>(context);

    auto send_processor = processing::make_send_signal_processor<T>(
        [context](processing::time::range const &time_range, channel_index_t const, connector_index_t const key,
                  T *const signal_ptr) {
            if (key == to_connector_index(output_key::out)) {
                auto out_each = make_fast_each(signal_ptr, time_range.length);
                processing::signal_event &left_signal = context->left_signal;
                processing::signal_event &right_signal = context->right_signal;
                auto const *left_ptr = left_signal.data<T>();
                auto const *right_ptr = right_signal.data<T>();
                processing::time const &left_time = context->left_time;
                processing::time const &right_time = context->right_time;
                auto const left_offset = left_time ? time_range.frame - left_time.get<time::range>().frame : 0;
                auto const right_offset = right_time ? time_range.frame - right_time.get<time::range>().frame : 0;
                auto const &left_length = left_time ? left_time.get<time::range>().length : 0;
                auto const &right_length = right_time ? right_time.get<time::range>().length : 0;

                while (yas_fast_each_next(out_each)) {
                    auto const &idx = yas_fast_each_index(out_each);
                    auto const left_idx = idx + left_offset;
                    auto const right_idx = idx + right_offset;
                    auto const &left_value = (left_idx >= 0 && left_idx < left_length) ? left_ptr[left_idx] : 0;
                    auto const &right_value = (right_idx >= 0 && right_idx < right_length) ? right_ptr[right_idx] : 0;
                    yas_fast_each_value(out_each) = left_value - right_value;
                }
            }
        });

    return processing::module{{std::move(prepare_processor), std::move(receive_processor), std::move(send_processor)}};
}

template processing::module processing::math::make_minus_signal_module<double>();
template processing::module processing::math::make_minus_signal_module<float>();
template processing::module processing::math::make_minus_signal_module<int64_t>();
template processing::module processing::math::make_minus_signal_module<int32_t>();
template processing::module processing::math::make_minus_signal_module<int16_t>();
template processing::module processing::math::make_minus_signal_module<int8_t>();
template processing::module processing::math::make_minus_signal_module<uint64_t>();
template processing::module processing::math::make_minus_signal_module<uint32_t>();
template processing::module processing::math::make_minus_signal_module<uint16_t>();
template processing::module processing::math::make_minus_signal_module<uint8_t>();

template <typename T>
processing::module processing::math::make_multiply_signal_module() {
    auto context = make_context<T>();

    auto prepare_processor = make_prepare_processor(context);

    auto receive_processor = make_receive_signal_processor<T>(context);

    auto send_processor = processing::make_send_signal_processor<T>(
        [context](processing::time::range const &time_range, channel_index_t const, connector_index_t const con_idx,
                  T *const signal_ptr) {
            if (con_idx == to_connector_index(output_key::out)) {
                auto out_each = make_fast_each(signal_ptr, time_range.length);
                processing::signal_event &left_signal = context->left_signal;
                processing::signal_event &right_signal = context->right_signal;
                auto const *left_ptr = left_signal.data<T>();
                auto const *right_ptr = right_signal.data<T>();
                processing::time const &left_time = context->left_time;
                processing::time const &right_time = context->right_time;
                auto const left_offset = left_time ? time_range.frame - left_time.get<time::range>().frame : 0;
                auto const right_offset = right_time ? time_range.frame - right_time.get<time::range>().frame : 0;
                auto const &left_length = left_time ? left_time.get<time::range>().length : 0;
                auto const &right_length = right_time ? right_time.get<time::range>().length : 0;

                while (yas_fast_each_next(out_each)) {
                    auto const &idx = yas_fast_each_index(out_each);
                    auto const left_idx = idx + left_offset;
                    auto const right_idx = idx + right_offset;
                    auto const &left_value = (left_idx >= 0 && left_idx < left_length) ? left_ptr[left_idx] : 0;
                    auto const &right_value = (right_idx >= 0 && right_idx < right_length) ? right_ptr[right_idx] : 0;
                    yas_fast_each_value(out_each) = left_value * right_value;
                }
            }
        });

    return processing::module{{std::move(prepare_processor), std::move(receive_processor), std::move(send_processor)}};
}

template processing::module processing::math::make_multiply_signal_module<double>();
template processing::module processing::math::make_multiply_signal_module<float>();
template processing::module processing::math::make_multiply_signal_module<int64_t>();
template processing::module processing::math::make_multiply_signal_module<int32_t>();
template processing::module processing::math::make_multiply_signal_module<int16_t>();
template processing::module processing::math::make_multiply_signal_module<int8_t>();
template processing::module processing::math::make_multiply_signal_module<uint64_t>();
template processing::module processing::math::make_multiply_signal_module<uint32_t>();
template processing::module processing::math::make_multiply_signal_module<uint16_t>();
template processing::module processing::math::make_multiply_signal_module<uint8_t>();

template <typename T>
processing::module processing::math::make_divide_signal_module() {
    auto context = make_context<T>();

    auto prepare_processor = make_prepare_processor(context);

    auto receive_processor = make_receive_signal_processor<T>(context);

    auto send_processor = processing::make_send_signal_processor<T>([context](
        processing::time::range const &time_range, channel_index_t const, connector_index_t const con_idx,
        T *const signal_ptr) {
        if (con_idx == to_connector_index(output_key::out)) {
            auto out_each = make_fast_each(signal_ptr, time_range.length);
            processing::signal_event &left_signal = context->left_signal;
            processing::signal_event &right_signal = context->right_signal;
            auto const *left_ptr = left_signal.data<T>();
            auto const *right_ptr = right_signal.data<T>();
            processing::time const &left_time = context->left_time;
            processing::time const &right_time = context->right_time;
            auto const left_offset = left_time ? time_range.frame - left_time.get<time::range>().frame : 0;
            auto const right_offset = right_time ? time_range.frame - right_time.get<time::range>().frame : 0;
            auto const &left_length = left_time ? left_time.get<time::range>().length : 0;
            auto const &right_length = right_time ? right_time.get<time::range>().length : 0;

            while (yas_fast_each_next(out_each)) {
                auto const &idx = yas_fast_each_index(out_each);
                auto const left_idx = idx + left_offset;
                auto const right_idx = idx + right_offset;
                auto const &left_value = (left_idx >= 0 && left_idx < left_length) ? left_ptr[left_idx] : 0;
                auto const &right_value = (right_idx >= 0 && right_idx < right_length) ? right_ptr[right_idx] : 0;
                yas_fast_each_value(out_each) = (left_value == 0 || right_value == 0) ? 0 : left_value / right_value;
            }
        }
    });

    return processing::module{{std::move(prepare_processor), std::move(receive_processor), std::move(send_processor)}};
}

template processing::module processing::math::make_divide_signal_module<double>();
template processing::module processing::math::make_divide_signal_module<float>();
template processing::module processing::math::make_divide_signal_module<int64_t>();
template processing::module processing::math::make_divide_signal_module<int32_t>();
template processing::module processing::math::make_divide_signal_module<int16_t>();
template processing::module processing::math::make_divide_signal_module<int8_t>();
template processing::module processing::math::make_divide_signal_module<uint64_t>();
template processing::module processing::math::make_divide_signal_module<uint32_t>();
template processing::module processing::math::make_divide_signal_module<uint16_t>();
template processing::module processing::math::make_divide_signal_module<uint8_t>();
<commit_msg>update math module<commit_after>//
//  yas_processing_math_modules.cpp
//

#include "yas_processing_math_modules.h"
#include "yas_processing_send_signal_processor.h"
#include "yas_processing_receive_signal_processor.h"
#include "yas_processing_module.h"
#include "yas_processing_signal_event.h"
#include "yas_fast_each.h"

using namespace yas;

namespace yas {
namespace processing {
    namespace math {
        struct context {
            signal_event left_signal;
            signal_event right_signal;
            time left_time;
            time right_time;

            context(signal_event &&left_signal, signal_event &&right_signal)
                : left_signal(std::move(left_signal)), right_signal(std::move(right_signal)) {
            }

            void reset() {
                left_signal.resize(0);
                right_signal.resize(0);
                left_time = nullptr;
                right_time = nullptr;
            }
        };

        using context_sptr = std::shared_ptr<context>;

        template <typename T>
        context_sptr make_context() {
            return std::make_shared<context>(make_signal_event<T>(0, reserved_signal_size),
                                             make_signal_event<T>(0, reserved_signal_size));
        }

        processor_f make_prepare_processor(context_sptr &context) {
            return [context](time::range const &, connector_map_t const &, connector_map_t const &, stream &) mutable {
                context->reset();
            };
        }

        template <typename T>
        processor_f make_receive_signal_processor(context_sptr const &context) {
            return processing::make_receive_signal_processor<T>(
                [context](processing::time::range const &time_range, channel_index_t const,
                          connector_index_t const con_idx, T const *const signal_ptr) mutable {
                    if (con_idx == to_connector_index(input_key::left_in)) {
                        context->left_time = time_range;
                        context->left_signal.copy_from(signal_ptr, time_range.length);
                    } else if (con_idx == to_connector_index(input_key::right_in)) {
                        context->right_time = time_range;
                        context->right_signal.copy_from(signal_ptr, time_range.length);
                    }
                });
        }
    }
}
}

template <typename T>
processing::module processing::math::make_plus_signal_module() {
    auto context = make_context<T>();

    auto prepare_processor = make_prepare_processor(context);

    auto receive_processor = make_receive_signal_processor<T>(context);

    auto send_processor = processing::make_send_signal_processor<T>(
        [context](processing::time::range const &time_range, channel_index_t const, connector_index_t const con_idx,
                  T *const signal_ptr) {
            if (con_idx == to_connector_index(output_key::out)) {
                auto out_each = make_fast_each(signal_ptr, time_range.length);
                processing::signal_event &left_signal = context->left_signal;
                processing::signal_event &right_signal = context->right_signal;
                auto const *left_ptr = left_signal.data<T>();
                auto const *right_ptr = right_signal.data<T>();
                processing::time const &left_time = context->left_time;
                processing::time const &right_time = context->right_time;
                auto const left_offset = left_time ? time_range.frame - left_time.get<time::range>().frame : 0;
                auto const right_offset = right_time ? time_range.frame - right_time.get<time::range>().frame : 0;
                auto const &left_length = left_time ? left_time.get<time::range>().length : zero_length;
                auto const &right_length = right_time ? right_time.get<time::range>().length : zero_length;

                while (yas_fast_each_next(out_each)) {
                    auto const &idx = yas_fast_each_index(out_each);
                    auto const left_idx = idx + left_offset;
                    auto const right_idx = idx + right_offset;
                    auto const &left_value = (left_idx >= 0 && left_idx < left_length) ? left_ptr[left_idx] : 0;
                    auto const &right_value = (right_idx >= 0 && right_idx < right_length) ? right_ptr[right_idx] : 0;
                    yas_fast_each_value(out_each) = left_value + right_value;
                }
            }
        });

    return processing::module{{std::move(prepare_processor), std::move(receive_processor), std::move(send_processor)}};
}

template processing::module processing::math::make_plus_signal_module<double>();
template processing::module processing::math::make_plus_signal_module<float>();
template processing::module processing::math::make_plus_signal_module<int64_t>();
template processing::module processing::math::make_plus_signal_module<int32_t>();
template processing::module processing::math::make_plus_signal_module<int16_t>();
template processing::module processing::math::make_plus_signal_module<int8_t>();
template processing::module processing::math::make_plus_signal_module<uint64_t>();
template processing::module processing::math::make_plus_signal_module<uint32_t>();
template processing::module processing::math::make_plus_signal_module<uint16_t>();
template processing::module processing::math::make_plus_signal_module<uint8_t>();

template <typename T>
processing::module processing::math::make_minus_signal_module() {
    auto context = make_context<T>();

    auto prepare_processor = make_prepare_processor(context);

    auto receive_processor = make_receive_signal_processor<T>(context);

    auto send_processor = processing::make_send_signal_processor<T>(
        [context](processing::time::range const &time_range, channel_index_t const, connector_index_t const key,
                  T *const signal_ptr) {
            if (key == to_connector_index(output_key::out)) {
                auto out_each = make_fast_each(signal_ptr, time_range.length);
                processing::signal_event &left_signal = context->left_signal;
                processing::signal_event &right_signal = context->right_signal;
                auto const *left_ptr = left_signal.data<T>();
                auto const *right_ptr = right_signal.data<T>();
                processing::time const &left_time = context->left_time;
                processing::time const &right_time = context->right_time;
                auto const left_offset = left_time ? time_range.frame - left_time.get<time::range>().frame : 0;
                auto const right_offset = right_time ? time_range.frame - right_time.get<time::range>().frame : 0;
                auto const &left_length = left_time ? left_time.get<time::range>().length : zero_length;
                auto const &right_length = right_time ? right_time.get<time::range>().length : zero_length;

                while (yas_fast_each_next(out_each)) {
                    auto const &idx = yas_fast_each_index(out_each);
                    auto const left_idx = idx + left_offset;
                    auto const right_idx = idx + right_offset;
                    auto const &left_value = (left_idx >= 0 && left_idx < left_length) ? left_ptr[left_idx] : 0;
                    auto const &right_value = (right_idx >= 0 && right_idx < right_length) ? right_ptr[right_idx] : 0;
                    yas_fast_each_value(out_each) = left_value - right_value;
                }
            }
        });

    return processing::module{{std::move(prepare_processor), std::move(receive_processor), std::move(send_processor)}};
}

template processing::module processing::math::make_minus_signal_module<double>();
template processing::module processing::math::make_minus_signal_module<float>();
template processing::module processing::math::make_minus_signal_module<int64_t>();
template processing::module processing::math::make_minus_signal_module<int32_t>();
template processing::module processing::math::make_minus_signal_module<int16_t>();
template processing::module processing::math::make_minus_signal_module<int8_t>();
template processing::module processing::math::make_minus_signal_module<uint64_t>();
template processing::module processing::math::make_minus_signal_module<uint32_t>();
template processing::module processing::math::make_minus_signal_module<uint16_t>();
template processing::module processing::math::make_minus_signal_module<uint8_t>();

template <typename T>
processing::module processing::math::make_multiply_signal_module() {
    auto context = make_context<T>();

    auto prepare_processor = make_prepare_processor(context);

    auto receive_processor = make_receive_signal_processor<T>(context);

    auto send_processor = processing::make_send_signal_processor<T>(
        [context](processing::time::range const &time_range, channel_index_t const, connector_index_t const con_idx,
                  T *const signal_ptr) {
            if (con_idx == to_connector_index(output_key::out)) {
                auto out_each = make_fast_each(signal_ptr, time_range.length);
                processing::signal_event &left_signal = context->left_signal;
                processing::signal_event &right_signal = context->right_signal;
                auto const *left_ptr = left_signal.data<T>();
                auto const *right_ptr = right_signal.data<T>();
                processing::time const &left_time = context->left_time;
                processing::time const &right_time = context->right_time;
                auto const left_offset = left_time ? time_range.frame - left_time.get<time::range>().frame : 0;
                auto const right_offset = right_time ? time_range.frame - right_time.get<time::range>().frame : 0;
                auto const &left_length = left_time ? left_time.get<time::range>().length : zero_length;
                auto const &right_length = right_time ? right_time.get<time::range>().length : zero_length;

                while (yas_fast_each_next(out_each)) {
                    auto const &idx = yas_fast_each_index(out_each);
                    auto const left_idx = idx + left_offset;
                    auto const right_idx = idx + right_offset;
                    auto const &left_value = (left_idx >= 0 && left_idx < left_length) ? left_ptr[left_idx] : 0;
                    auto const &right_value = (right_idx >= 0 && right_idx < right_length) ? right_ptr[right_idx] : 0;
                    yas_fast_each_value(out_each) = left_value * right_value;
                }
            }
        });

    return processing::module{{std::move(prepare_processor), std::move(receive_processor), std::move(send_processor)}};
}

template processing::module processing::math::make_multiply_signal_module<double>();
template processing::module processing::math::make_multiply_signal_module<float>();
template processing::module processing::math::make_multiply_signal_module<int64_t>();
template processing::module processing::math::make_multiply_signal_module<int32_t>();
template processing::module processing::math::make_multiply_signal_module<int16_t>();
template processing::module processing::math::make_multiply_signal_module<int8_t>();
template processing::module processing::math::make_multiply_signal_module<uint64_t>();
template processing::module processing::math::make_multiply_signal_module<uint32_t>();
template processing::module processing::math::make_multiply_signal_module<uint16_t>();
template processing::module processing::math::make_multiply_signal_module<uint8_t>();

template <typename T>
processing::module processing::math::make_divide_signal_module() {
    auto context = make_context<T>();

    auto prepare_processor = make_prepare_processor(context);

    auto receive_processor = make_receive_signal_processor<T>(context);

    auto send_processor = processing::make_send_signal_processor<T>([context](
        processing::time::range const &time_range, channel_index_t const, connector_index_t const con_idx,
        T *const signal_ptr) {
        if (con_idx == to_connector_index(output_key::out)) {
            auto out_each = make_fast_each(signal_ptr, time_range.length);
            processing::signal_event &left_signal = context->left_signal;
            processing::signal_event &right_signal = context->right_signal;
            auto const *left_ptr = left_signal.data<T>();
            auto const *right_ptr = right_signal.data<T>();
            processing::time const &left_time = context->left_time;
            processing::time const &right_time = context->right_time;
            auto const left_offset = left_time ? time_range.frame - left_time.get<time::range>().frame : 0;
            auto const right_offset = right_time ? time_range.frame - right_time.get<time::range>().frame : 0;
            auto const &left_length = left_time ? left_time.get<time::range>().length : zero_length;
            auto const &right_length = right_time ? right_time.get<time::range>().length : zero_length;

            while (yas_fast_each_next(out_each)) {
                auto const &idx = yas_fast_each_index(out_each);
                auto const left_idx = idx + left_offset;
                auto const right_idx = idx + right_offset;
                auto const &left_value = (left_idx >= 0 && left_idx < left_length) ? left_ptr[left_idx] : 0;
                auto const &right_value = (right_idx >= 0 && right_idx < right_length) ? right_ptr[right_idx] : 0;
                yas_fast_each_value(out_each) = (left_value == 0 || right_value == 0) ? 0 : left_value / right_value;
            }
        }
    });

    return processing::module{{std::move(prepare_processor), std::move(receive_processor), std::move(send_processor)}};
}

template processing::module processing::math::make_divide_signal_module<double>();
template processing::module processing::math::make_divide_signal_module<float>();
template processing::module processing::math::make_divide_signal_module<int64_t>();
template processing::module processing::math::make_divide_signal_module<int32_t>();
template processing::module processing::math::make_divide_signal_module<int16_t>();
template processing::module processing::math::make_divide_signal_module<int8_t>();
template processing::module processing::math::make_divide_signal_module<uint64_t>();
template processing::module processing::math::make_divide_signal_module<uint32_t>();
template processing::module processing::math::make_divide_signal_module<uint16_t>();
template processing::module processing::math::make_divide_signal_module<uint8_t>();
<|endoftext|>