<commit_before>/**
Copyright (c) 2016, Ubiquity Robotics
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 ubiquity_motor 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 HOLDER 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.
**/

#include <gtest/gtest.h>

#include <ubiquity_motor/motor_serial.h>
#include <ubiquity_motor/motor_message.h>
#include <ros/ros.h>

#include <string>

#if defined(__linux__)
#include <pty.h>
#else
#include <util.h>
#endif

class MotorSerialTests : public ::testing::Test {
protected:
  virtual void SetUp() {
    if (openpty(&master_fd, &slave_fd, name, NULL, NULL) == -1) {
      perror("openpty");
      exit(127);
    }

    ASSERT_TRUE(master_fd > 0);
    ASSERT_TRUE(slave_fd > 0);
    ASSERT_TRUE(std::string(name).length() > 0);

    ros::Time::init();
    motors = new MotorSerial(std::string(name), 9600, 1000);
  }

  virtual void TearDown() {
    delete motors;
  }

  MotorSerial * motors;
  int master_fd;
  int slave_fd;
  char name[100];
};

TEST_F(MotorSerialTests, goodreadWorks){
  uint8_t test[]= {0x7E, 0x02, 0xBB, 0x07, 0x00, 0x00, 0x01, 0x2C, 0x0E};
  //char test[]= {0x0E, 0x2C, 0x01, 0x00, 0x00, 0x07, 0xBB, 0x02, 0x7E};
  write(master_fd, test, 9);

  while(!motors->commandAvailable()) {
  }

  MotorMessage mm;
  mm = motors-> receiveCommand();
  ASSERT_EQ(300, mm.getData());
  ASSERT_EQ(MotorMessage::TYPE_WRITE, mm.getType());
  ASSERT_EQ(MotorMessage::REG_LEFT_SPEED_SET, mm.getRegister());
}

TEST_F(MotorSerialTests, badreadFails){
  uint8_t test[]= {0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  //uint8_t test[]= {0x7E, 0x02, 0xBB, 0x07, 0x00, 0x00, 0x01, 0x2C, 0x0E};
  write(master_fd, test, 9);

  ros::Rate loop(10);
  int times = 0;
  while(!motors->commandAvailable()) {
    loop.sleep();
    times++;
    if(times >= 20) {
      break;
    }
  }

  if(times >= 20) {
      SUCCEED();
  }
  else {
    FAIL();
  }
}

TEST_F(MotorSerialTests, writeWorks) {
	MotorMessage version;
	version.setRegister(MotorMessage::REG_FIRMWARE_VERSION);
	version.setType(MotorMessage::TYPE_READ);
	version.setData(0);
	motors->transmitCommand(version);

  uint8_t arr[9];
  read(master_fd, arr, 9);

  std::vector<uint8_t> input(arr, arr + sizeof(arr)/ sizeof(uint8_t));

  ASSERT_EQ(input, version.serialize());
}

TEST_F(MotorSerialTests, writeMultipleWorks) {
  std::vector<MotorMessage> commands;

  MotorMessage left_odom;
  left_odom.setRegister(MotorMessage::REG_LEFT_ODOM);
  left_odom.setType(MotorMessage::TYPE_READ);
  left_odom.setData(0);
  commands.push_back(left_odom);

  MotorMessage right_odom;
  right_odom.setRegister(MotorMessage::REG_RIGHT_ODOM);
  right_odom.setType(MotorMessage::TYPE_READ);
  right_odom.setData(0);
  commands.push_back(right_odom);

  MotorMessage left_vel;
  left_vel.setRegister(MotorMessage::REG_LEFT_SPEED_MEASURED);
  left_vel.setType(MotorMessage::TYPE_READ);
  left_vel.setData(0);
  commands.push_back(left_vel);

  MotorMessage right_vel;
  right_vel.setRegister(MotorMessage::REG_RIGHT_SPEED_MEASURED);
  right_vel.setType(MotorMessage::TYPE_READ);
  right_vel.setData(0);
  commands.push_back(right_vel);

  motors->transmitCommands(commands);

  uint8_t arr[36];
  read(master_fd, arr, 36);
  std::vector<uint8_t> input(arr, arr + sizeof(arr)/ sizeof(uint8_t));

  std::vector<uint8_t> expected(0);
  for (std::vector<MotorMessage>::iterator i = commands.begin(); i != commands.end(); ++i){
   std::vector<uint8_t> serialized = i->serialize();
   expected.insert(expected.end(), serialized.begin(), serialized.end());
  }

  ASSERT_EQ(expected, input);
}

int main(int argc, char **argv){
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}<commit_msg>Added misaligned test cases<commit_after>/**
Copyright (c) 2016, Ubiquity Robotics
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 ubiquity_motor 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 HOLDER 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.
**/

#include <gtest/gtest.h>

#include <ubiquity_motor/motor_serial.h>
#include <ubiquity_motor/motor_message.h>
#include <ros/ros.h>

#include <string>

#if defined(__linux__)
#include <pty.h>
#else
#include <util.h>
#endif

class MotorSerialTests : public ::testing::Test {
protected:
  virtual void SetUp() {
    if (openpty(&master_fd, &slave_fd, name, NULL, NULL) == -1) {
      perror("openpty");
      exit(127);
    }

    ASSERT_TRUE(master_fd > 0);
    ASSERT_TRUE(slave_fd > 0);
    ASSERT_TRUE(std::string(name).length() > 0);

    ros::Time::init();
    motors = new MotorSerial(std::string(name), 9600, 1000);
  }

  virtual void TearDown() {
    delete motors;
  }

  MotorSerial * motors;
  int master_fd;
  int slave_fd;
  char name[100];
};

TEST_F(MotorSerialTests, goodReadWorks){
  uint8_t test[]= {0x7E, 0x02, 0xBB, 0x07, 0x00, 0x00, 0x01, 0x2C, 0x0E};
  //char test[]= {0x0E, 0x2C, 0x01, 0x00, 0x00, 0x07, 0xBB, 0x02, 0x7E};
  write(master_fd, test, 9);

  while(!motors->commandAvailable()) {
  }

  MotorMessage mm;
  mm = motors-> receiveCommand();
  ASSERT_EQ(300, mm.getData());
  ASSERT_EQ(MotorMessage::TYPE_WRITE, mm.getType());
  ASSERT_EQ(MotorMessage::REG_LEFT_SPEED_SET, mm.getRegister());
}

TEST_F(MotorSerialTests, misalignedOneGoodReadWorks){
  uint8_t test[]= {0x00, 0x7E, 0x02, 0xBB, 0x07, 0x00, 0x00, 0x01, 0x2C, 0x0E};
  //char test[]= {0x0E, 0x2C, 0x01, 0x00, 0x00, 0x07, 0xBB, 0x02, 0x7E};
  write(master_fd, test, 10);

  while(!motors->commandAvailable()) {
  }

  MotorMessage mm;
  mm = motors-> receiveCommand();
  ASSERT_EQ(300, mm.getData());
  ASSERT_EQ(MotorMessage::TYPE_WRITE, mm.getType());
  ASSERT_EQ(MotorMessage::REG_LEFT_SPEED_SET, mm.getRegister());
}

TEST_F(MotorSerialTests, misalignedManyGoodReadWorks){
  uint8_t test[]= {0x01, 0x2C, 0x0E, 0x7E, 0x02, 0xBB, 0x07, 0x00, 0x00, 0x01, 0x2C, 0x0E};
  //char test[]= {0x0E, 0x2C, 0x01, 0x00, 0x00, 0x07, 0xBB, 0x02, 0x7E};
  write(master_fd, test, 12);

  while(!motors->commandAvailable()) {
  }

  MotorMessage mm;
  mm = motors-> receiveCommand();
  ASSERT_EQ(300, mm.getData());
  ASSERT_EQ(MotorMessage::TYPE_WRITE, mm.getType());
  ASSERT_EQ(MotorMessage::REG_LEFT_SPEED_SET, mm.getRegister());
}

TEST_F(MotorSerialTests, badReadFails){
  uint8_t test[]= {0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  //uint8_t test[]= {0x7E, 0x02, 0xBB, 0x07, 0x00, 0x00, 0x01, 0x2C, 0x0E};
  write(master_fd, test, 9);

  ros::Rate loop(10);
  int times = 0;
  while(!motors->commandAvailable()) {
    loop.sleep();
    times++;
    if(times >= 20) {
      break;
    }
  }

  if(times >= 20) {
      SUCCEED();
  }
  else {
    FAIL();
  }
}

TEST_F(MotorSerialTests, writeWorks) {
	MotorMessage version;
	version.setRegister(MotorMessage::REG_FIRMWARE_VERSION);
	version.setType(MotorMessage::TYPE_READ);
	version.setData(0);
	motors->transmitCommand(version);

  uint8_t arr[9];
  read(master_fd, arr, 9);

  std::vector<uint8_t> input(arr, arr + sizeof(arr)/ sizeof(uint8_t));

  ASSERT_EQ(input, version.serialize());
}

TEST_F(MotorSerialTests, writeMultipleWorks) {
  std::vector<MotorMessage> commands;

  MotorMessage left_odom;
  left_odom.setRegister(MotorMessage::REG_LEFT_ODOM);
  left_odom.setType(MotorMessage::TYPE_READ);
  left_odom.setData(0);
  commands.push_back(left_odom);

  MotorMessage right_odom;
  right_odom.setRegister(MotorMessage::REG_RIGHT_ODOM);
  right_odom.setType(MotorMessage::TYPE_READ);
  right_odom.setData(0);
  commands.push_back(right_odom);

  MotorMessage left_vel;
  left_vel.setRegister(MotorMessage::REG_LEFT_SPEED_MEASURED);
  left_vel.setType(MotorMessage::TYPE_READ);
  left_vel.setData(0);
  commands.push_back(left_vel);

  MotorMessage right_vel;
  right_vel.setRegister(MotorMessage::REG_RIGHT_SPEED_MEASURED);
  right_vel.setType(MotorMessage::TYPE_READ);
  right_vel.setData(0);
  commands.push_back(right_vel);

  motors->transmitCommands(commands);

  uint8_t arr[36];
  read(master_fd, arr, 36);
  std::vector<uint8_t> input(arr, arr + sizeof(arr)/ sizeof(uint8_t));

  std::vector<uint8_t> expected(0);
  for (std::vector<MotorMessage>::iterator i = commands.begin(); i != commands.end(); ++i){
   std::vector<uint8_t> serialized = i->serialize();
   expected.insert(expected.end(), serialized.begin(), serialized.end());
  }

  ASSERT_EQ(expected, input);
}

int main(int argc, char **argv){
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016 Intel Corporation. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "i965_test_fixture.h"
#include "i965_jpeg_test_data.h"

#include <iomanip>
#include <iostream>
#include <numeric>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>

namespace JPEG {

class JPEGDecodeTest : public I965TestFixture
{
protected:
    static const VAEntrypoint entrypoint = VAEntrypointVLD;
};

TEST_F(JPEGDecodeTest, Entrypoint)
{
    VAConfigID config = VA_INVALID_ID;
    ConfigAttribs attributes;
    struct i965_driver_data *i965(*this);

    ASSERT_PTR(i965);

    if (HAS_JPEG_DECODING(i965)) {
        config = createConfig(profile, entrypoint, attributes);
    } else {
        VAStatus status = i965_CreateConfig(
            *this, profile, entrypoint, attributes.data(), attributes.size(),
            &config);
        EXPECT_STATUS_EQ(VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT, status);
        EXPECT_INVALID_ID(config);
    }

    if (config != VA_INVALID_ID)
        destroyConfig(config);
}

class FourCCTest
    : public JPEGDecodeTest
    , public ::testing::WithParamInterface<
        std::tuple<TestPattern::SharedConst, const char*> >
{
protected:
    virtual void SetUp()
    {
        JPEGDecodeTest::SetUp();

        std::string sFourcc;
        std::tie(testPattern, sFourcc) = GetParam();

        ASSERT_PTR(testPattern.get()) << "Invalid test pattern parameter";

        ASSERT_EQ(4u, sFourcc.size())
            << "Invalid fourcc parameter '" << sFourcc << "'";

        unsigned fourcc = VA_FOURCC(
            sFourcc[0], sFourcc[1], sFourcc[2], sFourcc[3]);

        pd = testPattern->encoded(fourcc);

        ASSERT_PTR(pd.get())
            << "Unhandled fourcc parameter '" << sFourcc << "'"
            << " = 0x" << std::hex << fourcc << std::dec;

        ASSERT_EQ(fourcc, pd->fourcc);
    }

    void validateComponent(const uint8_t * const expect, const uint8_t * actual,
        unsigned width, unsigned height, unsigned pitch, unsigned hsample = 1,
        unsigned vsample = 1)
    {
        for (size_t row(0); row < (height / vsample); ++row) {
            for (size_t col(0); col < (width / hsample); ++col) {
                size_t aIdx = (row * pitch) + col;
                size_t eIdx = (row * vsample * height) + (col * hsample);

                std::vector<uint8_t> samples;
                for (size_t i(0); i < vsample; ++i) {
                    for (size_t j(0); j < hsample; ++j) {
                        size_t sIdx = eIdx + (width * i) + j;
                        samples.push_back(expect[sIdx]);
                    }
                }

                const uint8_t eVal =
                    std::accumulate(samples.begin(), samples.end(), 0x00)
                    / samples.size();

                const uint8_t aVal = actual[aIdx];

                SCOPED_TRACE(
                    ::testing::Message() << std::endl
                    << "\tRow    = " << row << std::endl
                    << "\tColumn = " << col << std::endl
                    << "\tExpect = 0x"
                    << std::hex << std::setfill('0') << std::setw(2)
                    << (uint32_t)eVal << std::endl
                    << "\tActual = 0x"
                    << std::hex << std::setfill('0') << std::setw(2)
                    << (uint32_t)aVal << std::dec);

                EXPECT_NEAR(eVal, aVal, 0x02);
            }
        }
    }

    void validateImageOutput(const VAImage& image, const uint8_t * const output)
    {
        {
            SCOPED_TRACE("Y Component\n");
            validateComponent(
                testPattern->decoded().data(), output + image.offsets[0],
                image.width, image.height, image.pitches[0]);
        }

        {
            SCOPED_TRACE("U Component\n");
            validateComponent(
                testPattern->decoded().data() + (image.width * image.height),
                output + image.offsets[1], image.width, image.height,
                image.pitches[1], pd->pparam.components[0].h_sampling_factor,
                pd->pparam.components[0].v_sampling_factor);
        }

        {
            SCOPED_TRACE("V Component\n");
            validateComponent(
                testPattern->decoded().data() + (image.width * image.height * 2),
                output + image.offsets[2], image.width, image.height,
                image.pitches[2], pd->pparam.components[0].h_sampling_factor,
                pd->pparam.components[0].v_sampling_factor);
        }
    }

    void printComponentDataTo(std::ostream& os, const uint8_t * const data,
        unsigned w, unsigned h, unsigned pitch, unsigned hsample = 1,
        unsigned vsample = 1)
    {
        const uint8_t *row = data;
        for (unsigned i(0); i < (h/vsample); ++i) {
            for (size_t j(0); j < (w/hsample); ++j) {
                os  << "0x" << std::hex << std::setfill('0') << std::setw(2)
                    << (uint32_t)row[j] << ",";
            }
            os << std::endl;
            row += pitch;
        }
        os << std::setw(0) << std::setfill(' ') << std::dec << std::endl;
    }

    void printImageOutputTo(std::ostream& os, const VAImage& image,
        const uint8_t * const output)
    {
        printComponentDataTo(os, output + image.offsets[0], image.width,
            image.height, image.pitches[0]); // Y

        printComponentDataTo(os, output + image.offsets[1], image.width,
            image.height, image.pitches[1],
            pd->pparam.components[0].h_sampling_factor,
            pd->pparam.components[0].v_sampling_factor); // U

        printComponentDataTo(os, output + image.offsets[2], image.width,
            image.height, image.pitches[2],
            pd->pparam.components[0].h_sampling_factor,
            pd->pparam.components[0].v_sampling_factor); // V
    }

    TestPattern::SharedConst testPattern;
    PictureData::SharedConst pd;
};

#define ASSERT_NO_FAILURE(statement) \
    statement; \
    ASSERT_FALSE(HasFailure());

TEST_P(FourCCTest, Decode)
{
    VAConfigAttrib a = { type:VAConfigAttribRTFormat, value:pd->format };
    ConfigAttribs attribs(1, a);

    ASSERT_NO_FAILURE(
        Surfaces surfaces = createSurfaces(
            pd->pparam.picture_width, pd->pparam.picture_height, pd->format));
    ASSERT_NO_FAILURE(
        VAConfigID config = createConfig(profile, entrypoint, attribs));
    ASSERT_NO_FAILURE(
        VAContextID context = createContext(
            config, pd->pparam.picture_width, pd->pparam.picture_height, 0,
            surfaces));
    ASSERT_NO_FAILURE(
        VABufferID sliceDataBufId = createBuffer(
            context, VASliceDataBufferType, pd->sparam.slice_data_size, 1,
            pd->slice.data()));
    ASSERT_NO_FAILURE(
        VABufferID sliceParamBufId = createBuffer(
            context, VASliceParameterBufferType, sizeof(pd->sparam), 1,
            &pd->sparam));
    ASSERT_NO_FAILURE(
        VABufferID picBufId = createBuffer(
            context, VAPictureParameterBufferType, sizeof(pd->pparam), 1,
            &pd->pparam));
    ASSERT_NO_FAILURE(
        VABufferID iqMatrixBufId = createBuffer(
            context, VAIQMatrixBufferType, sizeof(IQMatrix), 1, &pd->iqmatrix));
    ASSERT_NO_FAILURE(
        VABufferID huffTableBufId = createBuffer(
            context, VAHuffmanTableBufferType, sizeof(HuffmanTable), 1,
            &pd->huffman));

    ASSERT_NO_FAILURE(beginPicture(context, surfaces.front()));
    ASSERT_NO_FAILURE(renderPicture(context, &picBufId));
    ASSERT_NO_FAILURE(renderPicture(context, &iqMatrixBufId));
    ASSERT_NO_FAILURE(renderPicture(context, &huffTableBufId));
    ASSERT_NO_FAILURE(renderPicture(context, &sliceParamBufId));
    ASSERT_NO_FAILURE(renderPicture(context, &sliceDataBufId));
    ASSERT_NO_FAILURE(endPicture(context));

    VAImage image;
    ASSERT_NO_FAILURE(deriveImage(surfaces.front(), image));
    ASSERT_NO_FAILURE(
        uint8_t *output = mapBuffer<uint8_t>(image.buf));

    unsigned rwidth = ALIGN(image.width, 128);
    unsigned rheight =
        ALIGN(image.height, 32)
        + ALIGN(image.height / pd->pparam.components[0].v_sampling_factor, 32)
        * 2;

    SCOPED_TRACE(
        ::testing::Message()
        << std::endl
        << "image  : " << image.width << "x" << image.height
        << std::endl
        << "region : " << rwidth << "x" << rheight
        << std::endl
        << "planes : " << image.num_planes
        << std::endl
        << "offsets: " << image.offsets[0] << " " << image.offsets[1] << " " << image.offsets[2]
        << std::endl
        << "pitches: " << image.pitches[0] << " " << image.pitches[1] << " " << image.pitches[2]
    );

    EXPECT_EQ(3u, image.num_planes);
    EXPECT_EQ(pd->pparam.picture_width, image.width);
    EXPECT_EQ(pd->pparam.picture_height, image.height);
    EXPECT_EQ(rwidth * rheight, image.data_size);
    EXPECT_EQ(pd->fourcc, image.format.fourcc);

    std::ostringstream oss;
    printImageOutputTo(oss, image, output);
    RecordProperty("Output", oss.str());

    validateImageOutput(image, output);

//     std::cout << oss.str();

    unmapBuffer(image.buf);

    destroyBuffer(huffTableBufId);
    destroyBuffer(iqMatrixBufId);
    destroyBuffer(picBufId);
    destroyBuffer(sliceParamBufId);
    destroyBuffer(sliceDataBufId);

    destroyImage(image);
    destroyContext(context);
    destroyConfig(config);
    destroySurfaces(surfaces);
}

/** Teach Google Test how to print a TestPattern::SharedConst object */
void PrintTo(const TestPattern::SharedConst& t, std::ostream* os)
{
    *os << *t;
}

INSTANTIATE_TEST_CASE_P(
    JPEG, FourCCTest,
    ::testing::Combine(
        ::testing::Values(
            TestPattern::SharedConst(new TestPatternData<1>),
            TestPattern::SharedConst(new TestPatternData<2>),
            TestPattern::SharedConst(new TestPatternData<3>),
            TestPattern::SharedConst(new TestPatternData<4>)
        ),
        ::testing::Values("IMC3", "422H", "422V", "444P", "411P"))
);

} // namespace JPEG
<commit_msg>test: skip jpeg fourcc decode test if unsupported by hw<commit_after>/*
 * Copyright (C) 2016 Intel Corporation. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "i965_test_fixture.h"
#include "i965_jpeg_test_data.h"

#include <iomanip>
#include <iostream>
#include <numeric>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>

namespace JPEG {

class JPEGDecodeTest : public I965TestFixture
{
protected:
    static const VAEntrypoint entrypoint = VAEntrypointVLD;
};

TEST_F(JPEGDecodeTest, Entrypoint)
{
    VAConfigID config = VA_INVALID_ID;
    ConfigAttribs attributes;
    struct i965_driver_data *i965(*this);

    ASSERT_PTR(i965);

    if (HAS_JPEG_DECODING(i965)) {
        config = createConfig(profile, entrypoint, attributes);
    } else {
        VAStatus status = i965_CreateConfig(
            *this, profile, entrypoint, attributes.data(), attributes.size(),
            &config);
        EXPECT_STATUS_EQ(VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT, status);
        EXPECT_INVALID_ID(config);
    }

    if (config != VA_INVALID_ID)
        destroyConfig(config);
}

class FourCCTest
    : public JPEGDecodeTest
    , public ::testing::WithParamInterface<
        std::tuple<TestPattern::SharedConst, const char*> >
{
protected:
    virtual void SetUp()
    {
        JPEGDecodeTest::SetUp();

        std::string sFourcc;
        std::tie(testPattern, sFourcc) = GetParam();

        ASSERT_PTR(testPattern.get()) << "Invalid test pattern parameter";

        ASSERT_EQ(4u, sFourcc.size())
            << "Invalid fourcc parameter '" << sFourcc << "'";

        unsigned fourcc = VA_FOURCC(
            sFourcc[0], sFourcc[1], sFourcc[2], sFourcc[3]);

        pd = testPattern->encoded(fourcc);

        ASSERT_PTR(pd.get())
            << "Unhandled fourcc parameter '" << sFourcc << "'"
            << " = 0x" << std::hex << fourcc << std::dec;

        ASSERT_EQ(fourcc, pd->fourcc);
    }

    void validateComponent(const uint8_t * const expect, const uint8_t * actual,
        unsigned width, unsigned height, unsigned pitch, unsigned hsample = 1,
        unsigned vsample = 1)
    {
        for (size_t row(0); row < (height / vsample); ++row) {
            for (size_t col(0); col < (width / hsample); ++col) {
                size_t aIdx = (row * pitch) + col;
                size_t eIdx = (row * vsample * height) + (col * hsample);

                std::vector<uint8_t> samples;
                for (size_t i(0); i < vsample; ++i) {
                    for (size_t j(0); j < hsample; ++j) {
                        size_t sIdx = eIdx + (width * i) + j;
                        samples.push_back(expect[sIdx]);
                    }
                }

                const uint8_t eVal =
                    std::accumulate(samples.begin(), samples.end(), 0x00)
                    / samples.size();

                const uint8_t aVal = actual[aIdx];

                SCOPED_TRACE(
                    ::testing::Message() << std::endl
                    << "\tRow    = " << row << std::endl
                    << "\tColumn = " << col << std::endl
                    << "\tExpect = 0x"
                    << std::hex << std::setfill('0') << std::setw(2)
                    << (uint32_t)eVal << std::endl
                    << "\tActual = 0x"
                    << std::hex << std::setfill('0') << std::setw(2)
                    << (uint32_t)aVal << std::dec);

                EXPECT_NEAR(eVal, aVal, 0x02);
            }
        }
    }

    void validateImageOutput(const VAImage& image, const uint8_t * const output)
    {
        {
            SCOPED_TRACE("Y Component\n");
            validateComponent(
                testPattern->decoded().data(), output + image.offsets[0],
                image.width, image.height, image.pitches[0]);
        }

        {
            SCOPED_TRACE("U Component\n");
            validateComponent(
                testPattern->decoded().data() + (image.width * image.height),
                output + image.offsets[1], image.width, image.height,
                image.pitches[1], pd->pparam.components[0].h_sampling_factor,
                pd->pparam.components[0].v_sampling_factor);
        }

        {
            SCOPED_TRACE("V Component\n");
            validateComponent(
                testPattern->decoded().data() + (image.width * image.height * 2),
                output + image.offsets[2], image.width, image.height,
                image.pitches[2], pd->pparam.components[0].h_sampling_factor,
                pd->pparam.components[0].v_sampling_factor);
        }
    }

    void printComponentDataTo(std::ostream& os, const uint8_t * const data,
        unsigned w, unsigned h, unsigned pitch, unsigned hsample = 1,
        unsigned vsample = 1)
    {
        const uint8_t *row = data;
        for (unsigned i(0); i < (h/vsample); ++i) {
            for (size_t j(0); j < (w/hsample); ++j) {
                os  << "0x" << std::hex << std::setfill('0') << std::setw(2)
                    << (uint32_t)row[j] << ",";
            }
            os << std::endl;
            row += pitch;
        }
        os << std::setw(0) << std::setfill(' ') << std::dec << std::endl;
    }

    void printImageOutputTo(std::ostream& os, const VAImage& image,
        const uint8_t * const output)
    {
        printComponentDataTo(os, output + image.offsets[0], image.width,
            image.height, image.pitches[0]); // Y

        printComponentDataTo(os, output + image.offsets[1], image.width,
            image.height, image.pitches[1],
            pd->pparam.components[0].h_sampling_factor,
            pd->pparam.components[0].v_sampling_factor); // U

        printComponentDataTo(os, output + image.offsets[2], image.width,
            image.height, image.pitches[2],
            pd->pparam.components[0].h_sampling_factor,
            pd->pparam.components[0].v_sampling_factor); // V
    }

    TestPattern::SharedConst testPattern;
    PictureData::SharedConst pd;
};

#define ASSERT_NO_FAILURE(statement) \
    statement; \
    ASSERT_FALSE(HasFailure());

TEST_P(FourCCTest, Decode)
{
    struct i965_driver_data *i965(*this);
    ASSERT_PTR(i965);
    if (not HAS_JPEG_DECODING(i965)) {
        RecordProperty("skipped", true);
        std::cout << "[  SKIPPED ] " << getFullTestName()
            << " is unsupported on this hardware" << std::endl;
        return;
    }

    VAConfigAttrib a = { type:VAConfigAttribRTFormat, value:pd->format };
    ConfigAttribs attribs(1, a);

    ASSERT_NO_FAILURE(
        Surfaces surfaces = createSurfaces(
            pd->pparam.picture_width, pd->pparam.picture_height, pd->format));
    ASSERT_NO_FAILURE(
        VAConfigID config = createConfig(profile, entrypoint, attribs));
    ASSERT_NO_FAILURE(
        VAContextID context = createContext(
            config, pd->pparam.picture_width, pd->pparam.picture_height, 0,
            surfaces));
    ASSERT_NO_FAILURE(
        VABufferID sliceDataBufId = createBuffer(
            context, VASliceDataBufferType, pd->sparam.slice_data_size, 1,
            pd->slice.data()));
    ASSERT_NO_FAILURE(
        VABufferID sliceParamBufId = createBuffer(
            context, VASliceParameterBufferType, sizeof(pd->sparam), 1,
            &pd->sparam));
    ASSERT_NO_FAILURE(
        VABufferID picBufId = createBuffer(
            context, VAPictureParameterBufferType, sizeof(pd->pparam), 1,
            &pd->pparam));
    ASSERT_NO_FAILURE(
        VABufferID iqMatrixBufId = createBuffer(
            context, VAIQMatrixBufferType, sizeof(IQMatrix), 1, &pd->iqmatrix));
    ASSERT_NO_FAILURE(
        VABufferID huffTableBufId = createBuffer(
            context, VAHuffmanTableBufferType, sizeof(HuffmanTable), 1,
            &pd->huffman));

    ASSERT_NO_FAILURE(beginPicture(context, surfaces.front()));
    ASSERT_NO_FAILURE(renderPicture(context, &picBufId));
    ASSERT_NO_FAILURE(renderPicture(context, &iqMatrixBufId));
    ASSERT_NO_FAILURE(renderPicture(context, &huffTableBufId));
    ASSERT_NO_FAILURE(renderPicture(context, &sliceParamBufId));
    ASSERT_NO_FAILURE(renderPicture(context, &sliceDataBufId));
    ASSERT_NO_FAILURE(endPicture(context));

    VAImage image;
    ASSERT_NO_FAILURE(deriveImage(surfaces.front(), image));
    ASSERT_NO_FAILURE(
        uint8_t *output = mapBuffer<uint8_t>(image.buf));

    unsigned rwidth = ALIGN(image.width, 128);
    unsigned rheight =
        ALIGN(image.height, 32)
        + ALIGN(image.height / pd->pparam.components[0].v_sampling_factor, 32)
        * 2;

    SCOPED_TRACE(
        ::testing::Message()
        << std::endl
        << "image  : " << image.width << "x" << image.height
        << std::endl
        << "region : " << rwidth << "x" << rheight
        << std::endl
        << "planes : " << image.num_planes
        << std::endl
        << "offsets: " << image.offsets[0] << " " << image.offsets[1] << " " << image.offsets[2]
        << std::endl
        << "pitches: " << image.pitches[0] << " " << image.pitches[1] << " " << image.pitches[2]
    );

    EXPECT_EQ(3u, image.num_planes);
    EXPECT_EQ(pd->pparam.picture_width, image.width);
    EXPECT_EQ(pd->pparam.picture_height, image.height);
    EXPECT_EQ(rwidth * rheight, image.data_size);
    EXPECT_EQ(pd->fourcc, image.format.fourcc);

    std::ostringstream oss;
    printImageOutputTo(oss, image, output);
    RecordProperty("Output", oss.str());

    validateImageOutput(image, output);

//     std::cout << oss.str();

    unmapBuffer(image.buf);

    destroyBuffer(huffTableBufId);
    destroyBuffer(iqMatrixBufId);
    destroyBuffer(picBufId);
    destroyBuffer(sliceParamBufId);
    destroyBuffer(sliceDataBufId);

    destroyImage(image);
    destroyContext(context);
    destroyConfig(config);
    destroySurfaces(surfaces);
}

/** Teach Google Test how to print a TestPattern::SharedConst object */
void PrintTo(const TestPattern::SharedConst& t, std::ostream* os)
{
    *os << *t;
}

INSTANTIATE_TEST_CASE_P(
    JPEG, FourCCTest,
    ::testing::Combine(
        ::testing::Values(
            TestPattern::SharedConst(new TestPatternData<1>),
            TestPattern::SharedConst(new TestPatternData<2>),
            TestPattern::SharedConst(new TestPatternData<3>),
            TestPattern::SharedConst(new TestPatternData<4>)
        ),
        ::testing::Values("IMC3", "422H", "422V", "444P", "411P"))
);

} // namespace JPEG
<|endoftext|>


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

#include "fixedsourceselector.h"
#include "singlenumericattribute.hpp"

#include <vespa/log/log.h>
LOG_SETUP(".searchlib.attribute.fixed_source_selector");

namespace search {

namespace {
    attribute::Config getConfig() { return attribute::Config(attribute::BasicType::INT8); }
}

FixedSourceSelector::Iterator::Iterator(const FixedSourceSelector & sourceSelector) :
    IIterator(sourceSelector._source),
    _attributeGuard(sourceSelector._realSource)
{ }

FixedSourceSelector::FixedSourceSelector(queryeval::Source defaultSource,
                                         const vespalib::string & attrBaseFileName,
                                         uint32_t initialNumDocs) :
    SourceSelector(defaultSource, AttributeVector::SP(new SourceStore(attrBaseFileName, getConfig()))),
    _source(static_cast<SourceStore &>(*_realSource))
{
    reserve(initialNumDocs);
    _source.commit();
}

FixedSourceSelector::~FixedSourceSelector()
{
}

FixedSourceSelector::UP
FixedSourceSelector::cloneAndSubtract(const vespalib::string & attrBaseFileName,
                                      uint32_t diff)
{
    queryeval::Source newDefault = getNewSource(getDefaultSource(), diff);
    FixedSourceSelector::UP selector(new FixedSourceSelector(newDefault, attrBaseFileName, _source.getNumDocs()-1));
    for (uint32_t docId = 0; docId < _source.getNumDocs(); ++docId) {
        queryeval::Source src = _source.get(docId);
        src = getNewSource(src, diff);
        assert(src < SOURCE_LIMIT);
        selector->_source.set(docId, src);
    }
    selector->_source.commit();
    selector->setBaseId(getBaseId() + diff);
    selector->_source.setCommittedDocIdLimit(_source.getCommittedDocIdLimit());
    return selector;
}

FixedSourceSelector::UP
FixedSourceSelector::load(const vespalib::string & baseFileName)
{
    LoadInfo::UP info = extractLoadInfo(baseFileName);
    info->load();
    FixedSourceSelector::UP selector(new FixedSourceSelector(
                                             info->header()._defaultSource,
                                             info->header()._baseFileName,
                                             0));
    selector->setBaseId(info->header()._baseId);
    selector->_source.load();
    return selector;
}

void FixedSourceSelector::reserve(uint32_t numDocs)
{
    const uint32_t maxDoc(_source.getNumDocs());
    const uint32_t newMaxDocIdPlussOne(numDocs + 1);
    if (newMaxDocIdPlussOne > maxDoc) {
        uint32_t newDocId(0);
        for (_source.addDoc(newDocId); newDocId < numDocs; _source.addDoc(newDocId));
        for (uint32_t i = maxDoc; i < newMaxDocIdPlussOne; ++i) {
            _source.set(i, getDefaultSource());
        }
    }
}

void
FixedSourceSelector::setSource(uint32_t docId, queryeval::Source source)
{
    assert(source < SOURCE_LIMIT);
    /**
     * Due to matchingloop advancing 1 past end, we need to initialize data that
     * far too.
     **/
    reserve(docId+1);
    _source.update(docId, source);
    _source.updateUncommittedDocIdLimit(docId + 1);
    _source.commit();
}

void
FixedSourceSelector::compactLidSpace(uint32_t lidLimit)
{
    _source.compactLidSpace(lidLimit + 1);
}

} // namespace search
<commit_msg>Don't compact lid space on source selector if wanted lid limit is higher than current limit. This can happen when none of the document fields are indexed.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "fixedsourceselector.h"
#include "singlenumericattribute.hpp"

#include <vespa/log/log.h>
LOG_SETUP(".searchlib.attribute.fixed_source_selector");

namespace search {

namespace {
    attribute::Config getConfig() { return attribute::Config(attribute::BasicType::INT8); }
}

FixedSourceSelector::Iterator::Iterator(const FixedSourceSelector & sourceSelector) :
    IIterator(sourceSelector._source),
    _attributeGuard(sourceSelector._realSource)
{ }

FixedSourceSelector::FixedSourceSelector(queryeval::Source defaultSource,
                                         const vespalib::string & attrBaseFileName,
                                         uint32_t initialNumDocs) :
    SourceSelector(defaultSource, AttributeVector::SP(new SourceStore(attrBaseFileName, getConfig()))),
    _source(static_cast<SourceStore &>(*_realSource))
{
    reserve(initialNumDocs);
    _source.commit();
}

FixedSourceSelector::~FixedSourceSelector()
{
}

FixedSourceSelector::UP
FixedSourceSelector::cloneAndSubtract(const vespalib::string & attrBaseFileName,
                                      uint32_t diff)
{
    queryeval::Source newDefault = getNewSource(getDefaultSource(), diff);
    FixedSourceSelector::UP selector(new FixedSourceSelector(newDefault, attrBaseFileName, _source.getNumDocs()-1));
    for (uint32_t docId = 0; docId < _source.getNumDocs(); ++docId) {
        queryeval::Source src = _source.get(docId);
        src = getNewSource(src, diff);
        assert(src < SOURCE_LIMIT);
        selector->_source.set(docId, src);
    }
    selector->_source.commit();
    selector->setBaseId(getBaseId() + diff);
    selector->_source.setCommittedDocIdLimit(_source.getCommittedDocIdLimit());
    return selector;
}

FixedSourceSelector::UP
FixedSourceSelector::load(const vespalib::string & baseFileName)
{
    LoadInfo::UP info = extractLoadInfo(baseFileName);
    info->load();
    FixedSourceSelector::UP selector(new FixedSourceSelector(
                                             info->header()._defaultSource,
                                             info->header()._baseFileName,
                                             0));
    selector->setBaseId(info->header()._baseId);
    selector->_source.load();
    return selector;
}

void FixedSourceSelector::reserve(uint32_t numDocs)
{
    const uint32_t maxDoc(_source.getNumDocs());
    const uint32_t newMaxDocIdPlussOne(numDocs + 1);
    if (newMaxDocIdPlussOne > maxDoc) {
        uint32_t newDocId(0);
        for (_source.addDoc(newDocId); newDocId < numDocs; _source.addDoc(newDocId));
        for (uint32_t i = maxDoc; i < newMaxDocIdPlussOne; ++i) {
            _source.set(i, getDefaultSource());
        }
    }
}

void
FixedSourceSelector::setSource(uint32_t docId, queryeval::Source source)
{
    assert(source < SOURCE_LIMIT);
    /**
     * Due to matchingloop advancing 1 past end, we need to initialize data that
     * far too.
     **/
    reserve(docId+1);
    _source.update(docId, source);
    _source.updateUncommittedDocIdLimit(docId + 1);
    _source.commit();
}

void
FixedSourceSelector::compactLidSpace(uint32_t lidLimit)
{
    if (lidLimit < _source.getCommittedDocIdLimit()) {
        _source.compactLidSpace(lidLimit + 1);
    }
}

} // namespace search
<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Ruben Van Boxem
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/

/*
 * Signal
 * Registers observers.
 * Notifies registered observers.
 */

#ifndef SKUI_CORE_SIGNAL_H
#define SKUI_CORE_SIGNAL_H

#include "core/slot.h++"
#include "core/trackable.h++"
#include "core/value_ptr.h++"

#include <algorithm>
#include <functional>
#include <iterator>
#include <list>
#include <memory>
#include <mutex>
#include <utility>

namespace skui
{
  namespace core
  {
    namespace implementation
    {
      template<typename... ArgTypes>
      class signal_base : public tracker
      {
      public:
        using function_type = void(*)(ArgTypes...);
        using slot_type = slot<void, ArgTypes...>;
        using object_slot_type = std::pair<const trackable*, value_ptr<slot_type>>;
        using connection_type = typename std::list<object_slot_type>::const_iterator;

        signal_base() = default;
        ~signal_base() { disconnect_all(); }

        signal_base(const signal_base& other)
          : slots()
        {
          std::copy(other.slots.begin(), other.slots.end(), std::back_inserter(slots));
        }

        signal_base(signal_base&& other) : slots(std::move(other.slots)) {}
        signal_base& operator=(signal_base other) { swap(slots, other.slots); return *this; }

        virtual void trackable_deleted(const trackable* tracker) override
        {
          disconnect(tracker);
        }

        virtual void trackable_moved(const trackable* old_object, const trackable* new_object) override
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          std::for_each(slots.begin(), slots.end(),
                        [&old_object, &new_object](object_slot_type& object_slot)
                        {
                          if(object_slot.first == old_object)
                            object_slot.first = new_object;
                        });
        }

        virtual void trackable_copied(const trackable* old_object, const trackable* new_object) override
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          std::for_each(slots.begin(), slots.end(),
                        [&old_object, &new_object, this](const object_slot_type& object_slot)
                        {
                          if(object_slot.first == old_object)
                            slots.push_back({new_object, object_slot.second});
                        });

        }

        template<typename Callable, typename ReturnType = void>
        connection_type connect(Callable&& callable)
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          slots.emplace_back(nullptr, make_value<function_slot<Callable, ReturnType, ArgTypes...>>(callable));
          return --slots.end();
        }

        template<typename Class, typename ReturnType>
        connection_type connect(Class* object, ReturnType(Class::* slot)(ArgTypes...))
        {
          static_assert(std::is_base_of<trackable, Class>::value,
                        "You can only connect to member functions of a trackable object.");

          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);

          slots.emplace_back(object, make_value<member_function_slot<Class, ReturnType, ArgTypes...>>(slot));
          object->track(this);

          return --slots.end();
        }

        template<typename Class, typename ReturnType>
        connection_type connect(const Class* object, ReturnType(Class::* slot)(ArgTypes...) const)
        {
          static_assert(std::is_base_of<trackable, Class>::value,
                        "You can only connect to member functions of a trackable object");
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);

          object_slot_type object_slot(object, make_value<const_member_function_slot<Class, ReturnType, ArgTypes...>>(slot));
          slots.emplace_back(std::move(object_slot));
          object->track(this);

          return --slots.end();
        }

        // connects first object like first object
        void connect(trackable* /*first*/, trackable* /*second*/)
        {
          // trackable copy constructor makes second->track(this) unnecessary
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
        }

        // removes a previously made connection, handles lifetime tracking if it was the last connection to a specific object
        void disconnect(connection_type connection)
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          const trackable* object = connection->first;

          slots.erase(connection);
          if(object && std::find_if(slots.begin(), slots.end(),
                                   [&object](const object_slot_type& object_slot)
                                   {
                                     return object_slot.first == object;
                                   }) == slots.end())
            object->untrack(this);
        }

        // removes all connections to an object
        void disconnect(const trackable* object)
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          object->untrack(this);
          slots.remove_if([object](const object_slot_type& object_slot)
                          {
                            return object == object_slot.first;
                          });
        }

        // removes all connections
        void disconnect_all()
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          for(object_slot_type& object_slot : slots)
          {
            auto trackable = object_slot.first;
            if(trackable)
              trackable->untrack(this);
          }
          slots.clear();
        }

      protected:
        // mutable here allows to connect to a const object's signals
        mutable std::list<object_slot_type> slots;
        mutable std::mutex slots_mutex;
      };
    }

    template<typename... ArgTypes>
    class signal : public implementation::signal_base<ArgTypes...>
    {
    public:
      signal() = default;

      void emit(ArgTypes... arguments) const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& object_slot : this->slots)
        {
          (*object_slot.second)(object_slot.first, arguments...);
        }
      }
    };

    // Parameterless specialization
    template<> class signal<> : public implementation::signal_base<>
    {
    public:
      signal() = default;

      void emit() const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& object_slot : this->slots)
        {
          (*object_slot.second)(object_slot.first);
        }
      }
    };
  }
}

#endif
<commit_msg>Use std::any_of instead of std::find(...) != end.<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Ruben Van Boxem
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/

/*
 * Signal
 * Registers observers.
 * Notifies registered observers.
 */

#ifndef SKUI_CORE_SIGNAL_H
#define SKUI_CORE_SIGNAL_H

#include "core/slot.h++"
#include "core/trackable.h++"
#include "core/value_ptr.h++"

#include <algorithm>
#include <functional>
#include <iterator>
#include <list>
#include <memory>
#include <mutex>
#include <utility>

namespace skui
{
  namespace core
  {
    namespace implementation
    {
      template<typename... ArgTypes>
      class signal_base : public tracker
      {
      public:
        using function_type = void(*)(ArgTypes...);
        using slot_type = slot<void, ArgTypes...>;
        using object_slot_type = std::pair<const trackable*, value_ptr<slot_type>>;
        using connection_type = typename std::list<object_slot_type>::const_iterator;

        signal_base() = default;
        ~signal_base() { disconnect_all(); }

        signal_base(const signal_base& other)
          : slots()
        {
          std::copy(other.slots.begin(), other.slots.end(), std::back_inserter(slots));
        }

        signal_base(signal_base&& other) : slots(std::move(other.slots)) {}
        signal_base& operator=(signal_base other) { swap(slots, other.slots); return *this; }

        virtual void trackable_deleted(const trackable* tracker) override
        {
          disconnect(tracker);
        }

        virtual void trackable_moved(const trackable* old_object, const trackable* new_object) override
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          std::for_each(slots.begin(), slots.end(),
                        [&old_object, &new_object](object_slot_type& object_slot)
                        {
                          if(object_slot.first == old_object)
                            object_slot.first = new_object;
                        });
        }

        virtual void trackable_copied(const trackable* old_object, const trackable* new_object) override
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          std::for_each(slots.begin(), slots.end(),
                        [&old_object, &new_object, this](const object_slot_type& object_slot)
                        {
                          if(object_slot.first == old_object)
                            slots.push_back({new_object, object_slot.second});
                        });

        }

        template<typename Callable, typename ReturnType = void>
        connection_type connect(Callable&& callable)
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          slots.emplace_back(nullptr, make_value<function_slot<Callable, ReturnType, ArgTypes...>>(callable));
          return --slots.end();
        }

        template<typename Class, typename ReturnType>
        connection_type connect(Class* object, ReturnType(Class::* slot)(ArgTypes...))
        {
          static_assert(std::is_base_of<trackable, Class>::value,
                        "You can only connect to member functions of a trackable object.");

          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);

          slots.emplace_back(object, make_value<member_function_slot<Class, ReturnType, ArgTypes...>>(slot));
          object->track(this);

          return --slots.end();
        }

        template<typename Class, typename ReturnType>
        connection_type connect(const Class* object, ReturnType(Class::* slot)(ArgTypes...) const)
        {
          static_assert(std::is_base_of<trackable, Class>::value,
                        "You can only connect to member functions of a trackable object");
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);

          object_slot_type object_slot(object, make_value<const_member_function_slot<Class, ReturnType, ArgTypes...>>(slot));
          slots.emplace_back(std::move(object_slot));
          object->track(this);

          return --slots.end();
        }

        // connects first object like first object
        void connect(trackable* /*first*/, trackable* /*second*/)
        {
          // trackable copy constructor makes second->track(this) unnecessary
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
        }

        // removes a previously made connection, handles lifetime tracking if it was the last connection to a specific object
        void disconnect(connection_type connection)
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          const trackable* object = connection->first;

          slots.erase(connection);
          if(object && std::any_of(slots.begin(), slots.end(),
                                   [&object](const object_slot_type& object_slot)
                                   { return object_slot.first == object; }))
            object->untrack(this);
        }

        // removes all connections to an object
        void disconnect(const trackable* object)
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          object->untrack(this);
          slots.remove_if([object](const object_slot_type& object_slot)
                          {
                            return object == object_slot.first;
                          });
        }

        // removes all connections
        void disconnect_all()
        {
          const std::lock_guard<decltype(slots_mutex)> lock(slots_mutex);
          for(object_slot_type& object_slot : slots)
          {
            auto trackable = object_slot.first;
            if(trackable)
              trackable->untrack(this);
          }
          slots.clear();
        }

      protected:
        // mutable here allows to connect to a const object's signals
        mutable std::list<object_slot_type> slots;
        mutable std::mutex slots_mutex;
      };
    }

    template<typename... ArgTypes>
    class signal : public implementation::signal_base<ArgTypes...>
    {
    public:
      signal() = default;

      void emit(ArgTypes... arguments) const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& object_slot : this->slots)
        {
          (*object_slot.second)(object_slot.first, arguments...);
        }
      }
    };

    // Parameterless specialization
    template<> class signal<> : public implementation::signal_base<>
    {
    public:
      signal() = default;

      void emit() const
      {
        std::lock_guard<decltype(this->slots_mutex)> lock(this->slots_mutex);
        for(auto&& object_slot : this->slots)
        {
          (*object_slot.second)(object_slot.first);
        }
      }
    };
  }
}

#endif
<|endoftext|>


<commit_before>#include "../../../YaizuComLib/src/stkpl/StkPl.h"
#include "../../../YaizuComLib/src/commonfunc/StkObject.h"
#include "../../../YaizuComLib/src/stkwebapp/StkWebAppSend.h"

void TestGetUser(StkWebAppSend* StkWebAppSendObj)
{
	{
		StkPlPrintf("GetUser(admin) ... ");
		int ResultCode = 0;
		StkWebAppSendObj->SetAutholization("Bearer admin manager");
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, "/api/user/", NULL, &ResultCode);
		if (ResObj == NULL) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		StkObject* ResUserObj = ResObj->GetFirstChildElement()->GetFirstChildElement();
		if (ResUserObj == NULL || StkPlWcsCmp(ResUserObj->GetName(), L"User") != 0) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		StkPlPrintf("[OK]\n");
		delete ResObj;
	}

	{
		StkPlPrintf("GetUser(admin + Method=OPTIONS) ... ");
		int ResultCode = 0;
		StkWebAppSendObj->SetAutholization("Bearer admin manager");
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_OPTIONS, "/api/user/", NULL, &ResultCode);
		if (ResObj != NULL || ResultCode != 200) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		StkPlPrintf("[OK]\n");
		delete ResObj;
	}

	{
		StkPlPrintf("GetUser(dummy) ... ");
		int ResultCode = 0;
		StkWebAppSendObj->SetAutholization("Bearer dummy dummy");
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, "/api/user/", NULL, &ResultCode);
		if (ResultCode != 401) {
			StkPlPrintf("[NG] Res=%d\n", ResultCode);
			StkPlExit(1);
		}
		StkPlPrintf("[OK]\n");
		delete ResObj;
	}
}

bool TestForGetApi(StkWebAppSend* StkWebAppSendObj, const char* Url, const char* Auth, StkObject* CompObj)
{
	int ResultCode = 0;
	StkWebAppSendObj->SetAutholization(Auth);
	StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, Url, NULL, &ResultCode);
	if (ResultCode != 200) {
		if (ResObj != NULL) {
			delete ResObj;
		}
		return false;
	}

	if (ResObj->Contains(CompObj) == NULL) {
		delete ResObj;
		return false;
	}
	return true;
}

void TestNewAgentInfoNotificationNormal(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("New agent information notification (testagent) ... ");
	int ResultCode = 0;

	{
		StkObject* NewObj = new StkObject(L"");
		NewObj->AppendChildElement(new StkObject(L"Name", L"testagent"));
		NewObj->AppendChildElement(new StkObject(L"Status", -980));
		NewObj->AppendChildElement(new StkObject(L"StatusTime", L"00000000FFFFFFFF"));

		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/api/agent/", NewObj, &ResultCode);
		if (ResultCode != 200) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		delete ResObj;
		delete NewObj;
	}
	int ErrCode = 0;
	StkObject* ReqObj = StkObject::CreateObjectFromJson(L"{ \"Data\" : {\"AgentInfo\" : {\"Name\":\"testagent\", \"Status\":-980, \"AcqTime\" : \"00000000ffffffff\"}}}", &ErrCode);
	bool Result = TestForGetApi(StkWebAppSendObj, "/api/agent/", "Bearer admin manager", ReqObj);
	if (Result != true) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	delete ReqObj;
	StkPlPrintf("[OK]\n");
}

void TestNewAgentInfoNotificationNull(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("New agent information notification (NULL) ... ");
	int ResultCode = 0;
	StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/api/agent/", NULL, &ResultCode);
	if (ResultCode != 400 || ResObj == NULL) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	StkObject* ChkObj = new StkObject(L"Code", 4203);
	StkObject* FndObj = ResObj->Contains(ChkObj);
	if (FndObj == NULL) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	delete ChkObj;
	StkPlPrintf("[OK]\n");
	delete ResObj;
}

void TestNewAgentInfoNotificationNoAgentInfo(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("New agent information notification (no agent info) ... ");
	int ResultCode = 0;
	int ErrCode = 0;
	StkObject* ReqObj = StkObject::CreateObjectFromJson(L"{\"XXX\" : {\"Name\":\"testagent\", \"Status\":-980}}", &ErrCode);
	StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/api/agent/", ReqObj, &ResultCode);
	if (ResultCode != 400 || ResObj == NULL) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	StkObject* ChkObj = new StkObject(L"Code", 4201);
	StkObject* FndObj = ResObj->Contains(ChkObj);
	if (FndObj == NULL) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	delete ChkObj;
	StkPlPrintf("[OK]\n");
	delete ReqObj;
	delete ResObj;
}

void TestNewAgentInfoNotificationForbiddenChar(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("New agent information notification (invalid host name) ... ");
	const wchar_t* HostName[] = { L"abc_xyz", L"あいうえお", L"hello!" };
	wchar_t ReqStr[256] = L"";

	for (int Loop = 0; Loop < 3; Loop++) {
		StkPlSwPrintf(ReqStr, 256, L"{\"Name\":\"%ls\", \"Status\":-980}", HostName[Loop]);
		int ResultCode = 0;
		int ErrCode = 0;
		StkObject* ReqObj = StkObject::CreateObjectFromJson(ReqStr, &ErrCode);
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/api/agent/", ReqObj, &ResultCode);
		if (ResultCode != 400 || ResObj == NULL) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		StkObject* ChkObj = new StkObject(L"Code", 4205);
		StkObject* FndObj = ResObj->Contains(ChkObj);
		if (FndObj == NULL) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		delete ChkObj;
		delete ReqObj;
		delete ResObj;
	}
	StkPlPrintf("[OK]\n");
}

void TestPostOperationStop(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("PostOperationStop ... ");
	int ResultCode = 0;
	StkObject* ReqObj = new StkObject(L"");
	ReqObj->AppendChildElement(new StkObject(L"Operation", L"Stop"));
	StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/service/", ReqObj, &ResultCode);
	delete ResObj;
	StkPlPrintf("[OK]\n");
}

void TestGetCommand(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("Get Status Command ... ");
	int ResultCode1 = 0;
	StkObject* ResObj1 = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, "/api/statuscommand/aaa/", NULL, &ResultCode1);
	if (ResultCode1 != 400) {
		if (ResObj1 != NULL) {
			delete ResObj1;
		}
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	StkPlPrintf("[OK]\n");

	StkPlPrintf("Get Operation Command ... ");
	int ResultCode2 = 0;
	StkObject* ResObj2 = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, "/api/opcommand/aaa/", NULL, &ResultCode2);
	if (ResultCode2 != 400) {
		if (ResObj2 != NULL) {
			delete ResObj2;
		}
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	StkPlPrintf("[OK]\n");

	StkPlPrintf("Get Status/Operation Command (Result checking) ... ");
	if (!ResObj1->Equals(ResObj2)) {
		delete ResObj1;
		delete ResObj2;
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	int ErrCode = 0;
	StkObject *CodeObj = StkObject::CreateObjectFromJson(L"{\"Code\" : 4060}", &ErrCode);
	if (!ResObj1->Contains(CodeObj) || !ResObj2->Contains(CodeObj)) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	delete CodeObj;

	delete ResObj1;
	delete ResObj2;
	StkPlPrintf("[OK]\n");
}

void TestNewCommand(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("Add new Command ... ");
	{
		int ResultCode = 0;
		int ErrCode = 0;
		StkObject* ReqObj = StkObject::CreateObjectFromJson(L"{\"Name\" : \"hello\", \"Type\" : 0, \"Script\" : \"echo hello, world!!\", \"ServerFileName\" : \"\", \"AgentFileName\" : \"\"}", &ErrCode);
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/api/command/", ReqObj, &ResultCode);
		if (ResultCode != 200) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		if (ResObj == NULL) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		delete ReqObj;
		delete ResObj;
	}
	{
		int ResultCode = 0;
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, "/api/command/", NULL, &ResultCode);
		if (ResultCode != 200) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		int ErrCode = 0;
		StkObject* CompObj = StkObject::CreateObjectFromJson(L"\"Command\" : {\"Name\" : \"hello\"}", &ErrCode);
		if (!ResObj->Contains(CompObj)) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		delete CompObj;
		delete ResObj;
	}
	StkPlPrintf("[OK]\n");
}

int main(int Argc, char* Argv[])
{
	StkPlSleepMs(3000);
	StkWebAppSend* StkWebAppSendObj = new StkWebAppSend(10, L"localhost", 10009);
	TestGetUser(StkWebAppSendObj);

	TestNewAgentInfoNotificationNormal(StkWebAppSendObj);
	TestNewAgentInfoNotificationNull(StkWebAppSendObj);
	TestNewAgentInfoNotificationNoAgentInfo(StkWebAppSendObj);
	TestNewAgentInfoNotificationForbiddenChar(StkWebAppSendObj);

	TestGetCommand(StkWebAppSendObj);
	TestNewCommand(StkWebAppSendObj);

	TestPostOperationStop(StkWebAppSendObj);
	delete StkWebAppSendObj;
	return 0;
}
<commit_msg>#49 : Implementation : A bug fix of test code (StatusTime in post request has been changed into AcqTime)<commit_after>#include "../../../YaizuComLib/src/stkpl/StkPl.h"
#include "../../../YaizuComLib/src/commonfunc/StkObject.h"
#include "../../../YaizuComLib/src/stkwebapp/StkWebAppSend.h"

void TestGetUser(StkWebAppSend* StkWebAppSendObj)
{
	{
		StkPlPrintf("GetUser(admin) ... ");
		int ResultCode = 0;
		StkWebAppSendObj->SetAutholization("Bearer admin manager");
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, "/api/user/", NULL, &ResultCode);
		if (ResObj == NULL) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		StkObject* ResUserObj = ResObj->GetFirstChildElement()->GetFirstChildElement();
		if (ResUserObj == NULL || StkPlWcsCmp(ResUserObj->GetName(), L"User") != 0) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		StkPlPrintf("[OK]\n");
		delete ResObj;
	}

	{
		StkPlPrintf("GetUser(admin + Method=OPTIONS) ... ");
		int ResultCode = 0;
		StkWebAppSendObj->SetAutholization("Bearer admin manager");
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_OPTIONS, "/api/user/", NULL, &ResultCode);
		if (ResObj != NULL || ResultCode != 200) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		StkPlPrintf("[OK]\n");
		delete ResObj;
	}

	{
		StkPlPrintf("GetUser(dummy) ... ");
		int ResultCode = 0;
		StkWebAppSendObj->SetAutholization("Bearer dummy dummy");
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, "/api/user/", NULL, &ResultCode);
		if (ResultCode != 401) {
			StkPlPrintf("[NG] Res=%d\n", ResultCode);
			StkPlExit(1);
		}
		StkPlPrintf("[OK]\n");
		delete ResObj;
	}
}

bool TestForGetApi(StkWebAppSend* StkWebAppSendObj, const char* Url, const char* Auth, StkObject* CompObj)
{
	int ResultCode = 0;
	StkWebAppSendObj->SetAutholization(Auth);
	StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, Url, NULL, &ResultCode);
	if (ResultCode != 200) {
		if (ResObj != NULL) {
			delete ResObj;
		}
		return false;
	}

	if (ResObj->Contains(CompObj) == NULL) {
		delete ResObj;
		return false;
	}
	return true;
}

void TestNewAgentInfoNotificationNormal(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("New agent information notification (testagent) ... ");
	int ResultCode = 0;

	{
		StkObject* NewObj = new StkObject(L"");
		NewObj->AppendChildElement(new StkObject(L"Name", L"testagent"));
		NewObj->AppendChildElement(new StkObject(L"Status", -980));
		NewObj->AppendChildElement(new StkObject(L"AcqTime", L"00000000FFFFFFFF"));

		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/api/agent/", NewObj, &ResultCode);
		if (ResultCode != 200) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		delete ResObj;
		delete NewObj;
	}
	int ErrCode = 0;
	StkObject* ReqObj = StkObject::CreateObjectFromJson(L"{ \"Data\" : {\"AgentInfo\" : {\"Name\":\"testagent\", \"Status\":-980, \"AcqTime\" : \"00000000ffffffff\"}}}", &ErrCode);
	bool Result = TestForGetApi(StkWebAppSendObj, "/api/agent/", "Bearer admin manager", ReqObj);
	if (Result != true) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	delete ReqObj;
	StkPlPrintf("[OK]\n");
}

void TestNewAgentInfoNotificationNull(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("New agent information notification (NULL) ... ");
	int ResultCode = 0;
	StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/api/agent/", NULL, &ResultCode);
	if (ResultCode != 400 || ResObj == NULL) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	StkObject* ChkObj = new StkObject(L"Code", 4203);
	StkObject* FndObj = ResObj->Contains(ChkObj);
	if (FndObj == NULL) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	delete ChkObj;
	StkPlPrintf("[OK]\n");
	delete ResObj;
}

void TestNewAgentInfoNotificationNoAgentInfo(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("New agent information notification (no agent info) ... ");
	int ResultCode = 0;
	int ErrCode = 0;
	StkObject* ReqObj = StkObject::CreateObjectFromJson(L"{\"XXX\" : {\"Name\":\"testagent\", \"Status\":-980}}", &ErrCode);
	StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/api/agent/", ReqObj, &ResultCode);
	if (ResultCode != 400 || ResObj == NULL) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	StkObject* ChkObj = new StkObject(L"Code", 4201);
	StkObject* FndObj = ResObj->Contains(ChkObj);
	if (FndObj == NULL) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	delete ChkObj;
	StkPlPrintf("[OK]\n");
	delete ReqObj;
	delete ResObj;
}

void TestNewAgentInfoNotificationForbiddenChar(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("New agent information notification (invalid host name) ... ");
	const wchar_t* HostName[] = { L"abc_xyz", L"あいうえお", L"hello!" };
	wchar_t ReqStr[256] = L"";

	for (int Loop = 0; Loop < 3; Loop++) {
		StkPlSwPrintf(ReqStr, 256, L"{\"Name\":\"%ls\", \"Status\":-980}", HostName[Loop]);
		int ResultCode = 0;
		int ErrCode = 0;
		StkObject* ReqObj = StkObject::CreateObjectFromJson(ReqStr, &ErrCode);
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/api/agent/", ReqObj, &ResultCode);
		if (ResultCode != 400 || ResObj == NULL) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		StkObject* ChkObj = new StkObject(L"Code", 4205);
		StkObject* FndObj = ResObj->Contains(ChkObj);
		if (FndObj == NULL) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		delete ChkObj;
		delete ReqObj;
		delete ResObj;
	}
	StkPlPrintf("[OK]\n");
}

void TestPostOperationStop(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("PostOperationStop ... ");
	int ResultCode = 0;
	StkObject* ReqObj = new StkObject(L"");
	ReqObj->AppendChildElement(new StkObject(L"Operation", L"Stop"));
	StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/service/", ReqObj, &ResultCode);
	delete ResObj;
	StkPlPrintf("[OK]\n");
}

void TestGetCommand(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("Get Status Command ... ");
	int ResultCode1 = 0;
	StkObject* ResObj1 = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, "/api/statuscommand/aaa/", NULL, &ResultCode1);
	if (ResultCode1 != 400) {
		if (ResObj1 != NULL) {
			delete ResObj1;
		}
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	StkPlPrintf("[OK]\n");

	StkPlPrintf("Get Operation Command ... ");
	int ResultCode2 = 0;
	StkObject* ResObj2 = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, "/api/opcommand/aaa/", NULL, &ResultCode2);
	if (ResultCode2 != 400) {
		if (ResObj2 != NULL) {
			delete ResObj2;
		}
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	StkPlPrintf("[OK]\n");

	StkPlPrintf("Get Status/Operation Command (Result checking) ... ");
	if (!ResObj1->Equals(ResObj2)) {
		delete ResObj1;
		delete ResObj2;
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	int ErrCode = 0;
	StkObject *CodeObj = StkObject::CreateObjectFromJson(L"{\"Code\" : 4060}", &ErrCode);
	if (!ResObj1->Contains(CodeObj) || !ResObj2->Contains(CodeObj)) {
		StkPlPrintf("[NG]\n");
		StkPlExit(1);
	}
	delete CodeObj;

	delete ResObj1;
	delete ResObj2;
	StkPlPrintf("[OK]\n");
}

void TestNewCommand(StkWebAppSend* StkWebAppSendObj)
{
	StkPlPrintf("Add new Command ... ");
	{
		int ResultCode = 0;
		int ErrCode = 0;
		StkObject* ReqObj = StkObject::CreateObjectFromJson(L"{\"Name\" : \"hello\", \"Type\" : 0, \"Script\" : \"echo hello, world!!\", \"ServerFileName\" : \"\", \"AgentFileName\" : \"\"}", &ErrCode);
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_POST, "/api/command/", ReqObj, &ResultCode);
		if (ResultCode != 200) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		if (ResObj == NULL) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		delete ReqObj;
		delete ResObj;
	}
	{
		int ResultCode = 0;
		StkObject* ResObj = StkWebAppSendObj->SendRequestRecvResponse(StkWebAppSend::STKWEBAPP_METHOD_GET, "/api/command/", NULL, &ResultCode);
		if (ResultCode != 200) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		int ErrCode = 0;
		StkObject* CompObj = StkObject::CreateObjectFromJson(L"\"Command\" : {\"Name\" : \"hello\"}", &ErrCode);
		if (!ResObj->Contains(CompObj)) {
			StkPlPrintf("[NG]\n");
			StkPlExit(1);
		}
		delete CompObj;
		delete ResObj;
	}
	StkPlPrintf("[OK]\n");
}

int main(int Argc, char* Argv[])
{
	StkPlSleepMs(3000);
	StkWebAppSend* StkWebAppSendObj = new StkWebAppSend(10, L"localhost", 10009);
	TestGetUser(StkWebAppSendObj);

	TestNewAgentInfoNotificationNormal(StkWebAppSendObj);
	TestNewAgentInfoNotificationNull(StkWebAppSendObj);
	TestNewAgentInfoNotificationNoAgentInfo(StkWebAppSendObj);
	TestNewAgentInfoNotificationForbiddenChar(StkWebAppSendObj);

	TestGetCommand(StkWebAppSendObj);
	TestNewCommand(StkWebAppSendObj);

	TestPostOperationStop(StkWebAppSendObj);
	delete StkWebAppSendObj;
	return 0;
}
<|endoftext|>


<commit_before>/**
 * @file example.cpp
 * @author Marcus Edel
 *
 * Simple random agent.
 */

#include <iostream>

#include "environment.hpp"

using namespace gym;

int main(int argc, char* argv[])
{
  const std::string environment = "CartPole-v0";
  const std::string host = "kurg.org";
  const std::string port = "4040";

  double totalReward = 0;
  size_t totalSteps = 0;

  Environment env(host, port, environment);
  env.compression(9);
  env.monitor.start("./dummy/", true, true);

  env.reset();
  env.render();

  while (1)
  {
    arma::mat action = env.action_space.sample();
    std::cout << "action: \n" << action << std::endl;

    env.step(action);

    totalReward += env.reward;
    totalSteps += 1;

    if (env.done)
    {
      break;
    }

    std::cout << "Current step: " << totalSteps << " current reward: "
      << totalReward << std::endl;
  }

  std::cout << "Instance: " << env.instance << " total steps: " << totalSteps
      << " reward: " << totalReward << std::endl;

  std::cout << "Video: https://kurg.org/media/gym/" << env.instance
      << " (it might take some minutes before the video is accessible)."
      << std::endl;

  return 0;
}
<commit_msg>Changed the host's default address<commit_after>/**
 * @file example.cpp
 * @author Marcus Edel
 *
 * Simple random agent.
 */

#include <iostream>

#include "environment.hpp"

using namespace gym;

int main(int argc, char* argv[])
{
  const std::string environment = "CartPole-v0";
  const std::string host = "127.0.0.1";
  const std::string port = "4040";

  double totalReward = 0;
  size_t totalSteps = 0;

  Environment env(host, port, environment);
  env.compression(9);
  env.monitor.start("./dummy/", true, true);

  env.reset();
  env.render();

  while (1)
  {
    arma::mat action = env.action_space.sample();
    std::cout << "action: \n" << action << std::endl;

    env.step(action);

    totalReward += env.reward;
    totalSteps += 1;

    if (env.done)
    {
      break;
    }

    std::cout << "Current step: " << totalSteps << " current reward: "
      << totalReward << std::endl;
  }

  std::cout << "Instance: " << env.instance << " total steps: " << totalSteps
      << " reward: " << totalReward << std::endl;

  std::cout << "Video: https://kurg.org/media/gym/" << env.instance
      << " (it might take some minutes before the video is accessible)."
      << std::endl;

  return 0;
}
<|endoftext|>


<commit_before>#include "stdafx.h"

LuaMan luaMan;
extern MatrixStack luaMatrixStack;

LuaBindFuncContainer& GetLuaBindFuncContainer()
{
	static LuaBindFuncContainer c;
	return c;
}

LuaMan::LuaMan()
{
	L = nullptr;
}

LuaMan::~LuaMan()
{
	assert(!L);
}

void LuaMan::Create()
{
	Destroy();
	L = luaL_newstate();
	LuaBind(L);
	for (auto it : GetLuaBindFuncContainer()) {
		it(L);
	}
#ifdef _DEBUG
	const char* start = "lua/startD.lua";
#else
	const char* start = "lua/start.lua";
#endif
	if (!aflDoFile(L, start)) {
		printf("%s\n", lua_tostring(L, -1));
		lua_pop(L, 1);
	}
}

void LuaMan::Destroy()
{
	if (!L) {
		return;
	}
	int top = lua_gettop(L);
	if (top > 0) {
		aflog("error! stack is not empty\n");
	}
	lua_close(L);
	L = nullptr;
}

void LuaMan::CallGlobal(const char* func)
{
	luaMatrixStack.Reset();
	lua_getglobal(L, func);
	//	aflDumpStack();
	lua_call(L, 0, 0);
	//	aflDumpStack();
	int top = lua_gettop(L);
	if (top > 0) {
		aflog("error! stack is not empty\n");
	}
	luaMatrixStack.Reset();
}

void LuaMan::Update()
{
	CallGlobal("Update");
}

void LuaMan::Draw2D()
{
	CallGlobal("Draw2D");
}

void LuaMan::Draw3D()
{
	CallGlobal("Draw3D");
}
<commit_msg>Print error messages from Lua<commit_after>#include "stdafx.h"

LuaMan luaMan;
extern MatrixStack luaMatrixStack;

LuaBindFuncContainer& GetLuaBindFuncContainer()
{
	static LuaBindFuncContainer c;
	return c;
}

LuaMan::LuaMan()
{
	L = nullptr;
}

LuaMan::~LuaMan()
{
	assert(!L);
}

void LuaMan::Create()
{
	Destroy();
	L = luaL_newstate();
	LuaBind(L);
	for (auto it : GetLuaBindFuncContainer()) {
		it(L);
	}
#ifdef _DEBUG
	const char* start = "lua/startD.lua";
#else
	const char* start = "lua/start.lua";
#endif
	if (!aflDoFile(L, start)) {
		printf("%s\n", lua_tostring(L, -1));
		lua_pop(L, 1);
	}
}

void LuaMan::Destroy()
{
	if (!L) {
		return;
	}
	int top = lua_gettop(L);
	if (top > 0) {
		aflog("error! stack is not empty\n");
	}
	lua_close(L);
	L = nullptr;
}

void LuaMan::CallGlobal(const char* func)
{
	luaMatrixStack.Reset();
	lua_getglobal(L, func);
	//	aflDumpStack();
	if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
		printf("%s\n", lua_tostring(L, -1));
		lua_pop(L, 1);
	}
	//	aflDumpStack();
	int top = lua_gettop(L);
	if (top > 0) {
		aflog("error! stack is not empty\n");
	}
	luaMatrixStack.Reset();
}

void LuaMan::Update()
{
	CallGlobal("Update");
}

void LuaMan::Draw2D()
{
	CallGlobal("Draw2D");
}

void LuaMan::Draw3D()
{
	CallGlobal("Draw3D");
}
<|endoftext|>


<commit_before>/* Copyright 2019 Google LLC. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "block_map.h"

#include "third_party/gemmlowp/profiling/instrumentation.h"
#include "check_macros.h"
#include "opt_set.h"
#include "size_util.h"

namespace ruy {

void GetBlockByIndex(const BlockMap& block_map, int index,
                     SidePair<int>* block) {
  gemmlowp::ScopedProfilingLabel label("GetBlockByIndex");
  const std::uint32_t index_u32 = index;
  const std::uint32_t rectr =
      index_u32 & ((1u << block_map.rectangularness_log2[Side::kLhs]) - 1);
  const std::uint32_t rectc =
      index_u32 & ((1u << block_map.rectangularness_log2[Side::kRhs]) - 1);

  const std::uint32_t n1 =
      index_u32 >> (block_map.rectangularness_log2[Side::kLhs] +
                    block_map.rectangularness_log2[Side::kRhs]);
  RUY_DCHECK_EQ(index_u32,
                (n1 << (block_map.rectangularness_log2[Side::kLhs] +
                        block_map.rectangularness_log2[Side::kRhs])) +
                    rectr + rectc);

  std::uint32_t br, bc;
  if (block_map.traversal_order == BlockMapTraversalOrder::kLinear) {
    br = n1 & ((1u << block_map.num_blocks_base_log2) - 1);
    bc = n1 >> block_map.num_blocks_base_log2;
  } else {
    // Decode fractal z-order
    const std::uint32_t n2 = (n1 & 0x99999999u) | ((n1 & 0x44444444u) >> 1) |
                             ((n1 & 0x22222222u) << 1);
    const std::uint32_t n4 = (n2 & 0xc3c3c3c3u) | ((n2 & 0x30303030u) >> 2) |
                             ((n2 & 0x0c0c0c0cu) << 2);
    const std::uint32_t n8 = (n4 & 0xf00ff00fu) | ((n4 & 0x0f000f00u) >> 4) |
                             ((n4 & 0x00f000f0u) << 4);
    const std::uint32_t n16 = (n8 & 0xff0000ffu) | ((n8 & 0x00ff0000u) >> 8) |
                              ((n8 & 0x0000ff00u) << 8);

    br = n16 & 0xffff;
    bc = n16 >> 16;
    if (block_map.traversal_order == BlockMapTraversalOrder::kFractalU) {
      // Change fractal z-order to u-order
      br ^= bc;
    }
  }

  br = (br << block_map.rectangularness_log2[Side::kLhs]) + rectr;
  bc = (bc << block_map.rectangularness_log2[Side::kRhs]) + rectc;

  // Store
  (*block)[Side::kLhs] = br;
  (*block)[Side::kRhs] = bc;
}

namespace {

int floor_log2_quotient(int num, int denom) {
  if (num <= denom) {
    return 0;
  }
  int log2_quotient = floor_log2(num) - ceil_log2(denom);
  if ((denom << (log2_quotient + 1)) <= num) {
    log2_quotient++;
  }
  return log2_quotient;
}

}  // namespace

void MakeBlockMap(int rows, int cols, int depth, int kernel_rows,
                  int kernel_cols, int lhs_scalar_size, int rhs_scalar_size,
                  int cache_friendly_traversal_threshold, BlockMap* block_map) {
  gemmlowp::ScopedProfilingLabel label("MakeBlockMap");
  RUY_DCHECK_GE(rows, kernel_rows);
  RUY_DCHECK_GE(cols, kernel_cols);

  block_map->traversal_order = BlockMapTraversalOrder::kLinear;
  if (RUY_OPT_ENABLED(RUY_OPT_FRACTAL) &&
      (rows * lhs_scalar_size + cols * rhs_scalar_size) * depth >=
          cache_friendly_traversal_threshold) {
    block_map->traversal_order = RUY_OPT_ENABLED(RUY_OPT_FRACTAL_U)
                                     ? BlockMapTraversalOrder::kFractalU
                                     : BlockMapTraversalOrder::kFractalZ;
  }

  // See the comment on BlockMap in block_map.h.
  // The destination matrix shape (rows x cols) is to be subdivided into a
  // square (N x N) grid of blocks, whose shapes must be multiples of the
  // kernel block shape (kernel_rows x kernel_cols).
  // Inside each of these N*N blocks, we may have one further level of
  // subdivision either along rows or along cols but not both, to handle
  // better the highly rectangular cases. That is what we call
  // 'rectangularness'.  This extra level of subdivision is into
  // (1 << rows_rectangularness_log2) blocks along rows dimension, or into
  // (1 << cols_rectangularness_log2) blocks along cols dimension.
  int rows_rectangularness_log2 = 0;
  int cols_rectangularness_log2 = 0;
  // In order to compute these rectangularness values, we need to divide
  // the destination matrix's aspect ratio,
  //    rows / cols
  // by the kernel block's aspect ratio,
  //    kernel_block_rows / kernel_block_cols.
  // The quotient of these two quotients simplifies to
  //    (rows * kernel_cols) / (cols * kernel_rows)
  // Whence the introduction of the following products:
  const int rows_times_kernel_cols = rows * kernel_cols;
  const int cols_times_kernel_rows = cols * kernel_rows;
  if (rows_times_kernel_cols > cols_times_kernel_rows) {
    rows_rectangularness_log2 =
        floor_log2_quotient(rows_times_kernel_cols, cols_times_kernel_rows);
    // Sanity check that we did not over-estimate rows_rectangularness_log2.
    RUY_DCHECK_GE(rows_times_kernel_cols >> rows_rectangularness_log2,
                  cols_times_kernel_rows);
  } else if (cols_times_kernel_rows > rows_times_kernel_cols) {
    cols_rectangularness_log2 =
        floor_log2_quotient(cols_times_kernel_rows, rows_times_kernel_cols);
    // Sanity check that we did not over-estimate cols_rectangularness_log2.
    RUY_DCHECK_GE(cols_times_kernel_rows >> cols_rectangularness_log2,
                  rows_times_kernel_cols);
  }

  RUY_DCHECK(!rows_rectangularness_log2 || !cols_rectangularness_log2);

  const int size = std::min(rows, cols);
  const int size_floor_log2 = floor_log2(size);
  const int depth_ceil_log2 = ceil_log2(depth);
  const int kernel_width_log2 = ceil_log2(std::max(kernel_cols, kernel_rows));

  // l1_size_log2 was originally, coarsely speaking the number of rows of LHS,
  // or the number of columns of RHS in a matrix multiplication that we expect,
  // to fit in L1 cache.
  //
  // This initial rationale is not necessarily still relevant. The logic below
  // was determined empirically, not in a principled way.
  int l1_size_log2;
  if (size_floor_log2 <= 3) {
    l1_size_log2 = size_floor_log2;
  } else if (size_floor_log2 <= 6) {
    l1_size_log2 = 4;
  } else {
    l1_size_log2 = 5;
  }

  // The 15 here implicitly encodes target a 32k L1 cache (2^15 == 32k).
  // Once again this only has a distant memory of being originally motivated
  // by such clear principles linking this logic to cache sizes.
  l1_size_log2 = std::min(
      l1_size_log2, 15 - depth_ceil_log2 -
                        ceil_log2(std::max(lhs_scalar_size, rhs_scalar_size)));
  l1_size_log2 = std::max(l1_size_log2, kernel_width_log2);
  l1_size_log2 = std::min(l1_size_log2, size_floor_log2);

  int num_blocks_base_log2 = size_floor_log2 - l1_size_log2;
  RUY_DCHECK_GE(num_blocks_base_log2, 0);
  if (num_blocks_base_log2 == 0) {
    if ((rows % kernel_rows) || (cols % kernel_cols)) {
      num_blocks_base_log2 = 1;
    }
  }

  int rows_rounded_up = round_up_pot(rows, kernel_rows);
  int cols_rounded_up = round_up_pot(cols, kernel_cols);

  const int num_blocks_of_rows_log2 =
      num_blocks_base_log2 + rows_rectangularness_log2;
  const int num_blocks_of_cols_log2 =
      num_blocks_base_log2 + cols_rectangularness_log2;

  const int smallr =
      round_down_pot(rows_rounded_up >> num_blocks_of_rows_log2, kernel_rows);
  const int smallc =
      round_down_pot(cols_rounded_up >> num_blocks_of_cols_log2, kernel_cols);
  const int missr =
      round_up_pot(rows_rounded_up - (smallr << num_blocks_of_rows_log2),
                   kernel_rows) /
      kernel_rows;
  const int missc =
      round_up_pot(cols_rounded_up - (smallc << num_blocks_of_cols_log2),
                   kernel_cols) /
      kernel_cols;

  block_map->dims[Side::kLhs] = rows;
  block_map->dims[Side::kRhs] = cols;
  block_map->kernel_dims[Side::kLhs] = kernel_rows;
  block_map->kernel_dims[Side::kRhs] = kernel_cols;
  block_map->num_blocks_base_log2 = num_blocks_base_log2;
  block_map->rectangularness_log2[Side::kLhs] = rows_rectangularness_log2;
  block_map->rectangularness_log2[Side::kRhs] = cols_rectangularness_log2;
  block_map->small_block_dims[Side::kLhs] = smallr;
  block_map->small_block_dims[Side::kRhs] = smallc;
  block_map->large_blocks[Side::kLhs] = missr;
  block_map->large_blocks[Side::kRhs] = missc;
}

void GetBlockMatrixCoords(Side side, const BlockMap& block_map,
                          const SidePair<int>& block, int* start, int* end) {
  gemmlowp::ScopedProfilingLabel label("GetBlockMatrixCoords");
  const int b = block[side];
  *start =
      b * block_map.small_block_dims[side] +
      std::min(b, block_map.large_blocks[side]) * block_map.kernel_dims[side];
  *end = *start + block_map.small_block_dims[side] +
         (b < block_map.large_blocks[side] ? block_map.kernel_dims[side] : 0);

  RUY_DCHECK_EQ(0, *start % block_map.kernel_dims[side]);
  RUY_DCHECK_EQ(0, *end % block_map.kernel_dims[side]);
  RUY_DCHECK_LE(*end, block_map.dims[side]);
  RUY_DCHECK_LT(*start, *end);
  RUY_DCHECK_GE(*start, 0);
}

void GetBlockMatrixCoords(const BlockMap& block_map, const SidePair<int>& block,
                          SidePair<int>* start, SidePair<int>* end) {
  for (Side side : {Side::kLhs, Side::kRhs}) {
    GetBlockMatrixCoords(side, block_map, block, &(*start)[side],
                         &(*end)[side]);
  }
}

}  // namespace ruy
<commit_msg>Remove a useless piece of code in MakeBlockMap. It was a remnant from when ruy was trying to skip the packing phase in some cases, letting kernels operate directly on source matrices. In that case, there was a difficulty if the matrix sizes were not multiples of kernel size and there was only one block.<commit_after>/* Copyright 2019 Google LLC. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "block_map.h"

#include "third_party/gemmlowp/profiling/instrumentation.h"
#include "check_macros.h"
#include "opt_set.h"
#include "size_util.h"

namespace ruy {

void GetBlockByIndex(const BlockMap& block_map, int index,
                     SidePair<int>* block) {
  gemmlowp::ScopedProfilingLabel label("GetBlockByIndex");
  const std::uint32_t index_u32 = index;
  const std::uint32_t rectr =
      index_u32 & ((1u << block_map.rectangularness_log2[Side::kLhs]) - 1);
  const std::uint32_t rectc =
      index_u32 & ((1u << block_map.rectangularness_log2[Side::kRhs]) - 1);

  const std::uint32_t n1 =
      index_u32 >> (block_map.rectangularness_log2[Side::kLhs] +
                    block_map.rectangularness_log2[Side::kRhs]);
  RUY_DCHECK_EQ(index_u32,
                (n1 << (block_map.rectangularness_log2[Side::kLhs] +
                        block_map.rectangularness_log2[Side::kRhs])) +
                    rectr + rectc);

  std::uint32_t br, bc;
  if (block_map.traversal_order == BlockMapTraversalOrder::kLinear) {
    br = n1 & ((1u << block_map.num_blocks_base_log2) - 1);
    bc = n1 >> block_map.num_blocks_base_log2;
  } else {
    // Decode fractal z-order
    const std::uint32_t n2 = (n1 & 0x99999999u) | ((n1 & 0x44444444u) >> 1) |
                             ((n1 & 0x22222222u) << 1);
    const std::uint32_t n4 = (n2 & 0xc3c3c3c3u) | ((n2 & 0x30303030u) >> 2) |
                             ((n2 & 0x0c0c0c0cu) << 2);
    const std::uint32_t n8 = (n4 & 0xf00ff00fu) | ((n4 & 0x0f000f00u) >> 4) |
                             ((n4 & 0x00f000f0u) << 4);
    const std::uint32_t n16 = (n8 & 0xff0000ffu) | ((n8 & 0x00ff0000u) >> 8) |
                              ((n8 & 0x0000ff00u) << 8);

    br = n16 & 0xffff;
    bc = n16 >> 16;
    if (block_map.traversal_order == BlockMapTraversalOrder::kFractalU) {
      // Change fractal z-order to u-order
      br ^= bc;
    }
  }

  br = (br << block_map.rectangularness_log2[Side::kLhs]) + rectr;
  bc = (bc << block_map.rectangularness_log2[Side::kRhs]) + rectc;

  // Store
  (*block)[Side::kLhs] = br;
  (*block)[Side::kRhs] = bc;
}

namespace {

int floor_log2_quotient(int num, int denom) {
  if (num <= denom) {
    return 0;
  }
  int log2_quotient = floor_log2(num) - ceil_log2(denom);
  if ((denom << (log2_quotient + 1)) <= num) {
    log2_quotient++;
  }
  return log2_quotient;
}

}  // namespace

void MakeBlockMap(int rows, int cols, int depth, int kernel_rows,
                  int kernel_cols, int lhs_scalar_size, int rhs_scalar_size,
                  int cache_friendly_traversal_threshold, BlockMap* block_map) {
  gemmlowp::ScopedProfilingLabel label("MakeBlockMap");
  RUY_DCHECK_GE(rows, kernel_rows);
  RUY_DCHECK_GE(cols, kernel_cols);

  block_map->traversal_order = BlockMapTraversalOrder::kLinear;
  if (RUY_OPT_ENABLED(RUY_OPT_FRACTAL) &&
      (rows * lhs_scalar_size + cols * rhs_scalar_size) * depth >=
          cache_friendly_traversal_threshold) {
    block_map->traversal_order = RUY_OPT_ENABLED(RUY_OPT_FRACTAL_U)
                                     ? BlockMapTraversalOrder::kFractalU
                                     : BlockMapTraversalOrder::kFractalZ;
  }

  // See the comment on BlockMap in block_map.h.
  // The destination matrix shape (rows x cols) is to be subdivided into a
  // square (N x N) grid of blocks, whose shapes must be multiples of the
  // kernel block shape (kernel_rows x kernel_cols).
  // Inside each of these N*N blocks, we may have one further level of
  // subdivision either along rows or along cols but not both, to handle
  // better the highly rectangular cases. That is what we call
  // 'rectangularness'.  This extra level of subdivision is into
  // (1 << rows_rectangularness_log2) blocks along rows dimension, or into
  // (1 << cols_rectangularness_log2) blocks along cols dimension.
  int rows_rectangularness_log2 = 0;
  int cols_rectangularness_log2 = 0;
  // In order to compute these rectangularness values, we need to divide
  // the destination matrix's aspect ratio,
  //    rows / cols
  // by the kernel block's aspect ratio,
  //    kernel_block_rows / kernel_block_cols.
  // The quotient of these two quotients simplifies to
  //    (rows * kernel_cols) / (cols * kernel_rows)
  // Whence the introduction of the following products:
  const int rows_times_kernel_cols = rows * kernel_cols;
  const int cols_times_kernel_rows = cols * kernel_rows;
  if (rows_times_kernel_cols > cols_times_kernel_rows) {
    rows_rectangularness_log2 =
        floor_log2_quotient(rows_times_kernel_cols, cols_times_kernel_rows);
    // Sanity check that we did not over-estimate rows_rectangularness_log2.
    RUY_DCHECK_GE(rows_times_kernel_cols >> rows_rectangularness_log2,
                  cols_times_kernel_rows);
  } else if (cols_times_kernel_rows > rows_times_kernel_cols) {
    cols_rectangularness_log2 =
        floor_log2_quotient(cols_times_kernel_rows, rows_times_kernel_cols);
    // Sanity check that we did not over-estimate cols_rectangularness_log2.
    RUY_DCHECK_GE(cols_times_kernel_rows >> cols_rectangularness_log2,
                  rows_times_kernel_cols);
  }

  RUY_DCHECK(!rows_rectangularness_log2 || !cols_rectangularness_log2);

  const int size = std::min(rows, cols);
  const int size_floor_log2 = floor_log2(size);
  const int depth_ceil_log2 = ceil_log2(depth);
  const int kernel_width_log2 = ceil_log2(std::max(kernel_cols, kernel_rows));

  // l1_size_log2 was originally, coarsely speaking the number of rows of LHS,
  // or the number of columns of RHS in a matrix multiplication that we expect,
  // to fit in L1 cache.
  //
  // This initial rationale is not necessarily still relevant. The logic below
  // was determined empirically, not in a principled way.
  int l1_size_log2;
  if (size_floor_log2 <= 3) {
    l1_size_log2 = size_floor_log2;
  } else if (size_floor_log2 <= 6) {
    l1_size_log2 = 4;
  } else {
    l1_size_log2 = 5;
  }

  // The 15 here implicitly encodes target a 32k L1 cache (2^15 == 32k).
  // Once again this only has a distant memory of being originally motivated
  // by such clear principles linking this logic to cache sizes.
  l1_size_log2 = std::min(
      l1_size_log2, 15 - depth_ceil_log2 -
                        ceil_log2(std::max(lhs_scalar_size, rhs_scalar_size)));
  l1_size_log2 = std::max(l1_size_log2, kernel_width_log2);
  l1_size_log2 = std::min(l1_size_log2, size_floor_log2);

  int num_blocks_base_log2 = size_floor_log2 - l1_size_log2;
  RUY_DCHECK_GE(num_blocks_base_log2, 0);

  int rows_rounded_up = round_up_pot(rows, kernel_rows);
  int cols_rounded_up = round_up_pot(cols, kernel_cols);

  const int num_blocks_of_rows_log2 =
      num_blocks_base_log2 + rows_rectangularness_log2;
  const int num_blocks_of_cols_log2 =
      num_blocks_base_log2 + cols_rectangularness_log2;

  const int smallr =
      round_down_pot(rows_rounded_up >> num_blocks_of_rows_log2, kernel_rows);
  const int smallc =
      round_down_pot(cols_rounded_up >> num_blocks_of_cols_log2, kernel_cols);
  const int missr =
      round_up_pot(rows_rounded_up - (smallr << num_blocks_of_rows_log2),
                   kernel_rows) /
      kernel_rows;
  const int missc =
      round_up_pot(cols_rounded_up - (smallc << num_blocks_of_cols_log2),
                   kernel_cols) /
      kernel_cols;

  block_map->dims[Side::kLhs] = rows;
  block_map->dims[Side::kRhs] = cols;
  block_map->kernel_dims[Side::kLhs] = kernel_rows;
  block_map->kernel_dims[Side::kRhs] = kernel_cols;
  block_map->num_blocks_base_log2 = num_blocks_base_log2;
  block_map->rectangularness_log2[Side::kLhs] = rows_rectangularness_log2;
  block_map->rectangularness_log2[Side::kRhs] = cols_rectangularness_log2;
  block_map->small_block_dims[Side::kLhs] = smallr;
  block_map->small_block_dims[Side::kRhs] = smallc;
  block_map->large_blocks[Side::kLhs] = missr;
  block_map->large_blocks[Side::kRhs] = missc;
}

void GetBlockMatrixCoords(Side side, const BlockMap& block_map,
                          const SidePair<int>& block, int* start, int* end) {
  gemmlowp::ScopedProfilingLabel label("GetBlockMatrixCoords");
  const int b = block[side];
  *start =
      b * block_map.small_block_dims[side] +
      std::min(b, block_map.large_blocks[side]) * block_map.kernel_dims[side];
  *end = *start + block_map.small_block_dims[side] +
         (b < block_map.large_blocks[side] ? block_map.kernel_dims[side] : 0);

  RUY_DCHECK_EQ(0, *start % block_map.kernel_dims[side]);
  RUY_DCHECK_EQ(0, *end % block_map.kernel_dims[side]);
  RUY_DCHECK_LE(*end, block_map.dims[side]);
  RUY_DCHECK_LT(*start, *end);
  RUY_DCHECK_GE(*start, 0);
}

void GetBlockMatrixCoords(const BlockMap& block_map, const SidePair<int>& block,
                          SidePair<int>* start, SidePair<int>* end) {
  for (Side side : {Side::kLhs, Side::kRhs}) {
    GetBlockMatrixCoords(side, block_map, block, &(*start)[side],
                         &(*end)[side]);
  }
}

}  // namespace ruy
<|endoftext|>