Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/circle-resizer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
add_subdirectory(src)
add_subdirectory(app)
add_subdirectory(tests)
73 changes: 73 additions & 0 deletions compiler/circle-resizer/include/Shape.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2025 Samsung Electronics Co., Ltd. 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.
*/

#ifndef __CIRCLE_RESIZER_SHAPE_H__
#define __CIRCLE_RESIZER_SHAPE_H__

#include <stdexcept>
#include <stdint.h>
#include <vector>

namespace circle_resizer
{
/**
* The representation of a single dimension. Note that a dimension can be dynamic.
*/
class Dim
{
public:
/**
* @brief Initialize a single dimension. Note that '-1' means a dynamic dimension.
*
* Exceptions:
* - std::runtime_error if provided dim value is less than -1.
*/
explicit Dim(int32_t dim);

public:
/**
* @brief Return true if the dimension is dynamic. Otherwise, return false.
*/
bool is_dynamic();

/**
* @brief Return value of dimension in int32_t representation.
*/
int32_t value() const;

/**
* @brief Return true of the current dimension and the provided rhs are equal.
*/
bool operator==(const Dim &rhs) const;

private:
// Note that in the future, we might need to support dimension with lower and upper bounds
int32_t _dim_value;
};

/**
* The representation of a single shape.
*/
using Shape = std::vector<Dim>;

/**
* The representation of many shapes.
*/
using Shapes = std::vector<Shape>;

} // namespace circle_resizer

#endif // __CIRCLE_RESIZER_SHAPE_H__
40 changes: 40 additions & 0 deletions compiler/circle-resizer/include/ShapeParser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2025 Samsung Electronics Co., Ltd. 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.
*/

#ifndef __CIRCLE_RESIZER_SHAPE_PARSER_H__
#define __CIRCLE_RESIZER_SHAPE_PARSER_H__

#include "Shape.h"

#include <string>
#include <vector>
Comment thread
jinevening marked this conversation as resolved.

namespace circle_resizer
{
/**
Comment thread
jinevening marked this conversation as resolved.
* @brief Parse shapes from string representation to Shapes object.
*
* The single shape is represented by comma-separated integers inside squared brackets.
* If there is more than one shape, they are separated by commas.
* An example for single shape: [1,2,3], an example for many shapes: [1,2,3],[4,5].
*
* Exceptions:
* std::invalid_argument is the parsing failed.
*/
Shapes parse_shapes(std::string shapes_str);
} // namespace circle_resizer

#endif // __CIRCLE_RESIZER_SHAPE_PARSER_H__
8 changes: 8 additions & 0 deletions compiler/circle-resizer/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
list(APPEND CIRCLE_RESIZER_CORE_SOURCES Shape.cpp)
list(APPEND CIRCLE_RESIZER_CORE_SOURCES ShapeParser.cpp)

add_library(circle_resizer_core STATIC "${CIRCLE_RESIZER_CORE_SOURCES}")

target_include_directories(circle_resizer_core PUBLIC ../include)

install(TARGETS circle_resizer_core DESTINATION lib)
35 changes: 35 additions & 0 deletions compiler/circle-resizer/src/Shape.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2025 Samsung Electronics Co., Ltd. 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 "Shape.h"

#include <stdexcept>

using namespace circle_resizer;

Dim::Dim(int32_t dim) : _dim_value{dim}
{
if (dim < -1)
{
throw std::runtime_error("Invalid value of dimension: " + dim);
}
}

bool Dim::is_dynamic() { return _dim_value == -1; }

int32_t Dim::value() const { return _dim_value; }

bool Dim::operator==(const Dim &rhs) const { return value() == rhs.value(); }
83 changes: 83 additions & 0 deletions compiler/circle-resizer/src/ShapeParser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2025 Samsung Electronics Co., Ltd. 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 "ShapeParser.h"

#include <algorithm>
#include <stdexcept>
#include <sstream>

using namespace circle_resizer;

namespace
{
bool is_blank(const std::string &s)
Comment thread
jinevening marked this conversation as resolved.
{
return !s.empty() && std::find_if(s.begin(), s.end(),
[](unsigned char c) { return !std::isblank(c); }) == s.end();
}

Shape parse_single_shape(const std::string &shape_str)
{
Shape result_shape;
std::stringstream shape_stream(shape_str);
std::string token;
try
{
while (std::getline(shape_stream, token, ','))
{
result_shape.push_back(Dim{std::stoi(token)});
}
}
catch (...)
{
throw std::invalid_argument("Error during shape processing: " + shape_str);
}
if (result_shape.empty())
{
throw std::invalid_argument("No shapes found in input string: " + shape_str);
}
return result_shape;
}
} // namespace
Comment thread
jinevening marked this conversation as resolved.

Shapes circle_resizer::parse_shapes(std::string shapes_str)
Comment thread
jinevening marked this conversation as resolved.
Outdated
{
Shapes result_shapes;
std::stringstream shapes_stream(shapes_str);
std::string token;
size_t begin_pos = 0, end_pos = 0;
while ((begin_pos = shapes_str.find("[")) != std::string::npos &&
(end_pos = shapes_str.find("]")) != std::string::npos)
{
token = shapes_str.substr(begin_pos + 1, end_pos);
result_shapes.push_back(parse_single_shape(token));
shapes_str.erase(0, end_pos + 1);
}

if (result_shapes.empty())
{
throw std::invalid_argument("No shapes found in input string: " + shapes_str);
}

// the rest of the input not handled by loop above cannot be processed properly
if (shapes_str.size() > 0 && !is_blank(shapes_str))
{
throw std::invalid_argument("The part of input shape: " + shapes_str + " cannot be processed");
}

return result_shapes;
}
9 changes: 9 additions & 0 deletions compiler/circle-resizer/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
if(NOT ENABLE_TEST)
return()
endif(NOT ENABLE_TEST)

list(APPEND CIRCLE_RESIZER_TEST_SOURCES ShapeParser.test.cpp)

nnas_find_package(GTest REQUIRED)
GTest_AddTest(circle_resizer_unit_test ${CIRCLE_RESIZER_TEST_SOURCES})
target_link_libraries(circle_resizer_unit_test circle_resizer_core)
73 changes: 73 additions & 0 deletions compiler/circle-resizer/tests/ShapeParser.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2025 Samsung Electronics Co., Ltd. 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 "ShapeParser.h"

#include <gtest/gtest.h>

#include <algorithm>
#include <string>
#include <tuple>
#include <vector>

using namespace circle_resizer;

class ParseShapeTestFixture : public ::testing::TestWithParam<std::tuple<std::string, Shapes>>
{
};

TEST_P(ParseShapeTestFixture, successful_parsing)
{
const auto [input_shape_str, expected_shapes] = GetParam();
Shapes result_shapes;
EXPECT_NO_THROW(result_shapes = parse_shapes(input_shape_str));
ASSERT_EQ(result_shapes, expected_shapes);
}

INSTANTIATE_TEST_SUITE_P(
ParseShapeTest, ParseShapeTestFixture,
::testing::Values(
Comment thread
jinevening marked this conversation as resolved.
Outdated
// single shape
std::make_tuple("[3,4]", Shapes{Shape{Dim{3}, Dim{4}}}),
std::make_tuple("[3]", Shapes{Shape{Dim{3}}}), std::make_tuple("[-1]", Shapes{Shape{Dim{-1}}}),
std::make_tuple("[ 5, 6]", Shapes{Shape{Dim{5}, Dim{6}}}),
std::make_tuple("[3 , 4]", Shapes{Shape{Dim{3}, Dim{4}}}),
std::make_tuple("[-1 , 4]", Shapes{Shape{Dim{-1}, Dim{4}}}),
// many shapes
std::make_tuple("[3,4],[5,6]", Shapes{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}),
std::make_tuple("[1],[2]", Shapes{Shape{Dim{1}}, Shape{Dim{2}}}),
std::make_tuple(" [3, 4] , [5,6]", Shapes{Shape{Dim{3}, Dim{4}}, Shape{Dim{5}, Dim{6}}}),
std::make_tuple(" [ 1 ] ,[ 2]", Shapes{Shape{Dim{1}}, Shape{Dim{2}}}),
std::make_tuple(" [ 1 ] ,[ 2] ", Shapes{Shape{Dim{1}}, Shape{Dim{2}}}),
std::make_tuple(" [1,2],[3,4,5],[6,7,8,9]",
Shapes{Shape{Dim{1}, Dim{2}}, Shape{Dim{3}, Dim{4}, Dim{5}},
Shape{Dim{6}, Dim{7}, Dim{8}, Dim{9}}})));

class InvalidArgParseShapeTestFixture : public ::testing::TestWithParam<std::string>
{
};

TEST_P(InvalidArgParseShapeTestFixture, neg_invalid_input)
Comment thread
jinevening marked this conversation as resolved.
Outdated
{
EXPECT_THROW(parse_shapes(GetParam()), std::invalid_argument);
}

INSTANTIATE_TEST_SUITE_P(InvalidArgParseShape, InvalidArgParseShapeTestFixture,
::testing::Values(std::string{""}, std::string{"]"}, std::string{"1a,2"},
std::string{"[-2]"}, std::string{"[-2,1,3]"},
std::string{"-1"}, std::string{"7,7"}, std::string{"8"},
std::string{"[8],9"}, std::string{"1,2"},
std::string{"[1],[2],"}));