Added region_tree tests
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
#include "world_editor.h"
|
||||
#include "editor_event.h"
|
||||
#include "building_layout_graph.h"
|
||||
#include "grid_misc.h"
|
||||
#include "grow_job.h"
|
||||
#include "growth_regions.h"
|
||||
#include "graph_module.h"
|
||||
|
||||
#define MIN_ROOM_SIZE 16 /* 4 * 4 tiles */
|
||||
@@ -207,6 +210,14 @@ void BuildingLayoutGraph::graph_module::grow_cell(flecs::entity seed_e, int id)
|
||||
c_e.add<WorldEditor::components::belongs>(
|
||||
second);
|
||||
});
|
||||
int mcount = 0;
|
||||
seed_e.each<WorldEditor::components::belongs_room>(
|
||||
[&c_e, &mcount](flecs::entity second) {
|
||||
assert(mcount == 0);
|
||||
c_e.add<WorldEditor::components::belongs_room>(
|
||||
second);
|
||||
mcount++;
|
||||
});
|
||||
floor_e.get_mut<WorldEditor::components::
|
||||
buildings_layout_grid_floor>()
|
||||
->size_left--;
|
||||
@@ -256,15 +267,17 @@ void BuildingLayoutGraph::graph_module::create_floor_components(
|
||||
assert(!floor_e.has<growth_regions>());
|
||||
floor_e.set<WorldEditor::components::buildings_layout_grid_floor>(
|
||||
{ Set<int>(), size.grid_size, size.growth_size });
|
||||
floor_e.set<growth_regions>(
|
||||
{ Vector<struct growth_regions::region>(), false });
|
||||
floor_e.set<growth_regions>({ List<struct grow_job>(), false });
|
||||
floor_e.add<WorldEditor::components::belongs>(base_floor_e);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
void growth_regions::create_region(flecs::entity floor_e, flecs::entity seed_e,
|
||||
flecs::entity parent_e,
|
||||
flecs::entity region_e,
|
||||
const Vector2i &position, float area)
|
||||
const Vector2i &position, float area,
|
||||
int parent_index)
|
||||
{
|
||||
int i;
|
||||
struct growth_regions::region r;
|
||||
@@ -277,6 +290,13 @@ void growth_regions::create_region(flecs::entity floor_e, flecs::entity seed_e,
|
||||
r.can_grow_square = true;
|
||||
r.can_grow = true;
|
||||
r.complete = false;
|
||||
r.parent_region = parent_index;
|
||||
flecs::log::dbg("create region %s in %s",
|
||||
(r.rect.operator String()).ascii().ptr(),
|
||||
(parent_regions[parent_index].rect.operator String())
|
||||
.ascii()
|
||||
.ptr());
|
||||
assert(parent_regions[parent_index].rect.encloses(r.rect));
|
||||
bool ok = true;
|
||||
assert(check_region(-1, r.rect));
|
||||
const struct growth_regions *reg = floor_e.get<growth_regions>();
|
||||
@@ -300,9 +320,11 @@ void growth_regions::create_region(flecs::entity floor_e, flecs::entity seed_e,
|
||||
region_e.path().c_str());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
flecs::entity growth_regions::create_cell(flecs::entity floor_e,
|
||||
flecs::entity region_e, int id)
|
||||
{
|
||||
#if 0
|
||||
flecs::entity ret;
|
||||
flecs::log::dbg("create_cell: %s %d", region_e.path().c_str(), id);
|
||||
if (floor_e.get<WorldEditor::components::buildings_layout_grid_floor>()
|
||||
@@ -310,8 +332,11 @@ flecs::entity growth_regions::create_cell(flecs::entity floor_e,
|
||||
flecs::log::err("cell %d already exists", id);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
String pname("cell_" + itos(id));
|
||||
flecs::entity cell_e = floor_e.lookup(pname.ascii().ptr());
|
||||
if (cell_e.is_valid())
|
||||
flecs::log::err("cell %d already exists", id);
|
||||
assert(!cell_e.is_valid());
|
||||
cell_e = floor_e.world().entity(pname.ascii().ptr()).child_of(floor_e);
|
||||
floor_e.get_mut<WorldEditor::components::buildings_layout_grid_floor>()
|
||||
@@ -323,16 +348,178 @@ flecs::entity growth_regions::create_cell(flecs::entity floor_e,
|
||||
cell_e.set<WorldEditor::components::buildings_layout_grid_cell>(
|
||||
{ String(region_e.name()), id });
|
||||
cell_e.add<WorldEditor::components::belongs>(region_e);
|
||||
if (region_e.has<
|
||||
WorldEditor::components::buildings_layout_room>()) {
|
||||
int mcount = 0;
|
||||
cell_e.each<WorldEditor::components::belongs_room>(
|
||||
[&mcount, region_e](flecs::entity e) {
|
||||
if (e.id() != region_e.id())
|
||||
assert(false);
|
||||
mcount++;
|
||||
});
|
||||
if (mcount == 0)
|
||||
cell_e.add<WorldEditor::components::belongs_room>(
|
||||
region_e);
|
||||
}
|
||||
}
|
||||
return cell_e;
|
||||
}
|
||||
bool BuildingLayoutGraph::graph_module::check_region(flecs::entity floor_e,
|
||||
int index,
|
||||
const Rect2i &rect) const
|
||||
flecs::entity growth_regions::update_cell(flecs::entity floor_e,
|
||||
flecs::entity parent_e,
|
||||
flecs::entity region_e, int id)
|
||||
{
|
||||
const growth_regions *g = floor_e.get<growth_regions>();
|
||||
return g->check_region(index, rect);
|
||||
flecs::log::dbg("create_cell: %s %d", region_e.path().c_str(), id);
|
||||
String pname("cell_" + itos(id));
|
||||
flecs::entity cell_e = floor_e.lookup(pname.ascii().ptr());
|
||||
if (!cell_e.is_valid()) {
|
||||
flecs::log::warn("creating new cell %s", cell_e.path());
|
||||
cell_e = floor_e.world()
|
||||
.entity(pname.ascii().ptr())
|
||||
.child_of(floor_e);
|
||||
assert(cell_e.is_valid());
|
||||
}
|
||||
// assert(cell_e.has<WorldEditor::components::belongs>(parent_e));
|
||||
/* already there */
|
||||
floor_e.get_mut<WorldEditor::components::buildings_layout_grid_floor>()
|
||||
->cells.insert(id);
|
||||
floor_e.modified<WorldEditor::components::buildings_layout_grid_floor>();
|
||||
cell_e.set<WorldEditor::components::buildings_layout_grid_cell>(
|
||||
{ String(region_e.name()), id });
|
||||
cell_e.remove<WorldEditor::components::belongs>(parent_e);
|
||||
cell_e.add<WorldEditor::components::belongs>(region_e);
|
||||
if (region_e.has<WorldEditor::components::buildings_layout_room>()) {
|
||||
int mcount = 0;
|
||||
cell_e.each<WorldEditor::components::belongs_room>(
|
||||
[&mcount, region_e, cell_e](flecs::entity e) {
|
||||
flecs::log::err("adding region %s to cell %s",
|
||||
region_e.path().c_str(),
|
||||
cell_e.path().c_str());
|
||||
flecs::log::err("already belongs to %s",
|
||||
e.path().c_str());
|
||||
assert(mcount == 0);
|
||||
assert(region_e.id() == e.id());
|
||||
mcount++;
|
||||
});
|
||||
if (mcount == 0)
|
||||
cell_e.add<WorldEditor::components::belongs_room>(
|
||||
region_e);
|
||||
}
|
||||
return cell_e;
|
||||
}
|
||||
static inline int int_distance(const Vector2i &v1, const Vector2i &v2)
|
||||
{
|
||||
Vector2i l = v2 - v1;
|
||||
return l.x * l.x + l.y * l.y;
|
||||
}
|
||||
#if 0
|
||||
void growth_regions::split_region(
|
||||
flecs::entity grid_floor_e, int region_index,
|
||||
const List<Pair<flecs::entity, float> > ®ion_list)
|
||||
{
|
||||
int i, j;
|
||||
struct make_random r(173);
|
||||
int grid_size = grid_floor_e
|
||||
.get<WorldEditor::components::
|
||||
buildings_layout_grid_floor>()
|
||||
->grid_size;
|
||||
growth_regions::region parent_region = regions[region_index];
|
||||
// can't split rooms
|
||||
assert(!grid_floor_e.world()
|
||||
.entity(parent_region.region_et)
|
||||
.has<WorldEditor::components::buildings_layout_room>());
|
||||
regions.remove(region_index);
|
||||
RegionRect2i clip_rect = parent_region.rect;
|
||||
int parent_region_index = parent_regions.size();
|
||||
parent_regions.push_back(parent_region);
|
||||
flecs::log::warn("moved parent region %d", region_index);
|
||||
assert(clip_rect.get_area() > region_list.size());
|
||||
const List<Pair<flecs::entity, float> >::Element *e =
|
||||
region_list.front();
|
||||
Set<Vector2i> positions;
|
||||
LocalVector<Pair<flecs::entity, Vector2i> > new_start;
|
||||
int base_index = 0;
|
||||
while (e) {
|
||||
Vector2i pos;
|
||||
int iterations = 1000;
|
||||
while (1) {
|
||||
int i;
|
||||
pos = clip_rect.position +
|
||||
Vector2i(r.get() % clip_rect.size.x,
|
||||
r.get() % clip_rect.size.y);
|
||||
assert(clip_rect.has_point(pos));
|
||||
if (!positions.has(pos)) {
|
||||
bool ok = true;
|
||||
for (i = 0; i < (int)new_start.size(); i++) {
|
||||
if (iterations < 100 &&
|
||||
int_distance(new_start[i].second,
|
||||
pos) < 2) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (iterations < 200 &&
|
||||
int_distance(new_start[i].second,
|
||||
pos) < 1) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
positions.insert(pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
iterations--;
|
||||
if (iterations < 0) {
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < regions.size(); i++) {
|
||||
if (parent_region_index == regions[i].parent_region)
|
||||
flecs::log::dbg(
|
||||
"sibling regions: %d: %s", i,
|
||||
(regions[i].rect.operator String())
|
||||
.ascii()
|
||||
.ptr());
|
||||
}
|
||||
flecs::log::dbg("cell: position: %d, %d", pos.x, pos.y);
|
||||
for (i = 0; i < regions.size(); i++)
|
||||
assert(parent_regions[regions[i].parent_region]
|
||||
.rect.encloses(regions[i].rect));
|
||||
for (i = 0; i < regions.size(); i++)
|
||||
for (j = 0; j < regions.size(); j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
if (regions[i].rect.encloses(regions[j].rect) ||
|
||||
regions[j].rect.intersects(
|
||||
regions[i].rect) ||
|
||||
regions[i].rect.intersects(regions[j].rect))
|
||||
assert(false);
|
||||
}
|
||||
assert(parent_region.rect.has_point(pos));
|
||||
struct region reg;
|
||||
new_start.push_back({ e->get().first, pos });
|
||||
reg.can_grow = true;
|
||||
reg.can_grow_square = true;
|
||||
reg.complete = false;
|
||||
reg.parent = parent_region.region_et;
|
||||
reg.parent_region = parent_region_index;
|
||||
reg.rect = RegionRect2i(pos, Vector2i(1, 1));
|
||||
reg.region_et = e->get().first.id();
|
||||
reg.remains_area = (int)Math::ceil(e->get().second);
|
||||
int cell_id = pos.x + grid_size * pos.y;
|
||||
flecs::entity parent_e =
|
||||
grid_floor_e.world().entity(reg.parent);
|
||||
flecs::entity cell_e = update_cell(grid_floor_e, parent_e,
|
||||
e->get().first, cell_id);
|
||||
reg.seed_et = cell_e.id();
|
||||
regions.push_back(reg);
|
||||
e = e->next();
|
||||
base_index++;
|
||||
complete = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
void BuildingLayoutGraph::graph_module::zones_graph_module(
|
||||
flecs::world &ecs, const String &module_name)
|
||||
{
|
||||
@@ -451,7 +638,8 @@ void BuildingLayoutGraph::graph_module::zones_graph_module(
|
||||
buildings_layout_floor_index
|
||||
&rindex) {
|
||||
if (index.index == rindex.index) {
|
||||
sum += MAX(rarea.area, MIN_ROOM_SIZE);
|
||||
sum += MAX(rarea.area, MIN_ROOM_SIZE) *
|
||||
10 / 8;
|
||||
assert(sum >= 0.0f);
|
||||
count_rooms++;
|
||||
}
|
||||
@@ -567,6 +755,7 @@ BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs)
|
||||
ecs.component<WorldEditor::components::buildings_layout_order>()
|
||||
.member<int>("index");
|
||||
ecs.component<WorldEditor::components::belongs>();
|
||||
ecs.component<WorldEditor::components::belongs_room>();
|
||||
#if 0
|
||||
ecs.component<
|
||||
WorldEditor::components::buildings_layout_commands::command>()
|
||||
@@ -1072,10 +1261,11 @@ BuildingLayoutGraph::graph_module::graph_module(flecs::world &ecs)
|
||||
->get_layout_grid_base();
|
||||
flecs::entity grid_e =
|
||||
grid_base_e.lookup(graph_e.name());
|
||||
flecs::log::warn("deleting entity %s",
|
||||
grid_e.path().c_str());
|
||||
if (grid_e.is_valid())
|
||||
if (grid_e.is_valid()) {
|
||||
flecs::log::warn("deleting entity %s",
|
||||
grid_e.path().c_str());
|
||||
grid_e.destruct();
|
||||
}
|
||||
it.world().defer_resume();
|
||||
});
|
||||
ecs.system<const WorldEditor::components::buildings_layout_grid_size>(
|
||||
|
||||
@@ -1,87 +1,235 @@
|
||||
#ifndef GRAPH_MODULE_H_
|
||||
#define GRAPH_MODULE_H_
|
||||
struct growth_regions {
|
||||
struct region {
|
||||
flecs::entity_t parent;
|
||||
flecs::entity_t seed_et;
|
||||
flecs::entity_t region_et;
|
||||
Rect2i rect;
|
||||
int remains_area;
|
||||
bool can_grow_square;
|
||||
bool can_grow;
|
||||
bool complete;
|
||||
bool can_grow_region() const
|
||||
{
|
||||
bool ret = can_grow;
|
||||
if (remains_area <= 0)
|
||||
ret = false;
|
||||
return ret;
|
||||
#include <core/math/vector2.h>
|
||||
#include "grid_misc.h"
|
||||
#include "grow_job.h"
|
||||
struct RegionRect2i {
|
||||
Point2i position;
|
||||
Size2i size;
|
||||
|
||||
const Point2i &get_position() const
|
||||
{
|
||||
return position;
|
||||
}
|
||||
void set_position(const Point2i &p_position)
|
||||
{
|
||||
position = p_position;
|
||||
}
|
||||
const Size2i &get_size() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
void set_size(const Size2i &p_size)
|
||||
{
|
||||
size = p_size;
|
||||
}
|
||||
|
||||
int get_area() const
|
||||
{
|
||||
return size.width * size.height;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Vector2i get_center() const
|
||||
{
|
||||
return position + (size / 2);
|
||||
}
|
||||
|
||||
inline bool intersects(const RegionRect2i &p_rect) const
|
||||
{
|
||||
if (position.x > (p_rect.position.x + p_rect.size.width - 1)) {
|
||||
return false;
|
||||
}
|
||||
bool update_region_size(Rect2i &mrect)
|
||||
{
|
||||
bool ret = false;
|
||||
int old_area = rect.get_area();
|
||||
int new_area = mrect.get_area();
|
||||
int area_diff = new_area - old_area;
|
||||
if (area_diff > 0) {
|
||||
rect = mrect;
|
||||
remains_area -= area_diff;
|
||||
ret = true;
|
||||
}
|
||||
if (remains_area <= 0) {
|
||||
can_grow_square = false;
|
||||
can_grow = false;
|
||||
}
|
||||
flecs::log::dbg("update_region_size %d -> %d",
|
||||
area_diff, ret);
|
||||
return ret;
|
||||
if ((position.x + size.width - 1) < p_rect.position.x) {
|
||||
return false;
|
||||
}
|
||||
if (position.y > (p_rect.position.y + p_rect.size.height - 1)) {
|
||||
return false;
|
||||
}
|
||||
if ((position.y + size.height - 1) < p_rect.position.y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool encloses(const RegionRect2i &p_rect) const
|
||||
{
|
||||
return (p_rect.position.x >= position.x) &&
|
||||
(p_rect.position.y >= position.y) &&
|
||||
((p_rect.position.x + p_rect.size.x) <=
|
||||
(position.x + size.x)) &&
|
||||
((p_rect.position.y + p_rect.size.y) <=
|
||||
(position.y + size.y));
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool has_no_area() const
|
||||
{
|
||||
return (size.x <= 0 || size.y <= 0);
|
||||
}
|
||||
inline RegionRect2i clip(const RegionRect2i &p_rect) const
|
||||
{ /// return a clipped rect
|
||||
|
||||
RegionRect2i new_rect = p_rect;
|
||||
|
||||
if (!intersects(new_rect)) {
|
||||
return RegionRect2i();
|
||||
}
|
||||
|
||||
new_rect.position.x = MAX(p_rect.position.x, position.x);
|
||||
new_rect.position.y = MAX(p_rect.position.y, position.y);
|
||||
|
||||
Point2 p_rect_end = p_rect.position + p_rect.size;
|
||||
Point2 end = position + size;
|
||||
|
||||
new_rect.size.x =
|
||||
(int)(MIN(p_rect_end.x, end.x) - new_rect.position.x);
|
||||
new_rect.size.y =
|
||||
(int)(MIN(p_rect_end.y, end.y) - new_rect.position.y);
|
||||
|
||||
return new_rect;
|
||||
}
|
||||
|
||||
inline RegionRect2i merge(const RegionRect2i &p_rect) const
|
||||
{ ///< return a merged rect
|
||||
|
||||
RegionRect2i new_rect;
|
||||
|
||||
new_rect.position.x = MIN(p_rect.position.x, position.x);
|
||||
new_rect.position.y = MIN(p_rect.position.y, position.y);
|
||||
|
||||
new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x,
|
||||
position.x + size.x);
|
||||
new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y,
|
||||
position.y + size.y);
|
||||
|
||||
new_rect.size =
|
||||
new_rect.size - new_rect.position; //make relative again
|
||||
|
||||
return new_rect;
|
||||
};
|
||||
Vector<struct region> regions;
|
||||
bool complete;
|
||||
bool check_region(int index, const Rect2i &rect) const
|
||||
bool has_point(const Point2 &p_point) const
|
||||
{
|
||||
int i;
|
||||
bool ret = true;
|
||||
for (i = 0; i < regions.size(); i++) {
|
||||
if (i == index)
|
||||
continue;
|
||||
if (rect.intersects(regions[i].rect)) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
if (p_point.x < position.x) {
|
||||
return false;
|
||||
}
|
||||
flecs::log::dbg("check_region: %d -> %d", index, ret);
|
||||
return ret;
|
||||
if (p_point.y < position.y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_point.x >= (position.x + size.x)) {
|
||||
return false;
|
||||
}
|
||||
if (p_point.y >= (position.y + size.y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool update_region_size(int index, Rect2i &mrect)
|
||||
|
||||
bool operator==(const RegionRect2i &p_rect) const
|
||||
{
|
||||
return position == p_rect.position && size == p_rect.size;
|
||||
}
|
||||
bool operator!=(const RegionRect2i &p_rect) const
|
||||
{
|
||||
return position != p_rect.position || size != p_rect.size;
|
||||
}
|
||||
|
||||
RegionRect2i grow(int p_by) const
|
||||
{
|
||||
RegionRect2i g = *this;
|
||||
g.position.x -= p_by;
|
||||
g.position.y -= p_by;
|
||||
g.size.width += p_by * 2;
|
||||
g.size.height += p_by * 2;
|
||||
return g;
|
||||
}
|
||||
|
||||
inline RegionRect2i grow_margin(Margin p_margin, int p_amount) const
|
||||
{
|
||||
RegionRect2i g = *this;
|
||||
g = g.grow_individual((MARGIN_LEFT == p_margin) ? p_amount : 0,
|
||||
(MARGIN_TOP == p_margin) ? p_amount : 0,
|
||||
(MARGIN_RIGHT == p_margin) ? p_amount : 0,
|
||||
(MARGIN_BOTTOM == p_margin) ? p_amount :
|
||||
0);
|
||||
return g;
|
||||
}
|
||||
|
||||
inline RegionRect2i grow_individual(int p_left, int p_top, int p_right,
|
||||
int p_bottom) const
|
||||
{
|
||||
RegionRect2i g = *this;
|
||||
g.position.x -= p_left;
|
||||
g.position.y -= p_top;
|
||||
g.size.width += p_left + p_right;
|
||||
g.size.height += p_top + p_bottom;
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ RegionRect2i expand(const Vector2i &p_vector) const
|
||||
{
|
||||
RegionRect2i r = *this;
|
||||
r.expand_to(p_vector);
|
||||
return r;
|
||||
}
|
||||
|
||||
inline void expand_to(const Point2i &p_vector)
|
||||
{
|
||||
Point2i begin = position;
|
||||
Point2i end = position + size;
|
||||
|
||||
if (p_vector.x < begin.x) {
|
||||
begin.x = p_vector.x;
|
||||
}
|
||||
if (p_vector.y < begin.y) {
|
||||
begin.y = p_vector.y;
|
||||
}
|
||||
|
||||
if (p_vector.x > end.x) {
|
||||
end.x = p_vector.x;
|
||||
}
|
||||
if (p_vector.y > end.y) {
|
||||
end.y = p_vector.y;
|
||||
}
|
||||
|
||||
position = begin;
|
||||
size = end - begin;
|
||||
}
|
||||
|
||||
operator String() const
|
||||
{
|
||||
return String(position) + ", " + String(size);
|
||||
}
|
||||
|
||||
operator Rect2() const
|
||||
{
|
||||
return Rect2(position, size);
|
||||
}
|
||||
RegionRect2i(const Rect2 &p_r2)
|
||||
: position(p_r2.position)
|
||||
, size(p_r2.size)
|
||||
{
|
||||
}
|
||||
RegionRect2i(const Rect2i &p_r2)
|
||||
: position(p_r2.position)
|
||||
, size(p_r2.size)
|
||||
{
|
||||
}
|
||||
RegionRect2i()
|
||||
{
|
||||
}
|
||||
RegionRect2i(int p_x, int p_y, int p_width, int p_height)
|
||||
: position(Point2(p_x, p_y))
|
||||
, size(Size2(p_width, p_height))
|
||||
{
|
||||
}
|
||||
RegionRect2i(const Point2 &p_pos, const Size2 &p_size)
|
||||
: position(p_pos)
|
||||
, size(p_size)
|
||||
{
|
||||
bool ret = false;
|
||||
bool ok = check_region(index, mrect);
|
||||
if (ok)
|
||||
ret = regions.write[index].update_region_size(mrect);
|
||||
flecs::log::dbg("update_region_size %d -> %d", index, ret);
|
||||
return ret;
|
||||
}
|
||||
void create_region(flecs::entity floor_e, flecs::entity seed_e,
|
||||
flecs::entity parent_e, flecs::entity region_e,
|
||||
const Vector2i &position, float area);
|
||||
flecs::entity create_cell(flecs::entity floor_e, flecs::entity region_e,
|
||||
int id);
|
||||
};
|
||||
|
||||
struct make_random {
|
||||
int seed;
|
||||
int next;
|
||||
make_random(int seed)
|
||||
: seed(seed)
|
||||
, next(seed)
|
||||
{
|
||||
}
|
||||
int get()
|
||||
{
|
||||
next = next * 1103515245 + 12345;
|
||||
return (int)((unsigned)next >> 16) % 32768;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
477
src/modules/stream/ui/region_tree.cpp
Normal file
477
src/modules/stream/ui/region_tree.cpp
Normal file
@@ -0,0 +1,477 @@
|
||||
#undef NDEBUG
|
||||
#include <cassert>
|
||||
#include <core/ustring.h>
|
||||
#include <core/list.h>
|
||||
#include "region_tree.h"
|
||||
|
||||
void region_tree::split(const List<struct region> ®ions)
|
||||
{
|
||||
assert(children.size() == 0);
|
||||
const List<struct region>::Element *e = regions.front();
|
||||
while (e) {
|
||||
struct region_tree *child = memnew(struct region_tree);
|
||||
child->parent = this;
|
||||
child->region = e->get();
|
||||
children.push_back(child);
|
||||
e = e->next();
|
||||
}
|
||||
}
|
||||
|
||||
const region_tree *region_tree::find(flecs::entity_t which) const
|
||||
{
|
||||
int i;
|
||||
List<const struct region_tree *> queue;
|
||||
queue.push_back(this);
|
||||
while (!queue.empty()) {
|
||||
const struct region_tree *item = queue.front()->get();
|
||||
if (item->region.region_et == which)
|
||||
return item;
|
||||
queue.pop_front();
|
||||
for (i = 0; i < item->children.size(); i++)
|
||||
queue.push_back(item->children[i]);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
region_tree *region_tree::find(flecs::entity_t which)
|
||||
{
|
||||
int i;
|
||||
List<struct region_tree *> queue;
|
||||
queue.push_back(this);
|
||||
while (!queue.empty()) {
|
||||
struct region_tree *item = queue.front()->get();
|
||||
if (item->region.region_et == which)
|
||||
return item;
|
||||
queue.pop_front();
|
||||
for (i = 0; i < item->children.size(); i++)
|
||||
queue.push_back(item->children[i]);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void region_tree::dump(flecs::entity grid_floor_e) const
|
||||
{
|
||||
List<Pair<int, const struct region_tree *> > queue;
|
||||
queue.push_back({ 0, this });
|
||||
flecs::log::warn("tree dump:");
|
||||
while (!queue.empty()) {
|
||||
int tabs = queue.front()->get().first;
|
||||
const struct region_tree *item = queue.front()->get().second;
|
||||
queue.pop_front();
|
||||
String stabs = "\t";
|
||||
int i;
|
||||
for (i = 0; i < tabs; i++)
|
||||
stabs += "\t";
|
||||
flecs::log::warn("%sregion: %s", stabs.ascii().ptr(),
|
||||
grid_floor_e.world()
|
||||
.entity(item->region.region_et)
|
||||
.path()
|
||||
.c_str());
|
||||
flecs::log::warn(
|
||||
"%srect: %s", stabs.ascii().ptr(),
|
||||
(item->region.rect.operator String()).ascii().ptr());
|
||||
flecs::log::warn("");
|
||||
for (i = 0; i < item->children.size(); i++)
|
||||
queue.push_back({ tabs + 1, item->children[i] });
|
||||
}
|
||||
}
|
||||
|
||||
void region_tree::grow()
|
||||
{
|
||||
#ifndef TESTS
|
||||
List<struct region_tree *> grow_list;
|
||||
List<struct region_tree *> queue;
|
||||
List<struct region_tree *>::Element *e, *e1;
|
||||
if (region.complete)
|
||||
queue.push_back(this);
|
||||
while (!queue.empty()) {
|
||||
int i;
|
||||
struct region_tree *item = queue.front()->get();
|
||||
queue.pop_front();
|
||||
if (item->region.can_grow || item->region.can_grow_square)
|
||||
continue;
|
||||
for (i = 0; i < (int)item->children.size(); i++) {
|
||||
if (item->children[i]->region.can_grow)
|
||||
grow_list.push_back(item->children[i]);
|
||||
queue.push_back(item->children[i]);
|
||||
}
|
||||
}
|
||||
flecs::log::warn("grow list: %d", grow_list.size());
|
||||
e = grow_list.front();
|
||||
int amount = 0;
|
||||
while (e) {
|
||||
struct region_tree *item = e->get();
|
||||
flecs::log::warn(
|
||||
"item: %s",
|
||||
(item->region.rect.operator String()).ascii().ptr());
|
||||
if (!item->region.can_grow) {
|
||||
e = e->next();
|
||||
flecs::log::err("can't grow");
|
||||
continue;
|
||||
}
|
||||
RegionRect2i candidate = item->region.rect.grow(1);
|
||||
e1 = grow_list.front();
|
||||
bool ok = true;
|
||||
while (e1) {
|
||||
struct region_tree *check_item = e1->get();
|
||||
if (item != check_item) {
|
||||
flecs::log::warn("check item: %s candidate: %s",
|
||||
(check_item->region.rect
|
||||
.operator String())
|
||||
.ascii()
|
||||
.ptr(),
|
||||
(candidate.operator String())
|
||||
.ascii()
|
||||
.ptr());
|
||||
if (!check_item->check_candidate(candidate)) {
|
||||
ok = false;
|
||||
flecs::log::err("check failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
e1 = e1->next();
|
||||
}
|
||||
if (ok) {
|
||||
item->update_candidate(candidate);
|
||||
amount++;
|
||||
} else {
|
||||
flecs::log::err("can't update candidate");
|
||||
}
|
||||
e = e->next();
|
||||
}
|
||||
#if 0
|
||||
assert(false);
|
||||
if (region.complete)
|
||||
queue.push_back(this);
|
||||
while (!queue.empty()) {
|
||||
struct region_tree *item = queue.front()->get();
|
||||
queue.pop_front();
|
||||
if (item->region.can_grow || item->region.can_grow_square)
|
||||
continue;
|
||||
int i, j;
|
||||
int amount = 0;
|
||||
amount = 0;
|
||||
for (i = 0; i < (int)item->children.size(); i++) {
|
||||
if (item->children[i]->region.can_grow_square) {
|
||||
RegionRect2i candidate =
|
||||
item->children[i]->region.rect.grow(1);
|
||||
if (!item->region.rect.encloses(candidate)) {
|
||||
item->children.write[i]
|
||||
->region.can_grow_square =
|
||||
false;
|
||||
continue;
|
||||
}
|
||||
bool ok = item->check_candidate(i, candidate);
|
||||
if (ok) {
|
||||
item->children.write[i]
|
||||
->update_candidate(candidate);
|
||||
amount++;
|
||||
} else
|
||||
item->children.write[i]
|
||||
->region.can_grow_square =
|
||||
false;
|
||||
}
|
||||
}
|
||||
if (amount > 0) /* try again later */
|
||||
queue.push_back(item);
|
||||
#if 0
|
||||
for (i = 0; i < (int)item->children.size(); i++) {
|
||||
if (!can_grow_square)
|
||||
item->children.write[i]
|
||||
->region.can_grow_square =
|
||||
false;
|
||||
}
|
||||
while (can_grow) {
|
||||
amount = 0;
|
||||
for (i = 0; i < (int)item->children.size();
|
||||
i++) {
|
||||
if (!item->children[i]->region.can_grow)
|
||||
continue;
|
||||
int orig_area =
|
||||
item->children[i]
|
||||
->region.rect.get_area();
|
||||
RegionRect2i candidate =
|
||||
item->children[i]->region.rect;
|
||||
candidate.size.x += 1;
|
||||
if (!item->region.rect.encloses(
|
||||
candidate))
|
||||
continue;
|
||||
bool ok = item->check_candidate(
|
||||
i, candidate);
|
||||
if (ok) {
|
||||
item->children.write[i]
|
||||
->update_candidate(
|
||||
candidate);
|
||||
amount++;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < (int)item->children.size();
|
||||
i++) {
|
||||
if (!item->children[i]->region.can_grow)
|
||||
continue;
|
||||
int orig_area =
|
||||
item->children[i]
|
||||
->region.rect.get_area();
|
||||
RegionRect2i candidate =
|
||||
item->children[i]->region.rect;
|
||||
candidate.size.x += 1;
|
||||
candidate.position.x -= 1;
|
||||
if (!item->region.rect.encloses(
|
||||
candidate))
|
||||
continue;
|
||||
bool ok = item->check_candidate(
|
||||
i, candidate);
|
||||
if (ok) {
|
||||
item->children.write[i]
|
||||
->update_candidate(
|
||||
candidate);
|
||||
amount++;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < (int)item->children.size();
|
||||
i++) {
|
||||
if (!item->children[i]->region.can_grow)
|
||||
continue;
|
||||
int orig_area =
|
||||
item->children[i]
|
||||
->region.rect.get_area();
|
||||
RegionRect2i candidate =
|
||||
item->children[i]->region.rect;
|
||||
candidate.size.y += 1;
|
||||
if (!item->region.rect.encloses(
|
||||
candidate))
|
||||
continue;
|
||||
bool ok = item->check_candidate(
|
||||
i, candidate);
|
||||
if (ok) {
|
||||
item->children.write[i]
|
||||
->update_candidate(
|
||||
candidate);
|
||||
amount++;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < (int)item->children.size();
|
||||
i++) {
|
||||
if (!item->children[i]->region.can_grow)
|
||||
continue;
|
||||
int orig_area =
|
||||
item->children[i]
|
||||
->region.rect.get_area();
|
||||
RegionRect2i candidate =
|
||||
item->children[i]->region.rect;
|
||||
candidate.size.y += 1;
|
||||
candidate.position.y -= 1;
|
||||
if (!item->region.rect.encloses(
|
||||
candidate))
|
||||
continue;
|
||||
bool ok = item->check_candidate(
|
||||
i, candidate);
|
||||
if (ok) {
|
||||
item->children.write[i]
|
||||
->update_candidate(
|
||||
candidate);
|
||||
amount++;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < (int)item->children.size();
|
||||
i++) {
|
||||
if (item->children[i]
|
||||
->region.remains_area <
|
||||
-100) {
|
||||
item->children[i]
|
||||
->region.can_grow =
|
||||
false;
|
||||
item->children[i]
|
||||
->region.complete =
|
||||
true;
|
||||
}
|
||||
}
|
||||
if (amount == 0)
|
||||
can_grow = false;
|
||||
}
|
||||
#endif
|
||||
for (i = 0; i < (int)item->children.size(); i++) {
|
||||
queue.push_back(item->children[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void region_tree::place(flecs::entity grid_floor_e) const
|
||||
{
|
||||
#ifndef TESTS
|
||||
int i, j;
|
||||
List<const struct region_tree *> queue;
|
||||
int grid_size = grid_floor_e
|
||||
.get<WorldEditor::components::
|
||||
buildings_layout_grid_floor>()
|
||||
->grid_size;
|
||||
for (i = region.rect.position.x;
|
||||
i < region.rect.position.x + region.rect.size.x; i++)
|
||||
for (j = region.rect.position.y;
|
||||
j < region.rect.position.y + region.rect.size.y; j++) {
|
||||
int cell_id = i + grid_size * j;
|
||||
String cname = "cell_" + itos(cell_id);
|
||||
flecs::entity cell_e =
|
||||
grid_floor_e.lookup(cname.ascii().ptr());
|
||||
if (cell_e.is_valid())
|
||||
cell_e.destruct();
|
||||
}
|
||||
queue.push_back(this);
|
||||
while (!queue.empty()) {
|
||||
const struct region_tree *item = queue.front()->get();
|
||||
queue.pop_front();
|
||||
if (item->is_leaf()) {
|
||||
const RegionRect2i &rect = item->region.rect;
|
||||
for (i = rect.position.x;
|
||||
i < rect.position.x + rect.size.x; i++)
|
||||
for (j = rect.position.x;
|
||||
j < rect.position.x + rect.size.x; j++) {
|
||||
int cell_id = i + grid_size * j;
|
||||
update_cell(grid_floor_e,
|
||||
item->region.parent,
|
||||
item->region.region_et,
|
||||
cell_id);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < (int)item->children.size(); i++) {
|
||||
queue.push_back(item->children[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void region_tree::get_rects(List<RegionRect2i> *rect_list) const
|
||||
{
|
||||
#ifndef TESTS
|
||||
List<const struct region_tree *> queue;
|
||||
queue.push_back(this);
|
||||
while (!queue.empty()) {
|
||||
int i;
|
||||
const struct region_tree *item = queue.front()->get();
|
||||
queue.pop_front();
|
||||
if (item->children.size() == 0) {
|
||||
flecs::log::warn("rect: %s",
|
||||
(item->region.rect.operator String())
|
||||
.ascii()
|
||||
.ptr());
|
||||
rect_list->push_back(item->region.rect);
|
||||
}
|
||||
for (i = 0; i < (int)item->children.size(); i++)
|
||||
queue.push_back(item->children[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool region_tree::check_candidate(int i, const RegionRect2i &candidate) const
|
||||
{
|
||||
int j;
|
||||
bool ok = true;
|
||||
#ifndef TESTS
|
||||
if (!region.rect.encloses(candidate))
|
||||
return false;
|
||||
for (j = 0; j < children.size(); j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
if (candidate.intersects(children[j]->region.rect)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (candidate.encloses(children[j]->region.rect)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (children[j]->region.rect.encloses(candidate)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (children[j]->region.rect.intersects(candidate)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool region_tree::check_candidate(const RegionRect2i &candidate) const
|
||||
{
|
||||
bool ok = true;
|
||||
#ifndef TESTS
|
||||
if (parent && !parent->region.rect.encloses(candidate))
|
||||
return false;
|
||||
if (candidate.intersects(region.rect))
|
||||
ok = false;
|
||||
if (candidate.encloses(region.rect))
|
||||
ok = false;
|
||||
if (region.rect.encloses(candidate))
|
||||
ok = false;
|
||||
if (region.rect.intersects(candidate))
|
||||
ok = false;
|
||||
#endif
|
||||
return ok;
|
||||
}
|
||||
|
||||
flecs::entity region_tree::update_cell(flecs::entity grid_floor_e,
|
||||
flecs::entity_t parent_et,
|
||||
flecs::entity_t region_et, int id) const
|
||||
{
|
||||
flecs::entity region_e = grid_floor_e.world().entity(region_et);
|
||||
flecs::entity parent_e = grid_floor_e.world().entity(parent_et);
|
||||
flecs::log::dbg("create_cell: %s %d", region_e.path().c_str(), id);
|
||||
#ifndef TESTS
|
||||
String pname("cell_" + itos(id));
|
||||
flecs::entity cell_e = grid_floor_e.lookup(pname.ascii().ptr());
|
||||
if (!cell_e.is_valid()) {
|
||||
flecs::log::warn("creating new cell %s", pname.ascii().ptr());
|
||||
cell_e = grid_floor_e.world()
|
||||
.entity(pname.ascii().ptr())
|
||||
.child_of(grid_floor_e);
|
||||
assert(cell_e.is_valid());
|
||||
}
|
||||
// assert(cell_e.has<WorldEditor::components::belongs>(parent_e));
|
||||
/* already there */
|
||||
grid_floor_e
|
||||
.get_mut<WorldEditor::components::buildings_layout_grid_floor>()
|
||||
->cells.insert(id);
|
||||
grid_floor_e.modified<
|
||||
WorldEditor::components::buildings_layout_grid_floor>();
|
||||
cell_e.set<WorldEditor::components::buildings_layout_grid_cell>(
|
||||
{ String(region_e.name()), id });
|
||||
/* cell_e.remove<WorldEditor::components::belongs>(parent_e); */
|
||||
cell_e.add<WorldEditor::components::belongs>(region_e);
|
||||
if (region_e.has<WorldEditor::components::buildings_layout_room>()) {
|
||||
int mcount = 0;
|
||||
cell_e.each<WorldEditor::components::belongs_room>(
|
||||
[&mcount, region_e, cell_e](flecs::entity e) {
|
||||
flecs::log::err("adding region %s to cell %s",
|
||||
region_e.path().c_str(),
|
||||
cell_e.path().c_str());
|
||||
flecs::log::err("already belongs to %s",
|
||||
e.path().c_str());
|
||||
assert(mcount == 0);
|
||||
assert(region_e.id() == e.id());
|
||||
mcount++;
|
||||
});
|
||||
if (mcount == 0)
|
||||
cell_e.add<WorldEditor::components::belongs_room>(
|
||||
region_e);
|
||||
}
|
||||
#else
|
||||
flecs::entity cell_e;
|
||||
#endif
|
||||
return cell_e;
|
||||
}
|
||||
|
||||
void region_tree::update_candidate(const RegionRect2i &candidate)
|
||||
{
|
||||
#if 0
|
||||
int orig_area = region.rect.get_area();
|
||||
region.rect = candidate;
|
||||
int new_area = candidate.get_area();
|
||||
int avalue = MAX(0, new_area - orig_area);
|
||||
region.remains_area -= avalue;
|
||||
#endif
|
||||
}
|
||||
29
src/modules/stream/ui/region_tree.h
Normal file
29
src/modules/stream/ui/region_tree.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* ~/godot-projects/streaming_world/src/modules/stream/ui/region_tree.h */
|
||||
#ifndef REGION_TREE_H_
|
||||
#define REGION_TREE_H_
|
||||
#include "growth_regions.h"
|
||||
struct region_tree {
|
||||
struct region region;
|
||||
Vector<struct region_tree *> children;
|
||||
struct region_tree *parent;
|
||||
void split(const List<struct region> ®ions);
|
||||
const struct region_tree *find(flecs::entity_t which) const;
|
||||
struct region_tree *find(flecs::entity_t which);
|
||||
bool is_leaf() const
|
||||
{
|
||||
return children.size() == 0;
|
||||
}
|
||||
void dump(flecs::entity grid_floor_e) const;
|
||||
void grow();
|
||||
void place(flecs::entity grid_floor_e) const;
|
||||
void get_rects(List<RegionRect2i> *rect_list) const;
|
||||
|
||||
private:
|
||||
bool check_candidate(int i, const RegionRect2i &candidate) const;
|
||||
bool check_candidate(const RegionRect2i &candidate) const;
|
||||
flecs::entity update_cell(flecs::entity grid_floor_e,
|
||||
flecs::entity_t parent_et,
|
||||
flecs::entity_t region_et, int id) const;
|
||||
void update_candidate(const RegionRect2i &candidate);
|
||||
};
|
||||
#endif // REGION_TREE_H_
|
||||
@@ -1,17 +1,64 @@
|
||||
all: graph_test entity_test rect2i
|
||||
all: graph_test entity_test rect2i regions
|
||||
LIBS=
|
||||
# LIBS= ./godot_mockery/libgodot-mockery.a
|
||||
|
||||
CFLAGS = -I../src/flecs/distr
|
||||
CXXFLAGS = -I../src/flecs/distr -I../src/modules/stream/ui -I../src/godot -I../src/godot/platform/x11
|
||||
CFLAGS = -I../src/flecs/distr -g
|
||||
CXXFLAGS = -I../src/flecs/distr -I../src/modules/stream/ui -I./godot_mockery -I../src/godot -I../src/godot/platform/x11 -I. -I../src/modules/stream -g
|
||||
#CXXFLAGS = -I../src/flecs/distr -I../src/modules/stream/ui -I../src/godot -I../src/godot/platform/x11 -I. -I../src/modules/stream -g
|
||||
|
||||
graph_test: graph_module.o flecs.o
|
||||
$(CXX) -o $@ graph_module.o flecs.o
|
||||
graph_test: graph_module.o flecs.o $(LIBS)
|
||||
$(CXX) -o $@ graph_module.o flecs.o $(LIBS)
|
||||
|
||||
entity_test: entity_test.o flecs.o
|
||||
$(CXX) -o $@ entity_test.o flecs.o
|
||||
entity_test: entity_test.o flecs.o $(LIBS)
|
||||
$(CXX) -o $@ entity_test.o flecs.o $(LIBS)
|
||||
|
||||
flecs.o: ../src/flecs/distr/flecs.c
|
||||
$(CC) -o $@ -c $<
|
||||
rect2i: rect2i.o
|
||||
$(CXX) -o $@ rect2i.o
|
||||
rect2i: rect2i.o $(LIBS)
|
||||
$(CXX) -o $@ rect2i.o $(LIBS)
|
||||
|
||||
.PHONY: all
|
||||
region_tree.o: ../src/modules/stream/ui/region_tree.cpp
|
||||
$(CC) $(CFLAGS) $(CXXFLAGS) -DTESTS -o $@ -c $<
|
||||
|
||||
regions: regions.o flecs.o region_tree.o godot_mockery/core/os/memory.o godot_mockery/core/ustring.o $(LIBS)
|
||||
$(CXX) -o $@ regions.o flecs.o region_tree.o godot_mockery/core/os/memory.o godot_mockery/core/ustring.o $(LIBS)
|
||||
|
||||
MOCK_OBJS=godot_mockery/core/os/memory.o godot_mockery/core/error_macros.o \
|
||||
godot_mockery/core/ustring.o godot_mockery/core/array.o \
|
||||
godot_mockery/core/variant.o godot_mockery/core/variant_op.o \
|
||||
godot_mockery/core/pool_allocator.o godot_mockery/core/node_path.o \
|
||||
godot_mockery/core/pool_vector.o godot_mockery/core/reference.o \
|
||||
godot_mockery/core/object.o godot_mockery/core/ref_ptr.o \
|
||||
godot_mockery/core/string_name.o godot_mockery/core/method_bind.o \
|
||||
godot_mockery/core/class_db.o godot_mockery/core/dictionary.o \
|
||||
godot_mockery/core/color.o godot_mockery/core/math/plane.o \
|
||||
godot_mockery/core/math/transform.o godot_mockery/core/math/basis.o \
|
||||
godot_mockery/core/math/vector2.o godot_mockery/core/math/vector3.o \
|
||||
godot_mockery/core/math/math_funcs.o godot_mockery/core/resource.o \
|
||||
godot_mockery/core/math/random_pcg.o godot_mockery/core/math/transform_2d.o \
|
||||
godot_mockery/core/core_string_names.o godot_mockery/core/os/os.o \
|
||||
|
||||
|
||||
|
||||
./godot_mockery/libgodot-mockery.a: $(MOCK_OBJS)
|
||||
ar rcs $@ $(MOCK_OBJS)
|
||||
|
||||
godot_mockery/core/os/memory.o: godot_mockery/core/os/memory.cpp
|
||||
$(CC) $(CFLAGS) $(CXXFLAGS) -DTESTS -o $@ -c $<
|
||||
godot_mockery/core/error_macros.o: godot_mockery/core/error_macros.cpp
|
||||
$(CC) $(CFLAGS) $(CXXFLAGS) -DTESTS -o $@ -c $<
|
||||
godot_mockery/core/ustring.o: godot_mockery/core/ustring.cpp
|
||||
$(CC) $(CFLAGS) $(CXXFLAGS) -DTESTS -o $@ -c $<
|
||||
godot_mockery/core/array.o: ../src/godot/core/array.cpp
|
||||
$(CC) $(CFLAGS) $(CXXFLAGS) -DTESTS -o $@ -c $<
|
||||
godot_mockery/core/%.o: ../src/godot/core/%.cpp
|
||||
$(CC) $(CFLAGS) $(CXXFLAGS) -DTESTS -o $@ -c $<
|
||||
godot_mockery/core/math/%.o: ../src/godot/core/math/%.cpp
|
||||
mkdir -p godot_mockery/core/math
|
||||
$(CC) $(CFLAGS) $(CXXFLAGS) -DTESTS -o $@ -c $<
|
||||
|
||||
clean:
|
||||
rm -f godot_mockery/core/*.o godot_mockery/core/os/*.o *.o graph_test entity_test rect2i regions
|
||||
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
Binary file not shown.
379
tests/godot_mockery/core/cowdata.h
Normal file
379
tests/godot_mockery/core/cowdata.h
Normal file
@@ -0,0 +1,379 @@
|
||||
/**************************************************************************/
|
||||
/* cowdata.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef COWDATA_H
|
||||
#define COWDATA_H
|
||||
|
||||
#include <string.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include "core/error_macros.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/safe_refcount.h"
|
||||
|
||||
template <class T>
|
||||
class Vector;
|
||||
class String;
|
||||
class CharString;
|
||||
template <class T, class V>
|
||||
class VMap;
|
||||
|
||||
#if !defined(NO_THREADS)
|
||||
SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t)
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
class CowData {
|
||||
template <class TV>
|
||||
friend class Vector;
|
||||
friend class String;
|
||||
friend class CharString;
|
||||
template <class TV, class VV>
|
||||
friend class VMap;
|
||||
|
||||
private:
|
||||
mutable T *_ptr;
|
||||
|
||||
// internal helpers
|
||||
|
||||
_FORCE_INLINE_ SafeNumeric<uint32_t> *_get_refcount() const {
|
||||
if (!_ptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<SafeNumeric<uint32_t> *>(_ptr) - 2;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t *_get_size() const {
|
||||
if (!_ptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<uint32_t *>(_ptr) - 1;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const {
|
||||
//return nearest_power_of_2_templated(p_elements*sizeof(T)+sizeof(SafeRefCount)+sizeof(int));
|
||||
return next_power_of_2(p_elements * sizeof(T));
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const {
|
||||
#if defined(_add_overflow) && defined(_mul_overflow)
|
||||
size_t o;
|
||||
size_t p;
|
||||
if (_mul_overflow(p_elements, sizeof(T), &o)) {
|
||||
*out = 0;
|
||||
return false;
|
||||
}
|
||||
*out = next_power_of_2(o);
|
||||
if (_add_overflow(o, static_cast<size_t>(32), &p)) {
|
||||
return false; //no longer allocated here
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
// Speed is more important than correctness here, do the operations unchecked
|
||||
// and hope the best
|
||||
*out = _get_alloc_size(p_elements);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void _unref(void *p_data);
|
||||
void _ref(const CowData *p_from);
|
||||
void _ref(const CowData &p_from);
|
||||
uint32_t _copy_on_write();
|
||||
|
||||
public:
|
||||
void operator=(const CowData<T> &p_from) { _ref(p_from); }
|
||||
|
||||
_FORCE_INLINE_ T *ptrw() {
|
||||
_copy_on_write();
|
||||
return _ptr;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ const T *ptr() const {
|
||||
return _ptr;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int size() const {
|
||||
uint32_t *size = (uint32_t *)_get_size();
|
||||
if (size) {
|
||||
return *size;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void clear() { resize(0); }
|
||||
_FORCE_INLINE_ bool empty() const { return _ptr == nullptr; }
|
||||
|
||||
_FORCE_INLINE_ void set(int p_index, const T &p_elem) {
|
||||
assert(p_index < size());
|
||||
_copy_on_write();
|
||||
_ptr[p_index] = p_elem;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ T &get_m(int p_index) {
|
||||
CRASH_BAD_INDEX(p_index, size());
|
||||
_copy_on_write();
|
||||
return _ptr[p_index];
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ const T &get(int p_index) const {
|
||||
CRASH_BAD_INDEX(p_index, size());
|
||||
|
||||
return _ptr[p_index];
|
||||
}
|
||||
|
||||
Error resize(int p_size);
|
||||
|
||||
_FORCE_INLINE_ void remove(int p_index) {
|
||||
ERR_FAIL_INDEX(p_index, size());
|
||||
T *p = ptrw();
|
||||
int len = size();
|
||||
for (int i = p_index; i < len - 1; i++) {
|
||||
p[i] = p[i + 1];
|
||||
};
|
||||
|
||||
resize(len - 1);
|
||||
};
|
||||
|
||||
Error insert(int p_pos, const T &p_val) {
|
||||
ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER);
|
||||
resize(size() + 1);
|
||||
for (int i = (size() - 1); i > p_pos; i--) {
|
||||
set(i, get(i - 1));
|
||||
}
|
||||
set(p_pos, p_val);
|
||||
|
||||
return OK;
|
||||
};
|
||||
|
||||
int find(const T &p_val, int p_from = 0) const;
|
||||
|
||||
_FORCE_INLINE_ CowData();
|
||||
_FORCE_INLINE_ ~CowData();
|
||||
_FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); };
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void CowData<T>::_unref(void *p_data) {
|
||||
if (!p_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
SafeNumeric<uint32_t> *refc = _get_refcount();
|
||||
|
||||
if (refc->decrement() > 0) {
|
||||
return; // still in use
|
||||
}
|
||||
// clean up
|
||||
|
||||
if (!std::is_trivially_destructible<T>::value) {
|
||||
uint32_t *count = _get_size();
|
||||
T *data = (T *)(count + 1);
|
||||
|
||||
for (uint32_t i = 0; i < *count; ++i) {
|
||||
// call destructors
|
||||
data[i].~T();
|
||||
}
|
||||
}
|
||||
|
||||
// free mem
|
||||
Memory::free_static((uint8_t *)p_data, true);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint32_t CowData<T>::_copy_on_write() {
|
||||
if (!_ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SafeNumeric<uint32_t> *refc = _get_refcount();
|
||||
|
||||
uint32_t rc = refc->get();
|
||||
if (likely(rc > 1)) {
|
||||
/* in use by more than me */
|
||||
uint32_t current_size = *_get_size();
|
||||
|
||||
uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true);
|
||||
|
||||
new (mem_new - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(1); //refcount
|
||||
*(mem_new - 1) = current_size; //size
|
||||
|
||||
T *_data = (T *)(mem_new);
|
||||
|
||||
// initialize new elements
|
||||
if (std::is_trivially_copyable<T>::value) {
|
||||
memcpy(mem_new, _ptr, current_size * sizeof(T));
|
||||
|
||||
} else {
|
||||
for (uint32_t i = 0; i < current_size; i++) {
|
||||
memnew_placement(&_data[i], T(_ptr[i]));
|
||||
}
|
||||
}
|
||||
|
||||
_unref(_ptr);
|
||||
_ptr = _data;
|
||||
|
||||
rc = 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Error CowData<T>::resize(int p_size) {
|
||||
assert(p_size >= 0);
|
||||
|
||||
int current_size = size();
|
||||
|
||||
if (p_size == current_size) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (p_size == 0) {
|
||||
// wants to clean up
|
||||
_unref(_ptr);
|
||||
_ptr = nullptr;
|
||||
return OK;
|
||||
}
|
||||
|
||||
// possibly changing size, copy on write
|
||||
uint32_t rc = _copy_on_write();
|
||||
|
||||
size_t current_alloc_size = _get_alloc_size(current_size);
|
||||
size_t alloc_size;
|
||||
assert(_get_alloc_size_checked(p_size, &alloc_size));
|
||||
|
||||
if (p_size > current_size) {
|
||||
if (alloc_size != current_alloc_size) {
|
||||
if (current_size == 0) {
|
||||
// alloc from scratch
|
||||
uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true);
|
||||
ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY);
|
||||
*(ptr - 1) = 0; //size, currently none
|
||||
new (ptr - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(1); //refcount
|
||||
|
||||
_ptr = (T *)ptr;
|
||||
|
||||
} else {
|
||||
uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true);
|
||||
ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
|
||||
new (_ptrnew - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(rc); //refcount
|
||||
|
||||
_ptr = (T *)(_ptrnew);
|
||||
}
|
||||
}
|
||||
|
||||
// construct the newly created elements
|
||||
|
||||
if (!std::is_trivially_constructible<T>::value) {
|
||||
for (int i = *_get_size(); i < p_size; i++) {
|
||||
memnew_placement(&_ptr[i], T);
|
||||
}
|
||||
}
|
||||
|
||||
*_get_size() = p_size;
|
||||
|
||||
} else if (p_size < current_size) {
|
||||
if (!std::is_trivially_destructible<T>::value) {
|
||||
// deinitialize no longer needed elements
|
||||
for (uint32_t i = p_size; i < *_get_size(); i++) {
|
||||
T *t = &_ptr[i];
|
||||
t->~T();
|
||||
}
|
||||
}
|
||||
|
||||
if (alloc_size != current_alloc_size) {
|
||||
uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true);
|
||||
ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
|
||||
new (_ptrnew - 2, sizeof(uint32_t), "") SafeNumeric<uint32_t>(rc); //refcount
|
||||
|
||||
_ptr = (T *)(_ptrnew);
|
||||
}
|
||||
|
||||
*_get_size() = p_size;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
int CowData<T>::find(const T &p_val, int p_from) const {
|
||||
int ret = -1;
|
||||
|
||||
if (p_from < 0 || size() == 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int i = p_from; i < size(); i++) {
|
||||
if (get(i) == p_val) {
|
||||
ret = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void CowData<T>::_ref(const CowData *p_from) {
|
||||
_ref(*p_from);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void CowData<T>::_ref(const CowData &p_from) {
|
||||
if (_ptr == p_from._ptr) {
|
||||
return; // self assign, do nothing.
|
||||
}
|
||||
|
||||
_unref(_ptr);
|
||||
_ptr = nullptr;
|
||||
|
||||
if (!p_from._ptr) {
|
||||
return; //nothing to do
|
||||
}
|
||||
|
||||
if (p_from._get_refcount()->conditional_increment() > 0) { // could reference
|
||||
_ptr = p_from._ptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
CowData<T>::CowData() {
|
||||
_ptr = nullptr;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
CowData<T>::~CowData() {
|
||||
_unref(_ptr);
|
||||
}
|
||||
|
||||
#endif // COWDATA_H
|
||||
176
tests/godot_mockery/core/error_macros.cpp
Normal file
176
tests/godot_mockery/core/error_macros.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/**************************************************************************/
|
||||
/* error_macros.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <core/ustring.h>
|
||||
#include "error_macros.h"
|
||||
|
||||
#if 0
|
||||
|
||||
#include "core/io/logger.h"
|
||||
#include "core/ustring.h"
|
||||
#include "os/os.h"
|
||||
|
||||
|
||||
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
|
||||
#include "scene/main/node.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
static ErrorHandlerList *error_handler_list = nullptr;
|
||||
|
||||
void add_error_handler(ErrorHandlerList *p_handler) {
|
||||
_global_lock();
|
||||
p_handler->next = error_handler_list;
|
||||
error_handler_list = p_handler;
|
||||
_global_unlock();
|
||||
}
|
||||
|
||||
void remove_error_handler(ErrorHandlerList *p_handler) {
|
||||
_global_lock();
|
||||
|
||||
ErrorHandlerList *prev = nullptr;
|
||||
ErrorHandlerList *l = error_handler_list;
|
||||
|
||||
while (l) {
|
||||
if (l == p_handler) {
|
||||
if (prev) {
|
||||
prev->next = l->next;
|
||||
} else {
|
||||
error_handler_list = l->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = l;
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
_global_unlock();
|
||||
}
|
||||
|
||||
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type) {
|
||||
_err_print_error(p_function, p_file, p_line, p_error, "", p_type);
|
||||
}
|
||||
|
||||
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type) {
|
||||
_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), "", p_type);
|
||||
}
|
||||
|
||||
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type) {
|
||||
// Fallback if errors happen before OS init or after it's destroyed.
|
||||
const char *err_details = (p_message && *p_message) ? p_message : p_error;
|
||||
fprintf(stderr, "ERROR: %s\n at: %s (%s:%i)\n", err_details, p_function, p_file, p_line);
|
||||
|
||||
_global_lock();
|
||||
ErrorHandlerList *l = error_handler_list;
|
||||
while (l) {
|
||||
l->errfunc(l->userdata, p_function, p_file, p_line, p_error, p_message, p_type);
|
||||
l = l->next;
|
||||
}
|
||||
|
||||
_global_unlock();
|
||||
}
|
||||
|
||||
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type) {
|
||||
_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_type);
|
||||
}
|
||||
|
||||
void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type) {
|
||||
_err_print_error(p_function, p_file, p_line, p_error, p_message.utf8().get_data(), p_type);
|
||||
}
|
||||
|
||||
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type) {
|
||||
_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message.utf8().get_data(), p_type);
|
||||
}
|
||||
|
||||
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message, bool fatal) {
|
||||
String fstr(fatal ? "FATAL: " : "");
|
||||
String err(fstr + "Index " + p_index_str + " = " + itos(p_index) + " is out of bounds (" + p_size_str + " = " + itos(p_size) + ").");
|
||||
_err_print_error(p_function, p_file, p_line, err.utf8().get_data(), p_message);
|
||||
}
|
||||
|
||||
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal) {
|
||||
_err_print_index_error(p_function, p_file, p_line, p_index, p_size, p_index_str, p_size_str, p_message.utf8().get_data(), fatal);
|
||||
}
|
||||
|
||||
void _err_flush_stdout() {
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Prevent error spam by limiting the warnings to a certain frequency.
|
||||
void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string) {
|
||||
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
|
||||
const uint32_t warn_max = 2048;
|
||||
const uint32_t warn_timeout_seconds = 15;
|
||||
|
||||
static uint32_t warn_count = warn_max;
|
||||
static uint32_t warn_timeout = warn_timeout_seconds;
|
||||
|
||||
uint32_t time_now = UINT32_MAX;
|
||||
|
||||
if (warn_count) {
|
||||
warn_count--;
|
||||
}
|
||||
|
||||
if (!warn_count) {
|
||||
time_now = OS::get_singleton()->get_ticks_msec() / 1000;
|
||||
}
|
||||
|
||||
if ((warn_count == 0) && (time_now >= warn_timeout)) {
|
||||
warn_count = warn_max;
|
||||
warn_timeout = time_now + warn_timeout_seconds;
|
||||
|
||||
if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) {
|
||||
// UINT64_MAX means unused.
|
||||
if (p_id == UINT64_MAX) {
|
||||
_err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + " (possibly benign).", ERR_HANDLER_WARNING);
|
||||
} else {
|
||||
String node_name;
|
||||
if (p_id != 0) {
|
||||
if (ObjectDB::get_instance(p_id)) {
|
||||
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
|
||||
if (node && node->is_inside_tree()) {
|
||||
node_name = "\"" + String(node->get_path()) + "\"";
|
||||
} else {
|
||||
node_name = "\"unknown\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + ": " + node_name + " (possibly benign).", ERR_HANDLER_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
605
tests/godot_mockery/core/error_macros.h
Normal file
605
tests/godot_mockery/core/error_macros.h
Normal file
@@ -0,0 +1,605 @@
|
||||
/**************************************************************************/
|
||||
/* error_macros.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ERROR_MACROS_H
|
||||
#define ERROR_MACROS_H
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <core/safe_refcount.h>
|
||||
|
||||
#define ERR_FAIL_COND_V_MSG(cond, val, msg) {assert(!(cond));}
|
||||
#define ERR_FAIL_COND_MSG(cond, msg) {assert(!(cond));}
|
||||
#define ERR_FAIL_COND_V(cond, val) {assert(!(cond));}
|
||||
#define ERR_FAIL_COND(cond) {assert(!(cond));}
|
||||
#define ERR_FAIL_V_MSG(val, msg) {assert(false);}
|
||||
#define ERR_FAIL_V(val) {assert(false);}
|
||||
#define ERR_FAIL_MSG(msg) {assert(false);}
|
||||
#define CRASH_BAD_INDEX(idx, size) {assert(idx < size);}
|
||||
#define ERR_FAIL_INDEX(idx, size) {assert(idx < size);}
|
||||
#define ERR_FAIL_INDEX_V(idx, size, val) {assert(idx < size);}
|
||||
#define ERR_PRINT(x) {assert(false);}
|
||||
#define WARN_PRINT(x) {assert(false);}
|
||||
#define CRASH_COND(x) {assert(!(x));}
|
||||
#define DEV_ASSERT(x) {assert(x);}
|
||||
#define CRASH_NOW() {assert(false);}
|
||||
|
||||
enum ErrorHandlerType {
|
||||
ERR_HANDLER_ERROR,
|
||||
ERR_HANDLER_WARNING,
|
||||
ERR_HANDLER_SCRIPT,
|
||||
ERR_HANDLER_SHADER,
|
||||
};
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
#include "core/object_id.h"
|
||||
#include "core/safe_refcount.h"
|
||||
#include "core/typedefs.h"
|
||||
|
||||
|
||||
/**
|
||||
* Error macros. Unlike exceptions and asserts, these macros try to maintain consistency and stability
|
||||
* inside the code. It is recommended to always return processable data, so in case of an error,
|
||||
* the engine can keep working well.
|
||||
* In most cases, bugs and/or invalid data are not fatal and should never allow a perfectly running application
|
||||
* to fail or crash.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Pointer to the error macro printing function. Reassign to any function to have errors printed
|
||||
*/
|
||||
|
||||
/** Function used by the error macros */
|
||||
|
||||
// function, file, line, error, explanation
|
||||
|
||||
enum ErrorHandlerType {
|
||||
ERR_HANDLER_ERROR,
|
||||
ERR_HANDLER_WARNING,
|
||||
ERR_HANDLER_SCRIPT,
|
||||
ERR_HANDLER_SHADER,
|
||||
};
|
||||
|
||||
class String;
|
||||
typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, ErrorHandlerType p_type);
|
||||
|
||||
struct ErrorHandlerList {
|
||||
ErrorHandlerFunc errfunc;
|
||||
void *userdata;
|
||||
|
||||
ErrorHandlerList *next;
|
||||
|
||||
ErrorHandlerList() {
|
||||
errfunc = nullptr;
|
||||
next = nullptr;
|
||||
userdata = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
void add_error_handler(ErrorHandlerList *p_handler);
|
||||
void remove_error_handler(ErrorHandlerList *p_handler);
|
||||
|
||||
static inline void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR) {}
|
||||
static inline void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR) {}
|
||||
static inline void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR) {}
|
||||
static inline void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR) {}
|
||||
static inline void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR) {}
|
||||
static inline void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, ErrorHandlerType p_type = ERR_HANDLER_ERROR) {}
|
||||
static inline void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool fatal = false) {}
|
||||
static inline void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool fatal = false) {}
|
||||
static inline void _err_flush_stdout() {}
|
||||
|
||||
// void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string);
|
||||
|
||||
#ifndef _STR
|
||||
#define _STR(m_x) #m_x
|
||||
#define _MKSTR(m_x) _STR(m_x)
|
||||
#endif
|
||||
|
||||
#define _FNL __FILE__ ":"
|
||||
|
||||
/** An index has failed if m_index<0 or m_index >=m_size, the function exits */
|
||||
|
||||
#ifdef __GNUC__
|
||||
//#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying
|
||||
#define FUNCTION_STR __FUNCTION__
|
||||
#else
|
||||
#define FUNCTION_STR __FUNCTION__
|
||||
#endif
|
||||
|
||||
// Don't use this directly; instead, use any of the CRASH_* macros
|
||||
#ifdef _MSC_VER
|
||||
#define GENERATE_TRAP \
|
||||
__debugbreak(); \
|
||||
/* Avoid warning about control paths */ \
|
||||
for (;;) { \
|
||||
}
|
||||
#else
|
||||
#define GENERATE_TRAP __builtin_trap();
|
||||
#endif
|
||||
|
||||
// (*): See https://stackoverflow.com/questions/257418/do-while-0-what-is-it-good-for
|
||||
|
||||
/**
|
||||
* If `m_index` is less than 0 or greater than or equal to `m_size`, prints a generic
|
||||
* error message and returns from the function. This macro should be preferred to
|
||||
* `ERR_FAIL_COND` for bounds checking.
|
||||
*/
|
||||
#define ERR_FAIL_INDEX(m_index, m_size) \
|
||||
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
|
||||
return; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_index` is less than 0 or greater than or equal to `m_size`, prints a custom
|
||||
* error message and returns from the function. This macro should be preferred to
|
||||
* `ERR_FAIL_COND_MSG` for bounds checking.
|
||||
*/
|
||||
#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \
|
||||
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
|
||||
return; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_index` is less than 0 or greater than or equal to `m_size`,
|
||||
* prints a generic error message and returns the value specified in `m_retval`.
|
||||
* This macro should be preferred to `ERR_FAIL_COND_V` for bounds checking.
|
||||
*/
|
||||
#define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \
|
||||
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
|
||||
return m_retval; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_index` is less than 0 or greater than or equal to `m_size`,
|
||||
* prints a custom error message and returns the value specified in `m_retval`.
|
||||
* This macro should be preferred to `ERR_FAIL_COND_V_MSG` for bounds checking.
|
||||
*/
|
||||
#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
|
||||
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
|
||||
return m_retval; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_index` is greater than or equal to `m_size`,
|
||||
* prints a generic error message and returns the value specified in `m_retval`.
|
||||
* This macro should be preferred to `ERR_FAIL_COND_V` for unsigned bounds checking.
|
||||
*/
|
||||
#define ERR_FAIL_UNSIGNED_INDEX(m_index, m_size) \
|
||||
if (unlikely((m_index) >= (m_size))) { \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
|
||||
return; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_index` is greater than or equal to `m_size`,
|
||||
* prints a generic error message and returns the value specified in `m_retval`.
|
||||
* This macro should be preferred to `ERR_FAIL_COND_V` for unsigned bounds checking.
|
||||
*/
|
||||
#define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \
|
||||
if (unlikely((m_index) >= (m_size))) { \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
|
||||
return m_retval; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_index` is greater than or equal to `m_size`,
|
||||
* prints a custom error message and returns the value specified in `m_retval`.
|
||||
* This macro should be preferred to `ERR_FAIL_COND_V_MSG` for unsigned bounds checking.
|
||||
*/
|
||||
#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
|
||||
if (unlikely((m_index) >= (m_size))) { \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \
|
||||
return m_retval; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_index` is less than 0 or greater than or equal to `m_size`,
|
||||
* crashes the engine immediately with a generic error message.
|
||||
* Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
|
||||
* This macro should be preferred to `CRASH_COND` for bounds checking.
|
||||
*/
|
||||
#define CRASH_BAD_INDEX(m_index, m_size) \
|
||||
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \
|
||||
_err_flush_stdout(); \
|
||||
GENERATE_TRAP \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_index` is less than 0 or greater than or equal to `m_size`,
|
||||
* crashes the engine immediately with a custom error message.
|
||||
* Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
|
||||
* This macro should be preferred to `CRASH_COND` for bounds checking.
|
||||
*/
|
||||
#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \
|
||||
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
|
||||
_err_flush_stdout(); \
|
||||
GENERATE_TRAP \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_index` is greater than or equal to `m_size`,
|
||||
* crashes the engine immediately with a generic error message.
|
||||
* Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
|
||||
* This macro should be preferred to `CRASH_COND` for bounds checking.
|
||||
*/
|
||||
#define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size) \
|
||||
if (unlikely((m_index) >= (m_size))) { \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \
|
||||
_err_flush_stdout(); \
|
||||
GENERATE_TRAP \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_param` is `null`, prints a generic error message and returns from the function.
|
||||
*/
|
||||
#define ERR_FAIL_NULL(m_param) \
|
||||
if (unlikely(!m_param)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
|
||||
return; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_param` is `null`, prints a custom error message and returns from the function.
|
||||
*/
|
||||
#define ERR_FAIL_NULL_MSG(m_param, m_msg) \
|
||||
if (unlikely(!m_param)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \
|
||||
return; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_param` is `null`, prints a generic error message and returns the value specified in `m_retval`.
|
||||
*/
|
||||
#define ERR_FAIL_NULL_V(m_param, m_retval) \
|
||||
if (unlikely(!m_param)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
|
||||
return m_retval; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_param` is `null`, prints a custom error message and returns the value specified in `m_retval`.
|
||||
*/
|
||||
#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \
|
||||
if (unlikely(!m_param)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \
|
||||
return m_retval; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_cond` evaluates to `true`, prints a generic error message and returns from the function.
|
||||
*/
|
||||
#define ERR_FAIL_COND(m_cond) \
|
||||
if (unlikely(m_cond)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \
|
||||
return; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_cond` evaluates to `true`, prints a custom error message and returns from the function.
|
||||
*/
|
||||
#define ERR_FAIL_COND_MSG(m_cond, m_msg) \
|
||||
if (unlikely(m_cond)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg); \
|
||||
return; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_cond` evaluates to `true`, crashes the engine immediately with a generic error message.
|
||||
* Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
|
||||
*/
|
||||
#define CRASH_COND(m_cond) \
|
||||
if (unlikely(m_cond)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \
|
||||
_err_flush_stdout(); \
|
||||
GENERATE_TRAP \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_cond` evaluates to `true`, crashes the engine immediately with a custom error message.
|
||||
* Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
|
||||
*/
|
||||
#define CRASH_COND_MSG(m_cond, m_msg) \
|
||||
if (unlikely(m_cond)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", m_msg); \
|
||||
_err_flush_stdout(); \
|
||||
GENERATE_TRAP \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_cond` evaluates to `true`, prints a generic error message and returns the value specified in `m_retval`.
|
||||
*/
|
||||
#define ERR_FAIL_COND_V(m_cond, m_retval) \
|
||||
if (unlikely(m_cond)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returned: " _STR(m_retval)); \
|
||||
return m_retval; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_cond` evaluates to `true`, prints a custom error message and returns the value specified in `m_retval`.
|
||||
*/
|
||||
#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \
|
||||
if (unlikely(m_cond)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returned: " _STR(m_retval), m_msg); \
|
||||
return m_retval; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_cond` evaluates to `true`, prints a custom error message and continues the loop the macro is located in.
|
||||
*/
|
||||
#define ERR_CONTINUE(m_cond) \
|
||||
if (unlikely(m_cond)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing."); \
|
||||
continue; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_cond` evaluates to `true`, prints a custom error message and continues the loop the macro is located in.
|
||||
*/
|
||||
#define ERR_CONTINUE_MSG(m_cond, m_msg) \
|
||||
if (unlikely(m_cond)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg); \
|
||||
continue; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_cond` evaluates to `true`, prints a generic error message and breaks from the loop the macro is located in.
|
||||
*/
|
||||
#define ERR_BREAK(m_cond) \
|
||||
if (unlikely(m_cond)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking."); \
|
||||
break; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* If `m_cond` evaluates to `true`, prints a custom error message and breaks from the loop the macro is located in.
|
||||
*/
|
||||
#define ERR_BREAK_MSG(m_cond, m_msg) \
|
||||
if (unlikely(m_cond)) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg); \
|
||||
break; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* Prints a generic error message and returns from the function.
|
||||
*/
|
||||
#define ERR_FAIL() \
|
||||
if (true) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed."); \
|
||||
return; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* Prints a custom error message and returns from the function.
|
||||
*/
|
||||
#define ERR_FAIL_MSG(m_msg) \
|
||||
if (true) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed.", m_msg); \
|
||||
return; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* Prints a generic error message and returns the value specified in `m_retval`.
|
||||
*/
|
||||
#define ERR_FAIL_V(m_retval) \
|
||||
if (true) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed. Returning: " __STR(m_retval)); \
|
||||
return m_retval; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* Prints a custom error message and returns the value specified in `m_retval`.
|
||||
*/
|
||||
#define ERR_FAIL_V_MSG(m_retval, m_msg) \
|
||||
if (true) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method failed. Returning: " __STR(m_retval), m_msg); \
|
||||
return m_retval; \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* Crashes the engine immediately with a generic error message.
|
||||
* Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
|
||||
*/
|
||||
#define CRASH_NOW() \
|
||||
if (true) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method failed."); \
|
||||
_err_flush_stdout(); \
|
||||
GENERATE_TRAP \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* Crashes the engine immediately with a custom error message.
|
||||
* Only use this if there's no sensible fallback (i.e. the error is unrecoverable).
|
||||
*/
|
||||
#define CRASH_NOW_MSG(m_msg) \
|
||||
if (true) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method failed.", m_msg); \
|
||||
_err_flush_stdout(); \
|
||||
GENERATE_TRAP \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* Prints an error message without returning.
|
||||
*/
|
||||
#define ERR_PRINT(m_string) \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string)
|
||||
|
||||
/**
|
||||
* Prints an error message without returning, but only do so once in the application lifecycle.
|
||||
* This can be used to avoid spamming the console with error messages.
|
||||
*/
|
||||
#define ERR_PRINT_ONCE(m_string) \
|
||||
if (true) { \
|
||||
static bool first_print = true; \
|
||||
if (first_print) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string); \
|
||||
first_print = false; \
|
||||
} \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* Prints a warning message without returning. To warn about deprecated usage,
|
||||
* use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead.
|
||||
*/
|
||||
#define WARN_PRINT(m_string) \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING)
|
||||
|
||||
/**
|
||||
* Prints a warning message without returning, but only do so once in the application lifecycle.
|
||||
* This can be used to avoid spamming the console with warning messages.
|
||||
*/
|
||||
#define WARN_PRINT_ONCE(m_string) \
|
||||
if (true) { \
|
||||
static bool first_print = true; \
|
||||
if (first_print) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_string, ERR_HANDLER_WARNING); \
|
||||
first_print = false; \
|
||||
} \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* Prints a generic deprecation warning message without returning.
|
||||
* This should be preferred to `WARN_PRINT` for deprecation warnings.
|
||||
*/
|
||||
#define WARN_DEPRECATED \
|
||||
if (true) { \
|
||||
static SafeFlag warning_shown; \
|
||||
if (!warning_shown.is_set()) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", ERR_HANDLER_WARNING); \
|
||||
warning_shown.set(); \
|
||||
} \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* Prints a custom deprecation warning message without returning.
|
||||
* This should be preferred to `WARN_PRINT` for deprecation warnings.
|
||||
*/
|
||||
#define WARN_DEPRECATED_MSG(m_msg) \
|
||||
if (true) { \
|
||||
static SafeFlag warning_shown; \
|
||||
if (!warning_shown.is_set()) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, ERR_HANDLER_WARNING); \
|
||||
warning_shown.set(); \
|
||||
} \
|
||||
} else \
|
||||
((void)0)
|
||||
|
||||
/**
|
||||
* This should be a 'free' assert for program flow and should not be needed in any releases,
|
||||
* only used in dev builds.
|
||||
*/
|
||||
#ifdef DEV_ENABLED
|
||||
#define DEV_ASSERT(m_cond) \
|
||||
if (unlikely(!(m_cond))) { \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: DEV_ASSERT failed \"" _STR(m_cond) "\" is false."); \
|
||||
_err_flush_stdout(); \
|
||||
GENERATE_TRAP \
|
||||
} else \
|
||||
((void)0)
|
||||
#else
|
||||
#define DEV_ASSERT(m_cond)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* These should be 'free' checks for program flow and should not be needed in any releases,
|
||||
* only used in dev builds.
|
||||
*/
|
||||
#ifdef DEV_ENABLED
|
||||
#define DEV_CHECK(m_cond) \
|
||||
if (unlikely(!(m_cond))) { \
|
||||
ERR_PRINT("DEV_CHECK failed \"" _STR(m_cond) "\" is false."); \
|
||||
} else \
|
||||
((void)0)
|
||||
#else
|
||||
#define DEV_CHECK(m_cond)
|
||||
#endif
|
||||
|
||||
#ifdef DEV_ENABLED
|
||||
#define DEV_CHECK_ONCE(m_cond) \
|
||||
if (unlikely(!(m_cond))) { \
|
||||
ERR_PRINT_ONCE("DEV_CHECK_ONCE failed \"" _STR(m_cond) "\" is false."); \
|
||||
} else \
|
||||
((void)0)
|
||||
#else
|
||||
#define DEV_CHECK_ONCE(m_cond)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Physics Interpolation warnings.
|
||||
* These are spam protection warnings.
|
||||
*/
|
||||
#define PHYSICS_INTERPOLATION_NODE_WARNING(m_object_id, m_string) \
|
||||
_physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, m_object_id, m_string)
|
||||
|
||||
#define PHYSICS_INTERPOLATION_WARNING(m_string) \
|
||||
_physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, UINT64_MAX, m_string)
|
||||
#endif
|
||||
|
||||
#endif // ERROR_MACROS_H
|
||||
699
tests/godot_mockery/core/list.h
Normal file
699
tests/godot_mockery/core/list.h
Normal file
@@ -0,0 +1,699 @@
|
||||
/**************************************************************************/
|
||||
/* list.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef LIST_H
|
||||
#define LIST_H
|
||||
|
||||
#include "core/os/memory.h"
|
||||
#include "core/sort_array.h"
|
||||
|
||||
/**
|
||||
* Generic Templatized Linked List Implementation.
|
||||
* The implementation differs from the STL one because
|
||||
* a compatible preallocated linked list can be written
|
||||
* using the same API, or features such as erasing an element
|
||||
* from the iterator.
|
||||
*/
|
||||
|
||||
template <class T, class A = DefaultAllocator>
|
||||
class List {
|
||||
struct _Data;
|
||||
|
||||
public:
|
||||
class Element {
|
||||
private:
|
||||
friend class List<T, A>;
|
||||
|
||||
T value;
|
||||
Element *next_ptr;
|
||||
Element *prev_ptr;
|
||||
_Data *data;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get NEXT Element iterator, for constant lists.
|
||||
*/
|
||||
_FORCE_INLINE_ const Element *next() const {
|
||||
return next_ptr;
|
||||
};
|
||||
/**
|
||||
* Get NEXT Element iterator,
|
||||
*/
|
||||
_FORCE_INLINE_ Element *next() {
|
||||
return next_ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get PREV Element iterator, for constant lists.
|
||||
*/
|
||||
_FORCE_INLINE_ const Element *prev() const {
|
||||
return prev_ptr;
|
||||
};
|
||||
/**
|
||||
* Get PREV Element iterator,
|
||||
*/
|
||||
_FORCE_INLINE_ Element *prev() {
|
||||
return prev_ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* * operator, for using as *iterator, when iterators are defined on stack.
|
||||
*/
|
||||
_FORCE_INLINE_ const T &operator*() const {
|
||||
return value;
|
||||
};
|
||||
/**
|
||||
* operator->, for using as iterator->, when iterators are defined on stack, for constant lists.
|
||||
*/
|
||||
_FORCE_INLINE_ const T *operator->() const {
|
||||
return &value;
|
||||
};
|
||||
/**
|
||||
* * operator, for using as *iterator, when iterators are defined on stack,
|
||||
*/
|
||||
_FORCE_INLINE_ T &operator*() {
|
||||
return value;
|
||||
};
|
||||
/**
|
||||
* operator->, for using as iterator->, when iterators are defined on stack, for constant lists.
|
||||
*/
|
||||
_FORCE_INLINE_ T *operator->() {
|
||||
return &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* get the value stored in this element.
|
||||
*/
|
||||
_FORCE_INLINE_ T &get() {
|
||||
return value;
|
||||
};
|
||||
/**
|
||||
* get the value stored in this element, for constant lists
|
||||
*/
|
||||
_FORCE_INLINE_ const T &get() const {
|
||||
return value;
|
||||
};
|
||||
/**
|
||||
* set the value stored in this element.
|
||||
*/
|
||||
_FORCE_INLINE_ void set(const T &p_value) {
|
||||
value = (T &)p_value;
|
||||
};
|
||||
|
||||
void erase() {
|
||||
data->erase(this);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Element() {
|
||||
next_ptr = nullptr;
|
||||
prev_ptr = nullptr;
|
||||
data = nullptr;
|
||||
};
|
||||
};
|
||||
|
||||
private:
|
||||
struct _Data {
|
||||
Element *first;
|
||||
Element *last;
|
||||
int size_cache;
|
||||
|
||||
bool erase(const Element *p_I) {
|
||||
ERR_FAIL_COND_V(!p_I, false);
|
||||
ERR_FAIL_COND_V(p_I->data != this, false);
|
||||
|
||||
if (first == p_I) {
|
||||
first = p_I->next_ptr;
|
||||
};
|
||||
|
||||
if (last == p_I) {
|
||||
last = p_I->prev_ptr;
|
||||
}
|
||||
|
||||
if (p_I->prev_ptr) {
|
||||
p_I->prev_ptr->next_ptr = p_I->next_ptr;
|
||||
}
|
||||
|
||||
if (p_I->next_ptr) {
|
||||
p_I->next_ptr->prev_ptr = p_I->prev_ptr;
|
||||
}
|
||||
|
||||
memdelete_allocator<Element, A>(const_cast<Element *>(p_I));
|
||||
size_cache--;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
_Data *_data;
|
||||
|
||||
public:
|
||||
/**
|
||||
* return a const iterator to the beginning of the list.
|
||||
*/
|
||||
_FORCE_INLINE_ const Element *front() const {
|
||||
return _data ? _data->first : nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* return an iterator to the beginning of the list.
|
||||
*/
|
||||
_FORCE_INLINE_ Element *front() {
|
||||
return _data ? _data->first : nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* return a const iterator to the last member of the list.
|
||||
*/
|
||||
_FORCE_INLINE_ const Element *back() const {
|
||||
return _data ? _data->last : nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* return an iterator to the last member of the list.
|
||||
*/
|
||||
_FORCE_INLINE_ Element *back() {
|
||||
return _data ? _data->last : nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* store a new element at the end of the list
|
||||
*/
|
||||
Element *push_back(const T &value) {
|
||||
if (!_data) {
|
||||
_data = memnew_allocator(_Data, A);
|
||||
_data->first = nullptr;
|
||||
_data->last = nullptr;
|
||||
_data->size_cache = 0;
|
||||
}
|
||||
|
||||
Element *n = memnew_allocator(Element, A);
|
||||
n->value = (T &)value;
|
||||
|
||||
n->prev_ptr = _data->last;
|
||||
n->next_ptr = nullptr;
|
||||
n->data = _data;
|
||||
|
||||
if (_data->last) {
|
||||
_data->last->next_ptr = n;
|
||||
}
|
||||
|
||||
_data->last = n;
|
||||
|
||||
if (!_data->first) {
|
||||
_data->first = n;
|
||||
}
|
||||
|
||||
_data->size_cache++;
|
||||
|
||||
return n;
|
||||
};
|
||||
|
||||
void pop_back() {
|
||||
if (_data && _data->last) {
|
||||
erase(_data->last);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* store a new element at the beginning of the list
|
||||
*/
|
||||
Element *push_front(const T &value) {
|
||||
if (!_data) {
|
||||
_data = memnew_allocator(_Data, A);
|
||||
_data->first = nullptr;
|
||||
_data->last = nullptr;
|
||||
_data->size_cache = 0;
|
||||
}
|
||||
|
||||
Element *n = memnew_allocator(Element, A);
|
||||
n->value = (T &)value;
|
||||
n->prev_ptr = nullptr;
|
||||
n->next_ptr = _data->first;
|
||||
n->data = _data;
|
||||
|
||||
if (_data->first) {
|
||||
_data->first->prev_ptr = n;
|
||||
}
|
||||
|
||||
_data->first = n;
|
||||
|
||||
if (!_data->last) {
|
||||
_data->last = n;
|
||||
}
|
||||
|
||||
_data->size_cache++;
|
||||
|
||||
return n;
|
||||
};
|
||||
|
||||
void pop_front() {
|
||||
if (_data && _data->first) {
|
||||
erase(_data->first);
|
||||
}
|
||||
}
|
||||
|
||||
Element *insert_after(Element *p_element, const T &p_value) {
|
||||
CRASH_COND(p_element && (!_data || p_element->data != _data));
|
||||
|
||||
if (!p_element) {
|
||||
return push_back(p_value);
|
||||
}
|
||||
|
||||
Element *n = memnew_allocator(Element, A);
|
||||
n->value = (T &)p_value;
|
||||
n->prev_ptr = p_element;
|
||||
n->next_ptr = p_element->next_ptr;
|
||||
n->data = _data;
|
||||
|
||||
if (!p_element->next_ptr) {
|
||||
_data->last = n;
|
||||
} else {
|
||||
p_element->next_ptr->prev_ptr = n;
|
||||
}
|
||||
|
||||
p_element->next_ptr = n;
|
||||
|
||||
_data->size_cache++;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
Element *insert_before(Element *p_element, const T &p_value) {
|
||||
CRASH_COND(p_element && (!_data || p_element->data != _data));
|
||||
|
||||
if (!p_element) {
|
||||
return push_back(p_value);
|
||||
}
|
||||
|
||||
Element *n = memnew_allocator(Element, A);
|
||||
n->value = (T &)p_value;
|
||||
n->prev_ptr = p_element->prev_ptr;
|
||||
n->next_ptr = p_element;
|
||||
n->data = _data;
|
||||
|
||||
if (!p_element->prev_ptr) {
|
||||
_data->first = n;
|
||||
} else {
|
||||
p_element->prev_ptr->next_ptr = n;
|
||||
}
|
||||
|
||||
p_element->prev_ptr = n;
|
||||
|
||||
_data->size_cache++;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* find an element in the list,
|
||||
*/
|
||||
template <class T_v>
|
||||
Element *find(const T_v &p_val) {
|
||||
Element *it = front();
|
||||
while (it) {
|
||||
if (it->value == p_val) {
|
||||
return it;
|
||||
}
|
||||
it = it->next();
|
||||
};
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* erase an element in the list, by iterator pointing to it. Return true if it was found/erased.
|
||||
*/
|
||||
bool erase(const Element *p_I) {
|
||||
if (_data) {
|
||||
bool ret = _data->erase(p_I);
|
||||
|
||||
if (_data->size_cache == 0) {
|
||||
memdelete_allocator<_Data, A>(_data);
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* erase the first element in the list, that contains value
|
||||
*/
|
||||
bool erase(const T &value) {
|
||||
Element *I = find(value);
|
||||
return erase(I);
|
||||
};
|
||||
|
||||
/**
|
||||
* return whether the list is empty
|
||||
*/
|
||||
_FORCE_INLINE_ bool empty() const {
|
||||
return (!_data || !_data->size_cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* clear the list
|
||||
*/
|
||||
void clear() {
|
||||
while (front()) {
|
||||
erase(front());
|
||||
};
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ int size() const {
|
||||
return _data ? _data->size_cache : 0;
|
||||
}
|
||||
|
||||
void swap(Element *p_A, Element *p_B) {
|
||||
ERR_FAIL_COND(!p_A || !p_B);
|
||||
ERR_FAIL_COND(p_A->data != _data);
|
||||
ERR_FAIL_COND(p_B->data != _data);
|
||||
|
||||
if (p_A == p_B) {
|
||||
return;
|
||||
}
|
||||
Element *A_prev = p_A->prev_ptr;
|
||||
Element *A_next = p_A->next_ptr;
|
||||
Element *B_prev = p_B->prev_ptr;
|
||||
Element *B_next = p_B->next_ptr;
|
||||
|
||||
if (A_prev) {
|
||||
A_prev->next_ptr = p_B;
|
||||
} else {
|
||||
_data->first = p_B;
|
||||
}
|
||||
if (B_prev) {
|
||||
B_prev->next_ptr = p_A;
|
||||
} else {
|
||||
_data->first = p_A;
|
||||
}
|
||||
if (A_next) {
|
||||
A_next->prev_ptr = p_B;
|
||||
} else {
|
||||
_data->last = p_B;
|
||||
}
|
||||
if (B_next) {
|
||||
B_next->prev_ptr = p_A;
|
||||
} else {
|
||||
_data->last = p_A;
|
||||
}
|
||||
p_A->prev_ptr = A_next == p_B ? p_B : B_prev;
|
||||
p_A->next_ptr = B_next == p_A ? p_B : B_next;
|
||||
p_B->prev_ptr = B_next == p_A ? p_A : A_prev;
|
||||
p_B->next_ptr = A_next == p_B ? p_A : A_next;
|
||||
}
|
||||
/**
|
||||
* copy the list
|
||||
*/
|
||||
void operator=(const List &p_list) {
|
||||
clear();
|
||||
const Element *it = p_list.front();
|
||||
while (it) {
|
||||
push_back(it->get());
|
||||
it = it->next();
|
||||
}
|
||||
}
|
||||
|
||||
T &operator[](int p_index) {
|
||||
CRASH_BAD_INDEX(p_index, size());
|
||||
|
||||
Element *I = front();
|
||||
int c = 0;
|
||||
while (I) {
|
||||
if (c == p_index) {
|
||||
return I->get();
|
||||
}
|
||||
I = I->next();
|
||||
c++;
|
||||
}
|
||||
|
||||
CRASH_NOW(); // bug!!
|
||||
}
|
||||
|
||||
const T &operator[](int p_index) const {
|
||||
CRASH_BAD_INDEX(p_index, size());
|
||||
|
||||
const Element *I = front();
|
||||
int c = 0;
|
||||
while (I) {
|
||||
if (c == p_index) {
|
||||
return I->get();
|
||||
}
|
||||
I = I->next();
|
||||
c++;
|
||||
}
|
||||
|
||||
CRASH_NOW(); // bug!!
|
||||
}
|
||||
|
||||
void move_to_back(Element *p_I) {
|
||||
ERR_FAIL_COND(p_I->data != _data);
|
||||
if (!p_I->next_ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_data->first == p_I) {
|
||||
_data->first = p_I->next_ptr;
|
||||
};
|
||||
|
||||
if (_data->last == p_I) {
|
||||
_data->last = p_I->prev_ptr;
|
||||
}
|
||||
|
||||
if (p_I->prev_ptr) {
|
||||
p_I->prev_ptr->next_ptr = p_I->next_ptr;
|
||||
}
|
||||
|
||||
p_I->next_ptr->prev_ptr = p_I->prev_ptr;
|
||||
|
||||
_data->last->next_ptr = p_I;
|
||||
p_I->prev_ptr = _data->last;
|
||||
p_I->next_ptr = nullptr;
|
||||
_data->last = p_I;
|
||||
}
|
||||
|
||||
void invert() {
|
||||
int s = size() / 2;
|
||||
Element *F = front();
|
||||
Element *B = back();
|
||||
for (int i = 0; i < s; i++) {
|
||||
SWAP(F->value, B->value);
|
||||
F = F->next();
|
||||
B = B->prev();
|
||||
}
|
||||
}
|
||||
|
||||
void move_to_front(Element *p_I) {
|
||||
ERR_FAIL_COND(p_I->data != _data);
|
||||
if (!p_I->prev_ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_data->first == p_I) {
|
||||
_data->first = p_I->next_ptr;
|
||||
};
|
||||
|
||||
if (_data->last == p_I) {
|
||||
_data->last = p_I->prev_ptr;
|
||||
}
|
||||
|
||||
p_I->prev_ptr->next_ptr = p_I->next_ptr;
|
||||
|
||||
if (p_I->next_ptr) {
|
||||
p_I->next_ptr->prev_ptr = p_I->prev_ptr;
|
||||
}
|
||||
|
||||
_data->first->prev_ptr = p_I;
|
||||
p_I->next_ptr = _data->first;
|
||||
p_I->prev_ptr = nullptr;
|
||||
_data->first = p_I;
|
||||
}
|
||||
|
||||
void move_before(Element *value, Element *where) {
|
||||
if (value->prev_ptr) {
|
||||
value->prev_ptr->next_ptr = value->next_ptr;
|
||||
} else {
|
||||
_data->first = value->next_ptr;
|
||||
}
|
||||
if (value->next_ptr) {
|
||||
value->next_ptr->prev_ptr = value->prev_ptr;
|
||||
} else {
|
||||
_data->last = value->prev_ptr;
|
||||
}
|
||||
|
||||
value->next_ptr = where;
|
||||
if (!where) {
|
||||
value->prev_ptr = _data->last;
|
||||
_data->last = value;
|
||||
return;
|
||||
};
|
||||
|
||||
value->prev_ptr = where->prev_ptr;
|
||||
|
||||
if (where->prev_ptr) {
|
||||
where->prev_ptr->next_ptr = value;
|
||||
} else {
|
||||
_data->first = value;
|
||||
};
|
||||
|
||||
where->prev_ptr = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* simple insertion sort
|
||||
*/
|
||||
|
||||
void sort() {
|
||||
sort_custom<Comparator<T>>();
|
||||
}
|
||||
|
||||
template <class C>
|
||||
void sort_custom_inplace() {
|
||||
if (size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
Element *from = front();
|
||||
Element *current = from;
|
||||
Element *to = from;
|
||||
|
||||
while (current) {
|
||||
Element *next = current->next_ptr;
|
||||
|
||||
if (from != current) {
|
||||
current->prev_ptr = NULL;
|
||||
current->next_ptr = from;
|
||||
|
||||
Element *find = from;
|
||||
C less;
|
||||
while (find && less(find->value, current->value)) {
|
||||
current->prev_ptr = find;
|
||||
current->next_ptr = find->next_ptr;
|
||||
find = find->next_ptr;
|
||||
}
|
||||
|
||||
if (current->prev_ptr) {
|
||||
current->prev_ptr->next_ptr = current;
|
||||
} else {
|
||||
from = current;
|
||||
}
|
||||
|
||||
if (current->next_ptr) {
|
||||
current->next_ptr->prev_ptr = current;
|
||||
} else {
|
||||
to = current;
|
||||
}
|
||||
} else {
|
||||
current->prev_ptr = NULL;
|
||||
current->next_ptr = NULL;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
_data->first = from;
|
||||
_data->last = to;
|
||||
}
|
||||
|
||||
template <class C>
|
||||
struct AuxiliaryComparator {
|
||||
C compare;
|
||||
_FORCE_INLINE_ bool operator()(const Element *a, const Element *b) const {
|
||||
return compare(a->value, b->value);
|
||||
}
|
||||
};
|
||||
|
||||
template <class C>
|
||||
void sort_custom() {
|
||||
//this version uses auxiliary memory for speed.
|
||||
//if you don't want to use auxiliary memory, use the in_place version
|
||||
|
||||
int s = size();
|
||||
if (s < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
Element **aux_buffer = memnew_arr(Element *, s);
|
||||
|
||||
int idx = 0;
|
||||
for (Element *E = front(); E; E = E->next_ptr) {
|
||||
aux_buffer[idx] = E;
|
||||
idx++;
|
||||
}
|
||||
|
||||
SortArray<Element *, AuxiliaryComparator<C>> sort;
|
||||
sort.sort(aux_buffer, s);
|
||||
|
||||
_data->first = aux_buffer[0];
|
||||
aux_buffer[0]->prev_ptr = nullptr;
|
||||
aux_buffer[0]->next_ptr = aux_buffer[1];
|
||||
|
||||
_data->last = aux_buffer[s - 1];
|
||||
aux_buffer[s - 1]->prev_ptr = aux_buffer[s - 2];
|
||||
aux_buffer[s - 1]->next_ptr = nullptr;
|
||||
|
||||
for (int i = 1; i < s - 1; i++) {
|
||||
aux_buffer[i]->prev_ptr = aux_buffer[i - 1];
|
||||
aux_buffer[i]->next_ptr = aux_buffer[i + 1];
|
||||
}
|
||||
|
||||
memdelete_arr(aux_buffer);
|
||||
}
|
||||
|
||||
const void *id() const {
|
||||
return (void *)_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* copy constructor for the list
|
||||
*/
|
||||
List(const List &p_list) {
|
||||
_data = nullptr;
|
||||
const Element *it = p_list.front();
|
||||
while (it) {
|
||||
push_back(it->get());
|
||||
it = it->next();
|
||||
}
|
||||
}
|
||||
|
||||
List() {
|
||||
_data = nullptr;
|
||||
};
|
||||
~List() {
|
||||
clear();
|
||||
if (_data) {
|
||||
ERR_FAIL_COND(_data->size_cache);
|
||||
memdelete_allocator<_Data, A>(_data);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif // LIST_H
|
||||
201
tests/godot_mockery/core/os/memory.cpp
Normal file
201
tests/godot_mockery/core/os/memory.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
/**************************************************************************/
|
||||
/* memory.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
#include "core/error_macros.h"
|
||||
#include "core/safe_refcount.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void *operator new(size_t p_size, const char *p_description) {
|
||||
return Memory::alloc_static(p_size, false);
|
||||
}
|
||||
|
||||
void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)) {
|
||||
return p_allocfunc(p_size);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
void operator delete(void *p_mem, const char *p_description) {
|
||||
CRASH_NOW_MSG("Call to placement delete should not happen.");
|
||||
}
|
||||
|
||||
void operator delete(void *p_mem, void *(*p_allocfunc)(size_t p_size)) {
|
||||
CRASH_NOW_MSG("Call to placement delete should not happen.");
|
||||
}
|
||||
|
||||
void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_description) {
|
||||
CRASH_NOW_MSG("Call to placement delete should not happen.");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
SafeNumeric<uint64_t> Memory::mem_usage;
|
||||
SafeNumeric<uint64_t> Memory::max_usage;
|
||||
#endif
|
||||
|
||||
SafeNumeric<uint64_t> Memory::alloc_count;
|
||||
|
||||
void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool prepad = true;
|
||||
#else
|
||||
bool prepad = p_pad_align;
|
||||
#endif
|
||||
|
||||
void *mem = malloc(p_bytes + (prepad ? PAD_ALIGN : 0));
|
||||
|
||||
ERR_FAIL_COND_V(!mem, nullptr);
|
||||
|
||||
alloc_count.increment();
|
||||
|
||||
if (prepad) {
|
||||
uint64_t *s = (uint64_t *)mem;
|
||||
*s = p_bytes;
|
||||
|
||||
uint8_t *s8 = (uint8_t *)mem;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
uint64_t new_mem_usage = mem_usage.add(p_bytes);
|
||||
max_usage.exchange_if_greater(new_mem_usage);
|
||||
#endif
|
||||
return s8 + PAD_ALIGN;
|
||||
} else {
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
|
||||
if (p_memory == nullptr) {
|
||||
return alloc_static(p_bytes, p_pad_align);
|
||||
}
|
||||
|
||||
uint8_t *mem = (uint8_t *)p_memory;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool prepad = true;
|
||||
#else
|
||||
bool prepad = p_pad_align;
|
||||
#endif
|
||||
|
||||
if (prepad) {
|
||||
mem -= PAD_ALIGN;
|
||||
uint64_t *s = (uint64_t *)mem;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (p_bytes > *s) {
|
||||
uint64_t new_mem_usage = mem_usage.add(p_bytes - *s);
|
||||
max_usage.exchange_if_greater(new_mem_usage);
|
||||
} else {
|
||||
mem_usage.sub(*s - p_bytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p_bytes == 0) {
|
||||
free(mem);
|
||||
return nullptr;
|
||||
} else {
|
||||
*s = p_bytes;
|
||||
|
||||
mem = (uint8_t *)realloc(mem, p_bytes + PAD_ALIGN);
|
||||
ERR_FAIL_COND_V(!mem, nullptr);
|
||||
|
||||
s = (uint64_t *)mem;
|
||||
|
||||
*s = p_bytes;
|
||||
|
||||
return mem + PAD_ALIGN;
|
||||
}
|
||||
} else {
|
||||
mem = (uint8_t *)realloc(mem, p_bytes);
|
||||
|
||||
ERR_FAIL_COND_V(mem == nullptr && p_bytes > 0, nullptr);
|
||||
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::free_static(void *p_ptr, bool p_pad_align) {
|
||||
ERR_FAIL_COND(p_ptr == nullptr);
|
||||
|
||||
uint8_t *mem = (uint8_t *)p_ptr;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool prepad = true;
|
||||
#else
|
||||
bool prepad = p_pad_align;
|
||||
#endif
|
||||
|
||||
alloc_count.decrement();
|
||||
|
||||
if (prepad) {
|
||||
mem -= PAD_ALIGN;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
uint64_t *s = (uint64_t *)mem;
|
||||
mem_usage.sub(*s);
|
||||
#endif
|
||||
|
||||
free(mem);
|
||||
} else {
|
||||
free(mem);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Memory::get_mem_available() {
|
||||
return -1; // 0xFFFF...
|
||||
}
|
||||
|
||||
uint64_t Memory::get_mem_usage() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
return mem_usage.get();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t Memory::get_mem_max_usage() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
return max_usage.get();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
_GlobalNil::_GlobalNil() {
|
||||
color = 1;
|
||||
left = this;
|
||||
right = this;
|
||||
parent = this;
|
||||
}
|
||||
|
||||
_GlobalNil _GlobalNilClass::_nil;
|
||||
206
tests/godot_mockery/core/os/memory.h
Normal file
206
tests/godot_mockery/core/os/memory.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/**************************************************************************/
|
||||
/* memory.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef MEMORY_H
|
||||
#define MEMORY_H
|
||||
|
||||
// #include "core/error_macros.h"
|
||||
#include "core/safe_refcount.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef PAD_ALIGN
|
||||
#define PAD_ALIGN 16 //must always be greater than this at much
|
||||
#endif
|
||||
|
||||
class Memory {
|
||||
#ifdef DEBUG_ENABLED
|
||||
static SafeNumeric<uint64_t> mem_usage;
|
||||
static SafeNumeric<uint64_t> max_usage;
|
||||
#endif
|
||||
|
||||
static SafeNumeric<uint64_t> alloc_count;
|
||||
|
||||
public:
|
||||
static void *alloc_static(size_t p_bytes, bool p_pad_align = false);
|
||||
static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false);
|
||||
static void free_static(void *p_ptr, bool p_pad_align = false);
|
||||
|
||||
static uint64_t get_mem_available();
|
||||
static uint64_t get_mem_usage();
|
||||
static uint64_t get_mem_max_usage();
|
||||
};
|
||||
|
||||
class DefaultAllocator {
|
||||
public:
|
||||
_FORCE_INLINE_ static void *alloc(size_t p_memory) { return Memory::alloc_static(p_memory, false); }
|
||||
_FORCE_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr, false); }
|
||||
};
|
||||
|
||||
void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool
|
||||
void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)); ///< operator new that takes a description and uses MemoryStaticPool
|
||||
|
||||
void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description); ///< operator new that takes a description and uses a pointer to the preallocated memory
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// When compiling with VC++ 2017, the above declarations of placement new generate many irrelevant warnings (C4291).
|
||||
// The purpose of the following definitions is to muffle these warnings, not to provide a usable implementation of placement delete.
|
||||
void operator delete(void *p_mem, const char *p_description);
|
||||
void operator delete(void *p_mem, void *(*p_allocfunc)(size_t p_size));
|
||||
void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_description);
|
||||
#endif
|
||||
|
||||
#define memalloc(m_size) Memory::alloc_static(m_size)
|
||||
#define memrealloc(m_mem, m_size) Memory::realloc_static(m_mem, m_size)
|
||||
#define memfree(m_mem) Memory::free_static(m_mem)
|
||||
|
||||
_ALWAYS_INLINE_ void postinitialize_handler(void *) {}
|
||||
|
||||
template <class T>
|
||||
_ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
|
||||
postinitialize_handler(p_obj);
|
||||
return p_obj;
|
||||
}
|
||||
|
||||
#define memnew(m_class) _post_initialize(new ("") m_class)
|
||||
|
||||
_ALWAYS_INLINE_ void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description) {
|
||||
//void *failptr=0;
|
||||
//ERR_FAIL_COND_V( check < p_size , failptr); /** bug, or strange compiler, most likely */
|
||||
|
||||
return p_pointer;
|
||||
}
|
||||
|
||||
#define memnew_allocator(m_class, m_allocator) _post_initialize(new (m_allocator::alloc) m_class)
|
||||
#define memnew_placement(m_placement, m_class) _post_initialize(new (m_placement, sizeof(m_class), "") m_class)
|
||||
|
||||
_ALWAYS_INLINE_ bool predelete_handler(void *) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void memdelete(T *p_class) {
|
||||
if (!predelete_handler(p_class)) {
|
||||
return; // doesn't want to be deleted
|
||||
}
|
||||
if (!std::is_trivially_destructible<T>::value) {
|
||||
p_class->~T();
|
||||
}
|
||||
|
||||
Memory::free_static(p_class, false);
|
||||
}
|
||||
|
||||
template <class T, class A>
|
||||
void memdelete_allocator(T *p_class) {
|
||||
if (!predelete_handler(p_class)) {
|
||||
return; // doesn't want to be deleted
|
||||
}
|
||||
if (!std::is_trivially_destructible<T>::value) {
|
||||
p_class->~T();
|
||||
}
|
||||
|
||||
A::free(p_class);
|
||||
}
|
||||
|
||||
#define memdelete_notnull(m_v) \
|
||||
{ \
|
||||
if (m_v) \
|
||||
memdelete(m_v); \
|
||||
}
|
||||
|
||||
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
|
||||
|
||||
template <typename T>
|
||||
T *memnew_arr_template(size_t p_elements, const char *p_descr = "") {
|
||||
if (p_elements == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
/** overloading operator new[] cannot be done , because it may not return the real allocated address (it may pad the 'element count' before the actual array). Because of that, it must be done by hand. This is the
|
||||
same strategy used by std::vector, and the PoolVector class, so it should be safe.*/
|
||||
|
||||
size_t len = sizeof(T) * p_elements;
|
||||
uint64_t *mem = (uint64_t *)Memory::alloc_static(len, true);
|
||||
T *failptr = nullptr; //get rid of a warning
|
||||
ERR_FAIL_COND_V(!mem, failptr);
|
||||
*(mem - 1) = p_elements;
|
||||
|
||||
if (!std::is_trivially_constructible<T>::value) {
|
||||
T *elems = (T *)mem;
|
||||
|
||||
/* call operator new */
|
||||
for (size_t i = 0; i < p_elements; i++) {
|
||||
new (&elems[i], sizeof(T), p_descr) T;
|
||||
}
|
||||
}
|
||||
|
||||
return (T *)mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wonders of having own array functions, you can actually check the length of
|
||||
* an allocated-with memnew_arr() array
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
size_t memarr_len(const T *p_class) {
|
||||
uint64_t *ptr = (uint64_t *)p_class;
|
||||
return *(ptr - 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void memdelete_arr(T *p_class) {
|
||||
uint64_t *ptr = (uint64_t *)p_class;
|
||||
|
||||
if (!std::is_trivially_destructible<T>::value) {
|
||||
uint64_t elem_count = *(ptr - 1);
|
||||
|
||||
for (uint64_t i = 0; i < elem_count; i++) {
|
||||
p_class[i].~T();
|
||||
}
|
||||
}
|
||||
|
||||
Memory::free_static(ptr, true);
|
||||
}
|
||||
|
||||
struct _GlobalNil {
|
||||
int color;
|
||||
_GlobalNil *right;
|
||||
_GlobalNil *left;
|
||||
_GlobalNil *parent;
|
||||
|
||||
_GlobalNil();
|
||||
};
|
||||
|
||||
struct _GlobalNilClass {
|
||||
static _GlobalNil _nil;
|
||||
};
|
||||
|
||||
#endif // MEMORY_H
|
||||
318
tests/godot_mockery/core/ustring.cpp
Normal file
318
tests/godot_mockery/core/ustring.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#ifndef NO_USE_STDLIB
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <wchar.h>
|
||||
#include <cstdint>
|
||||
#include "core/math/math_funcs.h"
|
||||
|
||||
#include "ustring.h"
|
||||
|
||||
const char CharString::_null = 0;
|
||||
const CharType String::_null = 0;
|
||||
|
||||
String &String::operator+=(const String &p_str) {
|
||||
const int lhs_len = length();
|
||||
if (lhs_len == 0) {
|
||||
*this = p_str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const int rhs_len = p_str.length();
|
||||
if (rhs_len == 0) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
resize(lhs_len + rhs_len + 1);
|
||||
|
||||
const CharType *src = p_str.c_str();
|
||||
CharType *dst = ptrw() + lhs_len;
|
||||
|
||||
memcpy(dst, src, (rhs_len + 1) * sizeof(CharType));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
String &String::operator+=(const CharType *p_str) {
|
||||
*this += String(p_str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
String &String::operator+=(CharType p_char) {
|
||||
const int lhs_len = length();
|
||||
resize(lhs_len + 2);
|
||||
|
||||
CharType *dst = ptrw();
|
||||
dst[lhs_len] = p_char;
|
||||
dst[lhs_len + 1] = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
String &String::operator+=(const char *p_str) {
|
||||
if (!p_str || p_str[0] == 0) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
const size_t rhs_len = strlen(p_str);
|
||||
const int lhs_len = length();
|
||||
|
||||
resize(lhs_len + rhs_len + 1);
|
||||
|
||||
CharType *dst = ptrw() + lhs_len;
|
||||
|
||||
for (size_t i = 0; i <= rhs_len; i++) {
|
||||
dst[i] = p_str[i];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CharString String::ascii(bool p_allow_extended) const {
|
||||
if (!length()) {
|
||||
return CharString();
|
||||
}
|
||||
|
||||
CharString cs;
|
||||
cs.resize(size());
|
||||
|
||||
for (int i = 0; i < size(); i++) {
|
||||
cs[i] = operator[](i);
|
||||
}
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
String String::operator+(const String &p_str) const {
|
||||
String res = *this;
|
||||
res += p_str;
|
||||
return res;
|
||||
}
|
||||
|
||||
String String::num(double p_num, int p_decimals) {
|
||||
if (Math::is_nan(p_num)) {
|
||||
return "nan";
|
||||
}
|
||||
|
||||
#ifndef NO_USE_STDLIB
|
||||
|
||||
if (p_decimals > 16) {
|
||||
p_decimals = 16;
|
||||
}
|
||||
|
||||
char fmt[7];
|
||||
fmt[0] = '%';
|
||||
fmt[1] = '.';
|
||||
|
||||
if (p_decimals < 0) {
|
||||
fmt[1] = 'l';
|
||||
fmt[2] = 'f';
|
||||
fmt[3] = 0;
|
||||
|
||||
} else if (p_decimals < 10) {
|
||||
fmt[2] = '0' + p_decimals;
|
||||
fmt[3] = 'l';
|
||||
fmt[4] = 'f';
|
||||
fmt[5] = 0;
|
||||
} else {
|
||||
fmt[2] = '0' + (p_decimals / 10);
|
||||
fmt[3] = '0' + (p_decimals % 10);
|
||||
fmt[4] = 'l';
|
||||
fmt[5] = 'f';
|
||||
fmt[6] = 0;
|
||||
}
|
||||
char buf[256];
|
||||
|
||||
#if defined(__GNUC__) || defined(_MSC_VER)
|
||||
snprintf(buf, 256, fmt, p_num);
|
||||
#else
|
||||
sprintf(buf, fmt, p_num);
|
||||
#endif
|
||||
|
||||
buf[255] = 0;
|
||||
//destroy trailing zeroes
|
||||
{
|
||||
bool period = false;
|
||||
int z = 0;
|
||||
while (buf[z]) {
|
||||
if (buf[z] == '.') {
|
||||
period = true;
|
||||
}
|
||||
z++;
|
||||
}
|
||||
|
||||
if (period) {
|
||||
z--;
|
||||
while (z > 0) {
|
||||
if (buf[z] == '0') {
|
||||
buf[z] = 0;
|
||||
} else if (buf[z] == '.') {
|
||||
buf[z] = 0;
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
z--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
#else
|
||||
|
||||
String s;
|
||||
String sd;
|
||||
/* integer part */
|
||||
|
||||
bool neg = p_num < 0;
|
||||
p_num = ABS(p_num);
|
||||
int intn = (int)p_num;
|
||||
|
||||
/* decimal part */
|
||||
|
||||
if (p_decimals > 0 || (p_decimals == -1 && (int)p_num != p_num)) {
|
||||
double dec = p_num - (float)((int)p_num);
|
||||
|
||||
int digit = 0;
|
||||
if (p_decimals > MAX_DIGITS)
|
||||
p_decimals = MAX_DIGITS;
|
||||
|
||||
int dec_int = 0;
|
||||
int dec_max = 0;
|
||||
|
||||
while (true) {
|
||||
dec *= 10.0;
|
||||
dec_int = dec_int * 10 + (int)dec % 10;
|
||||
dec_max = dec_max * 10 + 9;
|
||||
digit++;
|
||||
|
||||
if (p_decimals == -1) {
|
||||
if (digit == MAX_DIGITS) //no point in going to infinite
|
||||
break;
|
||||
|
||||
if ((dec - (float)((int)dec)) < 1e-6)
|
||||
break;
|
||||
}
|
||||
|
||||
if (digit == p_decimals)
|
||||
break;
|
||||
}
|
||||
dec *= 10;
|
||||
int last = (int)dec % 10;
|
||||
|
||||
if (last > 5) {
|
||||
if (dec_int == dec_max) {
|
||||
dec_int = 0;
|
||||
intn++;
|
||||
} else {
|
||||
dec_int++;
|
||||
}
|
||||
}
|
||||
|
||||
String decimal;
|
||||
for (int i = 0; i < digit; i++) {
|
||||
char num[2] = { 0, 0 };
|
||||
num[0] = '0' + dec_int % 10;
|
||||
decimal = num + decimal;
|
||||
dec_int /= 10;
|
||||
}
|
||||
sd = '.' + decimal;
|
||||
}
|
||||
|
||||
if (intn == 0)
|
||||
|
||||
s = "0";
|
||||
else {
|
||||
while (intn) {
|
||||
CharType num = '0' + (intn % 10);
|
||||
intn /= 10;
|
||||
s = num + s;
|
||||
}
|
||||
}
|
||||
|
||||
s = s + sd;
|
||||
if (neg)
|
||||
s = "-" + s;
|
||||
return s;
|
||||
#endif
|
||||
}
|
||||
String::String(const char *p_str) {
|
||||
copy_from(p_str);
|
||||
}
|
||||
|
||||
String::String(const CharType *p_str, int p_clip_to_len) {
|
||||
copy_from(p_str, p_clip_to_len);
|
||||
}
|
||||
|
||||
void String::copy_from(const char *p_cstr) {
|
||||
if (!p_cstr) {
|
||||
resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t len = strlen(p_cstr);
|
||||
|
||||
if (len == 0) {
|
||||
resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
resize(len + 1); // include 0
|
||||
|
||||
CharType *dst = this->ptrw();
|
||||
|
||||
for (size_t i = 0; i <= len; i++) {
|
||||
dst[i] = p_cstr[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void String::copy_from(const CharType *p_cstr, const int p_clip_to) {
|
||||
if (!p_cstr) {
|
||||
resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
const CharType *ptr = p_cstr;
|
||||
while ((p_clip_to < 0 || len < p_clip_to) && *(ptr++) != 0) {
|
||||
len++;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
copy_from_unchecked(p_cstr, len);
|
||||
}
|
||||
|
||||
// assumes the following have already been validated:
|
||||
// p_char != NULL
|
||||
// p_length > 0
|
||||
// p_length <= p_char strlen
|
||||
void String::copy_from_unchecked(const CharType *p_char, const int p_length) {
|
||||
resize(p_length + 1);
|
||||
|
||||
CharType *dst = ptrw();
|
||||
memcpy(dst, p_char, p_length * sizeof(CharType));
|
||||
dst[p_length] = 0;
|
||||
}
|
||||
|
||||
void String::copy_from(const CharType &p_char) {
|
||||
resize(2);
|
||||
CharType *dst = ptrw();
|
||||
dst[0] = p_char;
|
||||
dst[1] = 0;
|
||||
}
|
||||
|
||||
const CharType *String::c_str() const {
|
||||
static const CharType zero = 0;
|
||||
|
||||
return size() ? &operator[](0) : &zero;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
496
tests/godot_mockery/core/ustring.h
Normal file
496
tests/godot_mockery/core/ustring.h
Normal file
@@ -0,0 +1,496 @@
|
||||
/**************************************************************************/
|
||||
/* ustring.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef USTRING_GODOT_H
|
||||
#define USTRING_GODOT_H
|
||||
|
||||
#if 0
|
||||
class String {
|
||||
flecs::string string;
|
||||
public:
|
||||
String() noexcept {}
|
||||
String(const char *p_str) noexcept string(p_str) {}
|
||||
String &operator=(const String &str) noexcept
|
||||
{
|
||||
string = str.string;
|
||||
return *this;
|
||||
}
|
||||
String &operator+(const String &)
|
||||
{
|
||||
String res = *this;
|
||||
res += p_str;
|
||||
return res;
|
||||
}
|
||||
String &operator+=(const String &p_str)
|
||||
{
|
||||
string += p_str.string;
|
||||
return *this;
|
||||
}
|
||||
String &operator+=(const char *s)
|
||||
{
|
||||
string += s;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const String &other)
|
||||
{
|
||||
return string == other.string;
|
||||
}
|
||||
bool operator==(const char *other)
|
||||
{
|
||||
return string == other;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#include "core/array.h"
|
||||
#include "core/cowdata.h"
|
||||
#include "core/typedefs.h"
|
||||
#include "core/vector.h"
|
||||
|
||||
template <class T>
|
||||
class CharProxy {
|
||||
friend class CharString;
|
||||
friend class String;
|
||||
|
||||
const int _index;
|
||||
CowData<T> &_cowdata;
|
||||
static const T _null = 0;
|
||||
|
||||
_FORCE_INLINE_ CharProxy(const int &p_index, CowData<T> &p_cowdata) :
|
||||
_index(p_index),
|
||||
_cowdata(p_cowdata) {}
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ CharProxy(const CharProxy<T> &p_other) :
|
||||
_index(p_other._index),
|
||||
_cowdata(p_other._cowdata) {}
|
||||
|
||||
_FORCE_INLINE_ operator T() const {
|
||||
if (unlikely(_index == _cowdata.size())) {
|
||||
return _null;
|
||||
}
|
||||
|
||||
return _cowdata.get(_index);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ const T *operator&() const {
|
||||
return _cowdata.ptr() + _index;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void operator=(const T &p_other) const {
|
||||
_cowdata.set(_index, p_other);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void operator=(const CharProxy<T> &p_other) const {
|
||||
_cowdata.set(_index, p_other.operator T());
|
||||
}
|
||||
};
|
||||
|
||||
class CharString {
|
||||
CowData<char> _cowdata;
|
||||
static const char _null;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ char *ptrw() { return _cowdata.ptrw(); }
|
||||
_FORCE_INLINE_ const char *ptr() const { return _cowdata.ptr(); }
|
||||
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
|
||||
Error resize(int p_size) { return _cowdata.resize(p_size); }
|
||||
|
||||
_FORCE_INLINE_ char get(int p_index) const { return _cowdata.get(p_index); }
|
||||
_FORCE_INLINE_ void set(int p_index, const char &p_elem) { _cowdata.set(p_index, p_elem); }
|
||||
_FORCE_INLINE_ const char &operator[](int p_index) const {
|
||||
if (unlikely(p_index == _cowdata.size())) {
|
||||
return _null;
|
||||
}
|
||||
|
||||
return _cowdata.get(p_index);
|
||||
}
|
||||
_FORCE_INLINE_ CharProxy<char> operator[](int p_index) { return CharProxy<char>(p_index, _cowdata); }
|
||||
|
||||
_FORCE_INLINE_ CharString() {}
|
||||
_FORCE_INLINE_ CharString(const CharString &p_str) { _cowdata._ref(p_str._cowdata); }
|
||||
_FORCE_INLINE_ CharString operator=(const CharString &p_str) {
|
||||
_cowdata._ref(p_str._cowdata);
|
||||
return *this;
|
||||
}
|
||||
_FORCE_INLINE_ CharString(const char *p_cstr) { copy_from(p_cstr); }
|
||||
|
||||
CharString &operator=(const char *p_cstr);
|
||||
bool operator<(const CharString &p_right) const;
|
||||
CharString &operator+=(char p_char);
|
||||
int length() const { return size() ? size() - 1 : 0; }
|
||||
const char *get_data() const;
|
||||
operator const char *() const { return get_data(); };
|
||||
|
||||
protected:
|
||||
void copy_from(const char *p_cstr);
|
||||
};
|
||||
|
||||
typedef wchar_t CharType;
|
||||
|
||||
struct StrRange {
|
||||
const CharType *c_str;
|
||||
int len;
|
||||
|
||||
StrRange(const CharType *p_c_str = nullptr, int p_len = 0) {
|
||||
c_str = p_c_str;
|
||||
len = p_len;
|
||||
}
|
||||
};
|
||||
|
||||
class String {
|
||||
CowData<CharType> _cowdata;
|
||||
static const CharType _null;
|
||||
|
||||
void copy_from(const char *p_cstr);
|
||||
void copy_from(const CharType *p_cstr, const int p_clip_to = -1);
|
||||
void copy_from(const CharType &p_char);
|
||||
void copy_from_unchecked(const CharType *p_char, const int p_length);
|
||||
bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const;
|
||||
int _count(const String &p_string, int p_from, int p_to, bool p_case_insensitive) const;
|
||||
|
||||
public:
|
||||
enum {
|
||||
|
||||
npos = -1 ///<for "some" compatibility with std::string (npos is a huge value in std::string)
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ CharType *ptrw() { return _cowdata.ptrw(); }
|
||||
_FORCE_INLINE_ const CharType *ptr() const { return _cowdata.ptr(); }
|
||||
|
||||
void remove(int p_index) { _cowdata.remove(p_index); }
|
||||
|
||||
_FORCE_INLINE_ void clear() { resize(0); }
|
||||
|
||||
_FORCE_INLINE_ CharType get(int p_index) const { return _cowdata.get(p_index); }
|
||||
_FORCE_INLINE_ void set(int p_index, const CharType &p_elem) { _cowdata.set(p_index, p_elem); }
|
||||
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
|
||||
Error resize(int p_size) { return _cowdata.resize(p_size); }
|
||||
|
||||
_FORCE_INLINE_ const CharType &operator[](int p_index) const {
|
||||
if (unlikely(p_index == _cowdata.size())) {
|
||||
return _null;
|
||||
}
|
||||
|
||||
return _cowdata.get(p_index);
|
||||
}
|
||||
_FORCE_INLINE_ CharProxy<CharType> operator[](int p_index) { return CharProxy<CharType>(p_index, _cowdata); }
|
||||
|
||||
bool operator==(const String &p_str) const;
|
||||
bool operator!=(const String &p_str) const;
|
||||
String operator+(const String &p_str) const;
|
||||
//String operator+(CharType p_char) const;
|
||||
|
||||
String &operator+=(const String &);
|
||||
String &operator+=(CharType p_char);
|
||||
String &operator+=(const char *p_str);
|
||||
String &operator+=(const CharType *p_str);
|
||||
|
||||
/* Compatibility Operators */
|
||||
|
||||
void operator=(const char *p_str);
|
||||
void operator=(const CharType *p_str);
|
||||
bool operator==(const char *p_str) const;
|
||||
bool operator==(const CharType *p_str) const;
|
||||
bool operator==(const StrRange &p_str_range) const;
|
||||
bool operator!=(const char *p_str) const;
|
||||
bool operator!=(const CharType *p_str) const;
|
||||
bool operator<(const CharType *p_str) const;
|
||||
bool operator<(const char *p_str) const;
|
||||
bool operator<(const String &p_str) const;
|
||||
bool operator<=(const String &p_str) const;
|
||||
|
||||
signed char casecmp_to(const String &p_str) const;
|
||||
signed char nocasecmp_to(const String &p_str) const;
|
||||
signed char naturalnocasecmp_to(const String &p_str) const;
|
||||
|
||||
const CharType *c_str() const;
|
||||
/* standard size stuff */
|
||||
|
||||
_FORCE_INLINE_ int length() const {
|
||||
int s = size();
|
||||
return s ? (s - 1) : 0; // length does not include zero
|
||||
}
|
||||
|
||||
/* complex helpers */
|
||||
String substr(int p_from, int p_chars = -1) const;
|
||||
int find(const String &p_str, int p_from = 0) const; ///< return <0 if failed
|
||||
int find(const char *p_str, int p_from = 0) const; ///< return <0 if failed
|
||||
int find_char(const CharType &p_char, int p_from = 0) const; ///< return <0 if failed
|
||||
int find_last(const String &p_str) const; ///< return <0 if failed
|
||||
int findn(const String &p_str, int p_from = 0) const; ///< return <0 if failed, case insensitive
|
||||
int rfind(const String &p_str, int p_from = -1) const; ///< return <0 if failed
|
||||
int rfindn(const String &p_str, int p_from = -1) const; ///< return <0 if failed, case insensitive
|
||||
int findmk(const Vector<String> &p_keys, int p_from = 0, int *r_key = nullptr) const; ///< return <0 if failed
|
||||
bool match(const String &p_wildcard) const;
|
||||
bool matchn(const String &p_wildcard) const;
|
||||
bool begins_with(const String &p_string) const;
|
||||
bool begins_with(const char *p_string) const;
|
||||
bool ends_with(const String &p_string) const;
|
||||
bool is_enclosed_in(const String &p_string) const;
|
||||
bool is_subsequence_of(const String &p_string) const;
|
||||
bool is_subsequence_ofi(const String &p_string) const;
|
||||
bool is_quoted() const;
|
||||
Vector<String> bigrams() const;
|
||||
float similarity(const String &p_string) const;
|
||||
String format(const Variant &values, String placeholder = "{_}") const;
|
||||
String replace_first(const String &p_key, const String &p_with) const;
|
||||
String replace(const String &p_key, const String &p_with) const;
|
||||
String replace(const char *p_key, const char *p_with) const;
|
||||
String replacen(const String &p_key, const String &p_with) const;
|
||||
String repeat(int p_count) const;
|
||||
String insert(int p_at_pos, const String &p_string) const;
|
||||
String pad_decimals(int p_digits) const;
|
||||
String pad_zeros(int p_digits) const;
|
||||
String trim_prefix(const String &p_prefix) const;
|
||||
String trim_suffix(const String &p_suffix) const;
|
||||
String lpad(int min_length, const String &character = " ") const;
|
||||
String rpad(int min_length, const String &character = " ") const;
|
||||
String sprintf(const Array &values, bool *error) const;
|
||||
String quote(String quotechar = "\"") const;
|
||||
String unquote() const;
|
||||
static String num(double p_num, int p_decimals = -1);
|
||||
static String num_scientific(double p_num);
|
||||
static String num_real(double p_num);
|
||||
static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false);
|
||||
static String num_uint64(uint64_t p_num, int base = 10, bool capitalize_hex = false);
|
||||
static String chr(CharType p_char);
|
||||
static String md5(const uint8_t *p_md5);
|
||||
static String hex_encode_buffer(const uint8_t *p_buffer, int p_len);
|
||||
bool is_numeric() const;
|
||||
double to_double() const;
|
||||
float to_float() const;
|
||||
int hex_to_int(bool p_with_prefix = true) const;
|
||||
int to_int() const;
|
||||
|
||||
int64_t hex_to_int64(bool p_with_prefix = true) const;
|
||||
int64_t bin_to_int64(bool p_with_prefix = true) const;
|
||||
int64_t to_int64() const;
|
||||
static int to_int(const char *p_str, int p_len = -1);
|
||||
static double to_double(const char *p_str);
|
||||
static double to_double(const CharType *p_str, const CharType **r_end = nullptr);
|
||||
static int64_t to_int(const CharType *p_str, int p_len = -1);
|
||||
String capitalize() const;
|
||||
String camelcase_to_underscore(bool lowercase = true) const;
|
||||
|
||||
int get_slice_count(String p_splitter) const;
|
||||
String get_slice(String p_splitter, int p_slice) const;
|
||||
String get_slicec(CharType p_splitter, int p_slice) const;
|
||||
|
||||
Vector<String> split(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const;
|
||||
Vector<String> rsplit(const String &p_splitter, bool p_allow_empty = true, int p_maxsplit = 0) const;
|
||||
Vector<String> split_spaces() const;
|
||||
Vector<float> split_floats(const String &p_splitter, bool p_allow_empty = true) const;
|
||||
Vector<float> split_floats_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const;
|
||||
Vector<int> split_ints(const String &p_splitter, bool p_allow_empty = true) const;
|
||||
Vector<int> split_ints_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const;
|
||||
|
||||
String join(const Vector<String> &parts) const;
|
||||
|
||||
static CharType char_uppercase(CharType p_char);
|
||||
static CharType char_lowercase(CharType p_char);
|
||||
String to_upper() const;
|
||||
String to_lower() const;
|
||||
|
||||
int count(const String &p_string, int p_from = 0, int p_to = 0) const;
|
||||
int countn(const String &p_string, int p_from = 0, int p_to = 0) const;
|
||||
|
||||
String left(int p_pos) const;
|
||||
String right(int p_pos) const;
|
||||
String indent(const String &p_prefix) const;
|
||||
String dedent() const;
|
||||
String strip_edges(bool left = true, bool right = true) const;
|
||||
String strip_escapes() const;
|
||||
String lstrip(const String &p_chars) const;
|
||||
String rstrip(const String &p_chars) const;
|
||||
String get_extension() const;
|
||||
String get_basename() const;
|
||||
String plus_file(const String &p_file) const;
|
||||
CharType ord_at(int p_idx) const;
|
||||
|
||||
void erase(int p_pos, int p_chars);
|
||||
|
||||
CharString ascii(bool p_allow_extended = false) const;
|
||||
CharString utf8() const;
|
||||
bool parse_utf8(const char *p_utf8, int p_len = -1, bool p_skip_cr = false); //return true on error
|
||||
static String utf8(const char *p_utf8, int p_len = -1);
|
||||
|
||||
static uint32_t hash(const CharType *p_cstr, int p_len); /* hash the string */
|
||||
static uint32_t hash(const CharType *p_cstr); /* hash the string */
|
||||
static uint32_t hash(const char *p_cstr, int p_len); /* hash the string */
|
||||
static uint32_t hash(const char *p_cstr); /* hash the string */
|
||||
uint32_t hash() const; /* hash the string */
|
||||
uint64_t hash64() const; /* hash the string */
|
||||
String md5_text() const;
|
||||
String sha1_text() const;
|
||||
String sha256_text() const;
|
||||
Vector<uint8_t> md5_buffer() const;
|
||||
Vector<uint8_t> sha1_buffer() const;
|
||||
Vector<uint8_t> sha256_buffer() const;
|
||||
|
||||
_FORCE_INLINE_ bool empty() const { return length() == 0; }
|
||||
|
||||
// path functions
|
||||
bool is_abs_path() const;
|
||||
bool is_rel_path() const;
|
||||
bool is_resource_file() const;
|
||||
String path_to(const String &p_path) const;
|
||||
String path_to_file(const String &p_path) const;
|
||||
String get_base_dir() const;
|
||||
String get_file() const;
|
||||
static String humanize_size(uint64_t p_size);
|
||||
String simplify_path() const;
|
||||
bool is_network_share_path() const;
|
||||
|
||||
String xml_escape(bool p_escape_quotes = false) const;
|
||||
String xml_unescape() const;
|
||||
String http_escape() const;
|
||||
String http_unescape() const;
|
||||
String c_escape() const;
|
||||
String c_escape_multiline() const;
|
||||
String c_unescape() const;
|
||||
String json_escape() const;
|
||||
String word_wrap(int p_chars_per_line) const;
|
||||
Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const;
|
||||
|
||||
String percent_encode() const;
|
||||
String percent_decode() const;
|
||||
|
||||
String property_name_encode() const;
|
||||
|
||||
// node functions
|
||||
static const String invalid_node_name_characters;
|
||||
String validate_node_name() const;
|
||||
String validate_identifier() const;
|
||||
|
||||
bool is_valid_identifier() const;
|
||||
bool is_valid_integer() const;
|
||||
bool is_valid_float() const;
|
||||
bool is_valid_hex_number(bool p_with_prefix) const;
|
||||
bool is_valid_html_color() const;
|
||||
bool is_valid_ip_address() const;
|
||||
bool is_valid_filename() const;
|
||||
|
||||
/**
|
||||
* The constructors must not depend on other overloads
|
||||
*/
|
||||
/* String(CharType p_char);*/
|
||||
|
||||
_FORCE_INLINE_ String() {}
|
||||
_FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); }
|
||||
String operator=(const String &p_str) {
|
||||
_cowdata._ref(p_str._cowdata);
|
||||
return *this;
|
||||
}
|
||||
|
||||
String(const char *p_str);
|
||||
String(const CharType *p_str, int p_clip_to_len = -1);
|
||||
String(const StrRange &p_range);
|
||||
};
|
||||
|
||||
bool operator==(const char *p_chr, const String &p_str);
|
||||
|
||||
String operator+(const char *p_chr, const String &p_str);
|
||||
String operator+(CharType p_chr, const String &p_str);
|
||||
|
||||
String itos(int64_t p_val);
|
||||
String uitos(uint64_t p_val);
|
||||
String rtos(double p_val);
|
||||
String rtoss(double p_val); //scientific version
|
||||
|
||||
struct NoCaseComparator {
|
||||
bool operator()(const String &p_a, const String &p_b) const {
|
||||
return p_a.nocasecmp_to(p_b) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct NaturalNoCaseComparator {
|
||||
bool operator()(const String &p_a, const String &p_b) const {
|
||||
return p_a.naturalnocasecmp_to(p_b) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
_FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) {
|
||||
while (true) {
|
||||
if (*l_ptr == 0 && *r_ptr == 0) {
|
||||
return false;
|
||||
} else if (*l_ptr == 0) {
|
||||
return true;
|
||||
} else if (*r_ptr == 0) {
|
||||
return false;
|
||||
} else if (*l_ptr < *r_ptr) {
|
||||
return true;
|
||||
} else if (*l_ptr > *r_ptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
l_ptr++;
|
||||
r_ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* end of namespace */
|
||||
|
||||
// Tool translate (TTR and variants) for the editor UI,
|
||||
// and doc translate for the class reference (DTR).
|
||||
#if 0
|
||||
// Gets parsed.
|
||||
String TTR(const String &p_text, const String &p_context = "");
|
||||
String DTR(const String &);
|
||||
// Use for C strings.
|
||||
#define TTRC(m_value) (m_value)
|
||||
// Use to avoid parsing (for use later with C strings).
|
||||
#define TTRGET(m_value) TTR(m_value)
|
||||
|
||||
#else
|
||||
#define TTR(m_value) (String())
|
||||
#define DTR(m_value) (String())
|
||||
#define TTRC(m_value) (m_value)
|
||||
#define TTRGET(m_value) (m_value)
|
||||
#endif
|
||||
|
||||
// Use this to mark property names for editor translation.
|
||||
// Often for dynamic properties defined in _get_property_list().
|
||||
// Property names defined directly inside EDITOR_DEF, GLOBAL_DEF, and ADD_PROPERTY macros don't need this.
|
||||
#define PNAME(m_value) (m_value)
|
||||
|
||||
// Similar to PNAME, but to mark groups, i.e. properties with PROPERTY_USAGE_GROUP.
|
||||
// Groups defined directly inside ADD_GROUP macros don't need this.
|
||||
// The arguments are the same as ADD_GROUP. m_prefix is only used for extraction.
|
||||
#define GNAME(m_value, m_prefix) (m_value)
|
||||
|
||||
// Runtime translate for the public node API.
|
||||
String RTR(const String &);
|
||||
|
||||
bool is_symbol(CharType c);
|
||||
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end);
|
||||
|
||||
#endif // USTRING_GODOT_H
|
||||
4620
tests/godot_mockery/core/ustring2.cpp
Normal file
4620
tests/godot_mockery/core/ustring2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
205
tests/godot_mockery/core/vector.h
Normal file
205
tests/godot_mockery/core/vector.h
Normal file
@@ -0,0 +1,205 @@
|
||||
/**************************************************************************/
|
||||
/* vector.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef VECTOR_H
|
||||
#define VECTOR_H
|
||||
|
||||
/**
|
||||
* @class Vector
|
||||
* @author Juan Linietsky
|
||||
* Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use PoolVector for large arrays.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "core/cowdata.h"
|
||||
#include "core/error_macros.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/sort_array.h"
|
||||
|
||||
template <class T>
|
||||
class VectorWriteProxy {
|
||||
public:
|
||||
_FORCE_INLINE_ T &operator[](int p_index) {
|
||||
CRASH_BAD_INDEX(p_index, ((Vector<T> *)(this))->_cowdata.size());
|
||||
|
||||
return ((Vector<T> *)(this))->_cowdata.ptrw()[p_index];
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class Vector {
|
||||
friend class VectorWriteProxy<T>;
|
||||
|
||||
public:
|
||||
VectorWriteProxy<T> write;
|
||||
|
||||
private:
|
||||
CowData<T> _cowdata;
|
||||
|
||||
public:
|
||||
bool push_back(T p_elem);
|
||||
void fill(T p_elem);
|
||||
|
||||
void remove(int p_index) { _cowdata.remove(p_index); }
|
||||
void erase(const T &p_val) {
|
||||
int idx = find(p_val);
|
||||
if (idx >= 0) {
|
||||
remove(idx);
|
||||
}
|
||||
};
|
||||
void invert();
|
||||
|
||||
_FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); }
|
||||
_FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); }
|
||||
_FORCE_INLINE_ void clear() { resize(0); }
|
||||
_FORCE_INLINE_ bool empty() const { return _cowdata.empty(); }
|
||||
|
||||
_FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); }
|
||||
_FORCE_INLINE_ const T &get(int p_index) const { return _cowdata.get(p_index); }
|
||||
_FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
|
||||
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
|
||||
Error resize(int p_size) { return _cowdata.resize(p_size); }
|
||||
_FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); }
|
||||
Error insert(int p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); }
|
||||
int find(const T &p_val, int p_from = 0) const { return _cowdata.find(p_val, p_from); }
|
||||
|
||||
void append_array(Vector<T> p_other);
|
||||
|
||||
template <class C>
|
||||
void sort_custom() {
|
||||
int len = _cowdata.size();
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
T *data = ptrw();
|
||||
SortArray<T, C> sorter;
|
||||
sorter.sort(data, len);
|
||||
}
|
||||
|
||||
void sort() {
|
||||
sort_custom<_DefaultComparator<T>>();
|
||||
}
|
||||
|
||||
void ordered_insert(const T &p_val) {
|
||||
int i;
|
||||
for (i = 0; i < _cowdata.size(); i++) {
|
||||
if (p_val < operator[](i)) {
|
||||
break;
|
||||
};
|
||||
};
|
||||
insert(i, p_val);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Vector() {}
|
||||
_FORCE_INLINE_ Vector(const Vector &p_from) { _cowdata._ref(p_from._cowdata); }
|
||||
inline Vector &operator=(const Vector &p_from) {
|
||||
_cowdata._ref(p_from._cowdata);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector<uint8_t> to_byte_array() const {
|
||||
Vector<uint8_t> ret;
|
||||
ret.resize(size() * sizeof(T));
|
||||
memcpy(ret.ptrw(), ptr(), sizeof(T) * size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector<T> slice(int p_begin, int p_end = INT32_MAX) const {
|
||||
Vector<T> result;
|
||||
|
||||
const int s = size();
|
||||
|
||||
int begin = CLAMP(p_begin, -s, s);
|
||||
if (begin < 0) {
|
||||
begin += s;
|
||||
}
|
||||
int end = CLAMP(p_end, -s, s);
|
||||
if (end < 0) {
|
||||
end += s;
|
||||
}
|
||||
|
||||
assert(begin <= end);
|
||||
|
||||
int result_size = end - begin;
|
||||
result.resize(result_size);
|
||||
|
||||
const T *const r = ptr();
|
||||
T *const w = result.ptrw();
|
||||
for (int i = 0; i < result_size; ++i) {
|
||||
w[i] = r[begin + i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ ~Vector() {}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
void Vector<T>::invert() {
|
||||
for (int i = 0; i < size() / 2; i++) {
|
||||
T *p = ptrw();
|
||||
SWAP(p[i], p[size() - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Vector<T>::append_array(Vector<T> p_other) {
|
||||
const int ds = p_other.size();
|
||||
if (ds == 0) {
|
||||
return;
|
||||
}
|
||||
const int bs = size();
|
||||
resize(bs + ds);
|
||||
for (int i = 0; i < ds; ++i) {
|
||||
ptrw()[bs + i] = p_other[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool Vector<T>::push_back(T p_elem) {
|
||||
Error err = resize(size() + 1);
|
||||
assert(err != OK);
|
||||
set(size() - 1, p_elem);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Vector<T>::fill(T p_elem) {
|
||||
T *p = ptrw();
|
||||
for (int i = 0; i < size(); i++) {
|
||||
p[i] = p_elem;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // VECTOR_H
|
||||
@@ -4,26 +4,6 @@
|
||||
#include <core/math/rect2.h>
|
||||
#include "flecs.h"
|
||||
#include "graph_module.h"
|
||||
namespace WorldEditor
|
||||
{
|
||||
namespace components
|
||||
{
|
||||
struct buildings_layout_zone
|
||||
{
|
||||
int type;
|
||||
bool align_wall;
|
||||
};
|
||||
struct buildings_layout_area
|
||||
{
|
||||
float area;
|
||||
};
|
||||
struct buildings_layout_system
|
||||
{
|
||||
flecs::entity e;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
struct graph_module
|
||||
{
|
||||
graph_module(flecs::world &ecs)
|
||||
|
||||
BIN
tests/graph_test
BIN
tests/graph_test
Binary file not shown.
BIN
tests/rect2i
BIN
tests/rect2i
Binary file not shown.
7
tests/regions.cpp
Normal file
7
tests/regions.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "../src/modules/stream/ui/region_tree.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user