summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--compat/int-hash.cpp78
-rw-r--r--compat/int-hash.hpp14
-rw-r--r--editor/app.cpp2
-rw-r--r--serialize/wall-atlas.cpp100
-rw-r--r--src/point.cpp4
-rw-r--r--src/wall-atlas.cpp42
-rw-r--r--src/wall-atlas.hpp9
-rw-r--r--src/world.cpp4
-rw-r--r--test/hash.cpp34
-rw-r--r--test/json/frameset-header2.json9
-rw-r--r--test/wall-atlas.cpp12
11 files changed, 221 insertions, 87 deletions
diff --git a/compat/int-hash.cpp b/compat/int-hash.cpp
index 5847e284..aa5287f8 100644
--- a/compat/int-hash.cpp
+++ b/compat/int-hash.cpp
@@ -6,72 +6,88 @@ namespace floormat {
namespace {
+template<typename T> struct fnvhash_params;
+
+template<> struct fnvhash_params<uint32_t> { uint32_t a = 0x811c9dc5u, b = 0x01000193u; };
+template<> struct fnvhash_params<uint64_t> { uint64_t a = 0xcbf29ce484222325u, b = 0x100000001b3u; };
+
[[maybe_unused]]
-CORRADE_ALWAYS_INLINE uint32_t FNVHash_32(uint32_t x)
+CORRADE_ALWAYS_INLINE size_t fnvhash_uint_32(uint32_t x)
{
- const auto *str = (const char*)&x, *end = str + 4;
- uint32_t hash = 0x811c9dc5u;
-fm_UNROLL_4
- for (; str != end; ++str)
- {
- hash *= 0x01000193u;
- hash ^= (uint8_t)*str;
- }
+ constexpr auto params = fnvhash_params<std::common_type_t<size_t>>{};
+ constexpr auto a = params.a, b = params.b;
+ auto hash = a;
+ const auto* str = (const char*)&x;
+
+ hash *= b; hash ^= (uint8_t)*str++; // 0
+ hash *= b; hash ^= (uint8_t)*str++; // 1
+ hash *= b; hash ^= (uint8_t)*str++; // 2
+ hash *= b; hash ^= (uint8_t)*str++; // 3
+
return hash;
}
-CORRADE_ALWAYS_INLINE uint64_t FNVHash_64(uint64_t x)
+CORRADE_ALWAYS_INLINE size_t fnvhash_uint_64(uint64_t x)
{
- const auto *str = (const char*)&x, *end = str + 8;
- uint64_t hash = 0xcbf29ce484222325u;
-fm_UNROLL_8
- for (; str != end; ++str)
- {
- hash *= 0x100000001b3u;
- hash ^= (uint8_t)*str;
- }
+ constexpr auto params = fnvhash_params<std::common_type_t<size_t>>{};
+ constexpr auto a = params.a, b = params.b;
+ auto hash = a;
+ const auto* str = (const char*)&x;
+
+ hash *= b; hash ^= (uint8_t)*str++; // 0
+ hash *= b; hash ^= (uint8_t)*str++; // 1
+ hash *= b; hash ^= (uint8_t)*str++; // 2
+ hash *= b; hash ^= (uint8_t)*str++; // 3
+ hash *= b; hash ^= (uint8_t)*str++; // 4
+ hash *= b; hash ^= (uint8_t)*str++; // 5
+ hash *= b; hash ^= (uint8_t)*str++; // 6
+ hash *= b; hash ^= (uint8_t)*str++; // 7
+
return hash;
}
} // namespace
-uint64_t fnvhash_32(const void* buf, size_t size)
+uint32_t hash_32(const void* buf, size_t size) noexcept
{
+ constexpr auto params = fnvhash_params<std::common_type_t<uint32_t>>{};
+ constexpr auto a = params.a, b = params.b;
+ auto hash = a;
const auto *str = (const char*)buf, *const end = str + size;
- uint32_t hash = 0x811c9dc5u;
+
fm_UNROLL_4
for (; str != end; ++str)
{
- hash *= 0x01000193u;
+ hash *= b;
hash ^= (uint8_t)*str;
}
return hash;
}
-uint64_t fnvhash_64(const void* buf, size_t size)
+uint64_t hash_64(const void* buf, size_t size) noexcept
{
+ constexpr auto params = fnvhash_params<std::common_type_t<size_t>>{};
+ constexpr auto a = params.a, b = params.b;
+ auto hash = a;
const auto *str = (const char*)buf, *const end = str + size;
- uint64_t hash = 0xcbf29ce484222325u;
+
fm_UNROLL_8
for (; str != end; ++str)
{
- hash *= 0x100000001b3u;
+ hash *= b;
hash ^= (uint8_t)*str;
}
return hash;
}
-size_t int_hash(uint32_t x) noexcept
+size_t hash_int(uint32_t x) noexcept
{
- if constexpr(sizeof(size_t) == 4)
- return FNVHash_32(x);
- else
- return FNVHash_64(x);
+ return fnvhash_uint_32(x);
}
-size_t int_hash(uint64_t x) noexcept
+size_t hash_int(uint64_t x) noexcept
{
- return FNVHash_64(x);
+ return fnvhash_uint_64(x);
}
} // namespace floormat
diff --git a/compat/int-hash.hpp b/compat/int-hash.hpp
index 86fc6a45..b3dd7616 100644
--- a/compat/int-hash.hpp
+++ b/compat/int-hash.hpp
@@ -1,11 +1,17 @@
#pragma once
+namespace floormat::impl_hash {
+
+template<size_t N> size_t hash_buf(const void* buf, size_t size) noexcept = delete;
+
+} // namespace floormat::impl_hash
+
namespace floormat {
-size_t int_hash(uint32_t x) noexcept;
-size_t int_hash(uint64_t x) noexcept;
+uint64_t hash_64(const void* buf, size_t size) noexcept;
+uint32_t hash_32(const void* buf, size_t size) noexcept;
-uint64_t fnvhash_64(const void* buf, size_t size);
-uint64_t fnvhash_32(const void* buf, size_t size);
+size_t hash_int(uint32_t x) noexcept;
+size_t hash_int(uint64_t x) noexcept;
} // namespace floormat
diff --git a/editor/app.cpp b/editor/app.cpp
index 8f3694ab..7505ff84 100644
--- a/editor/app.cpp
+++ b/editor/app.cpp
@@ -17,7 +17,7 @@
namespace floormat {
-struct Optional<struct point> cursor_state::point() const
+Optional<struct point> cursor_state::point() const
{
if (tile)
return {InPlaceInit, *tile, *subpixel};
diff --git a/serialize/wall-atlas.cpp b/serialize/wall-atlas.cpp
index 4a709060..31090e45 100644
--- a/serialize/wall-atlas.cpp
+++ b/serialize/wall-atlas.cpp
@@ -13,6 +13,8 @@ namespace floormat {
namespace {
+using nlohmann::json;
+constexpr auto none = (uint8_t)-1;
using namespace std::string_literals;
constexpr StringView rotation_names[] = { "n"_s, "e"_s, "s"_s, "w"_s, };
@@ -33,54 +35,33 @@ StringView rotation_to_name(size_t i)
return rotation_names[i];
}
-void read_frameset_metadata(const nlohmann::json& j, wall_frames& val, size_t& rot)
+[[nodiscard]] wall_frames read_frames_metadata(const json& jfs)
{
- val = {};
- rot = rotation_from_name(std::string{j["name"s]});
+ wall_frames val;
- if (j.contains("pixel-size"s))
- val.pixel_size = j["pixel-size"s];
- if (j.contains("tint"s))
+ if (jfs.contains("pixel-size"s))
+ val.pixel_size = jfs["pixel-size"s];
+ if (jfs.contains("tint"s))
{
- std::tie(val.tint_mult, val.tint_add) = std::pair<Vector4, Vector3>{j["tint"s]};
+ std::tie(val.tint_mult, val.tint_add) = std::pair<Vector4, Vector3>{ jfs["tint"s]};
fm_soft_assert(val.tint_mult >= Color4{0});
}
- if (j.contains("from-rotation"s))
- val.from_rotation = (uint8_t)rotation_from_name(std::string{j["from-rotation"s]});
- if (j.contains("mirrored"s))
- val.mirrored = !!j["mirrored"s];
- if (j.contains("use-default-tint"s))
- val.use_default_tint = !!j["use-default-tint"s];
+ if (jfs.contains("from-rotation"s))
+ val.from_rotation = (uint8_t)rotation_from_name(std::string{jfs["from-rotation"s]});
+ if (jfs.contains("mirrored"s))
+ val.mirrored = jfs["mirrored"s];
+ if (jfs.contains("use-default-tint"s))
+ val.use_default_tint = jfs["use-default-tint"s];
+
+ return val;
}
-void write_frameset_metadata(nlohmann::json& j, const wall_atlas& a, const wall_frames& val, size_t rot)
+[[nodiscard]] wall_frame_set read_frameset_metadata(const json& j)
{
- constexpr wall_frames default_value;
-
- fm_soft_assert(val.count != (uint32_t)-1);
- fm_soft_assert(val.index == (uint32_t)-1 || val.index < a.frame_array().size());
- fm_soft_assert((val.index == (uint32_t)-1) == (val.count == 0));
-
- j["name"s] = rotation_to_name(rot);
- j["pixel-size"s] = val.pixel_size;
- if (val.tint_mult != default_value.tint_mult || val.tint_add != default_value.tint_add)
- {
- auto tint = std::pair<Vector4, Vector3>{{val.tint_mult}, {val.tint_add}};
- j["tint"s] = tint;
- }
- if (val.from_rotation != default_value.from_rotation)
- {
- fm_soft_assert(val.from_rotation != (uint8_t)-1 && val.from_rotation < 4);
- j["from-rotation"s] = val.from_rotation;
- }
- if (val.mirrored != default_value.mirrored)
- j["mirrored"s] = val.mirrored;
- if (val.use_default_tint)
- if (val.tint_mult != default_value.tint_mult || val.tint_add != default_value.tint_add)
- j["use-default-tint"s] = true;
+ return {};
}
-void read_framesets(const nlohmann::json& jf, wall_atlas_def& val)
+void read_framesets(const json& jf, wall_atlas_def& val)
{
fm_soft_assert(jf.is_object());
fm_soft_assert(val.framesets == nullptr && val.frameset_count == 0);
@@ -95,7 +76,7 @@ void read_framesets(const nlohmann::json& jf, wall_atlas_def& val)
{
fm_soft_assert(jf[key].is_object());
auto& index = val.frameset_indexes[i];
- fm_soft_assert(index == (uint8_t)-1);
+ fm_soft_assert(index == none);
index = count++;
}
}
@@ -104,11 +85,50 @@ void read_framesets(const nlohmann::json& jf, wall_atlas_def& val)
val.framesets = std::make_unique<wall_frame_set[]>(count);
val.frameset_count = count;
+
+ for (auto i = 0uz; i < 4; i++)
+ {
+ auto index = val.frameset_indexes[i];
+ if (index == none)
+ continue;
+ auto r = rotation_to_name(i);
+ auto key = std::string_view{r.data(), r.size()};
+
+ fm_debug_assert(index < val.frameset_count);
+ val.framesets[index] = read_frameset_metadata(jf[key]);
+ }
+}
+
+void write_frameset_metadata(json& j, const wall_atlas& a, const wall_frames& val, size_t rot)
+{
+ constexpr wall_frames default_value;
+
+ fm_soft_assert(val.count != (uint32_t)-1);
+ fm_soft_assert(val.index == (uint32_t)-1 || val.index < a.frame_array().size());
+ fm_soft_assert((val.index == (uint32_t)-1) == (val.count == 0));
+
+ j["name"s] = rotation_to_name(rot);
+ j["pixel-size"s] = val.pixel_size;
+ if (val.tint_mult != default_value.tint_mult || val.tint_add != default_value.tint_add)
+ {
+ auto tint = std::pair<Vector4, Vector3>{{val.tint_mult}, {val.tint_add}};
+ j["tint"s] = tint;
+ }
+ if (val.from_rotation != default_value.from_rotation)
+ {
+ fm_soft_assert(val.from_rotation != none && val.from_rotation < 4);
+ j["from-rotation"s] = val.from_rotation;
+ }
+ if (val.mirrored != default_value.mirrored)
+ j["mirrored"s] = val.mirrored;
+ if (val.use_default_tint)
+ if (val.tint_mult != default_value.tint_mult || val.tint_add != default_value.tint_add)
+ j["use-default-tint"s] = true;
}
} // namespace
-void Serialize::wall_test::read_atlas_header(const nlohmann::json& j, wall_atlas_def& val)
+void Serialize::wall_test::read_atlas_header(const json& j, wall_atlas_def& val)
{
val = {};
val.info = { std::string(j["name"s]), j["depth"], };
diff --git a/src/point.cpp b/src/point.cpp
index a6e27f66..3e970ba3 100644
--- a/src/point.cpp
+++ b/src/point.cpp
@@ -9,10 +9,10 @@ size_t point::hash() const
static_assert(sizeof *this == size);
#ifdef FLOORMAT_64
static_assert(sizeof nullptr > 4);
- return fnvhash_64(this, sizeof *this);
+ return hash_64(this, sizeof *this);
#else
static_assert(sizeof nullptr == 4);
- return fnvhash_32(this, sizeof *this);
+ return hash_32(this, sizeof *this);
#endif
}
diff --git a/src/wall-atlas.cpp b/src/wall-atlas.cpp
index be4010c7..d73510bb 100644
--- a/src/wall-atlas.cpp
+++ b/src/wall-atlas.cpp
@@ -1,11 +1,32 @@
#include "wall-atlas.hpp"
#include "compat/assert.hpp"
+#include "compat/function2.hpp"
#include <utility>
#include <Magnum/ImageView.h>
#include <Magnum/GL/TextureFormat.h>
namespace floormat {
+namespace {
+
+#define FM_FRAMESET_ITER(Name) do { if (fun( #Name ## _s, const_cast<Self>(frameset.Name), wall_frame_set::type::Name)) return; } while(false)
+#define FM_FRAMESET_ITER2(Str, Name) do { if (fun( Str, const_cast<Self>(frameset.Name), wall_frame_set::type::Name )) return; } while(false)
+
+template<typename Self>
+CORRADE_ALWAYS_INLINE void visit_frameset_impl(const wall_frame_set& frameset, auto&& fun)
+{
+ FM_FRAMESET_ITER(wall);
+ FM_FRAMESET_ITER(overlay);
+ FM_FRAMESET_ITER(side);
+ FM_FRAMESET_ITER(top);
+ FM_FRAMESET_ITER2("corner-L"_s, corner_L);
+ FM_FRAMESET_ITER2("corner-R"_s, corner_R);
+}
+
+#undef FM_FRAMESET_ITER
+
+} // namespace
+
size_t wall_atlas::enum_to_index(enum rotation r)
{
static_assert(rotation_COUNT == rotation{8});
@@ -16,6 +37,17 @@ size_t wall_atlas::enum_to_index(enum rotation r)
return x;
}
+bool wall_frames::is_empty() const noexcept
+{
+ return count == 0;
+}
+
+bool wall_frame_set::is_empty() const noexcept
+{
+ return !wall.is_empty() && !overlay.is_empty() && !side.is_empty() && !top.is_empty() &&
+ !corner_L.is_empty() && !corner_R.is_empty();
+}
+
wall_atlas::wall_atlas(wall_info info, const ImageView2D& image,
Array<wall_frame> frames,
std::unique_ptr<wall_frame_set[]> framesets,
@@ -53,6 +85,16 @@ ArrayView<const wall_frame> wall_atlas::frame_array() const { return _frame_arra
StringView wall_atlas::name() const { return _info.name; }
const wall_frame_set& wall_atlas::frameset(enum rotation r) const { return frameset(enum_to_index(r)); }
+void wall_frame_set::visit(const fu2::function_view<bool(StringView name, const wall_frames& frames, type tag) const>& fun) const&
+{
+ visit_frameset_impl<const wall_frames&>(*this, fun);
+}
+
+void wall_frame_set::visit(const fu2::function_view<bool(StringView name, wall_frames& frames, type tag) const>& fun) &
+{
+ visit_frameset_impl<wall_frames&>(*this, fun);
+}
+
const wall_frame_set& wall_atlas::frameset(size_t i) const
{
fm_assert(i < 4 && _frameset_indexes[i] != (uint8_t)-1);
diff --git a/src/wall-atlas.hpp b/src/wall-atlas.hpp
index aa18c0bc..544bc5b7 100644
--- a/src/wall-atlas.hpp
+++ b/src/wall-atlas.hpp
@@ -1,5 +1,6 @@
#pragma once
#include "compat/defs.hpp"
+#include "compat/function2.fwd.hpp"
#include "src/rotation.hpp"
#include <array>
#include <memory>
@@ -20,6 +21,7 @@ struct wall_frame
struct wall_frames
{
+ bool is_empty() const noexcept;
ArrayView<const wall_frame> items(const wall_atlas& a) const;
uint32_t index = (uint32_t)-1, count = 0;
@@ -34,6 +36,13 @@ struct wall_frames
struct wall_frame_set
{
+ enum class type : uint8_t { wall = 1, overlay, side, top, corner_L, corner_R, };
+
+ bool is_empty() const noexcept;
+
+ void visit(const fu2::function_view<bool(StringView, const wall_frames&, type) const>& fun) const&;
+ void visit(const fu2::function_view<bool(StringView, wall_frames&, type) const>& fun) &;
+
wall_frames wall, overlay, side, top;
wall_frames corner_L, corner_R;
};
diff --git a/src/world.cpp b/src/world.cpp
index 5f2bb870..7920c205 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -6,7 +6,7 @@
using namespace floormat;
-size_t world::object_id_hasher::operator()(object_id id) const noexcept { return int_hash(id); }
+size_t world::object_id_hasher::operator()(object_id id) const noexcept { return hash_int(id); }
size_t world::chunk_coords_hasher::operator()(const chunk_coords_& coord) const noexcept
{
@@ -14,7 +14,7 @@ size_t world::chunk_coords_hasher::operator()(const chunk_coords_& coord) const
x |= uint64_t((uint16_t)coord.x) << 0;
x |= uint64_t((uint16_t)coord.y) << 16;
x |= uint64_t( (uint8_t)coord.z) << 32;
- return int_hash(x);
+ return hash_int(x);
}
namespace floormat {
diff --git a/test/hash.cpp b/test/hash.cpp
index 3a8af600..4e8d9d81 100644
--- a/test/hash.cpp
+++ b/test/hash.cpp
@@ -1,11 +1,31 @@
#include "app.hpp"
#include "compat/int-hash.hpp"
+#include <Corrade/Containers/StringView.h>
#include <bitset>
#include <memory>
namespace floormat {
-void test_app::test_hash()
+namespace {
+
+void test_simple()
+{
+ constexpr StringView list[] = { "foo"_s, "bar"_s, "bar\0"_s, "bar2"_s, "baz"_s, };
+ constexpr auto size = arraySize(list);
+
+ size_t hashes[size] = {};
+ for (auto i = 0uz; i < size; i++)
+ hashes[i] = hash_64(list[i].data(), list[i].size());
+
+ for (auto i = 0uz; i < size; i++)
+ for (auto j = i+1; j < size; j++)
+ {
+ if (hashes[i] == hashes[j])
+ fm_abort("hash collision between '%s' and '%s': 0x%p", list[i].data(), list[j].data(), (void*)hashes[i]);
+ }
+}
+
+void test_collisions()
{
constexpr int max = 8;
constexpr size_t size = (2*max+1)*(2*max+1)*4 * 10;
@@ -25,7 +45,7 @@ void test_app::test_hash()
value |= (uint64_t)(uint16_t)(int16_t)i << 0;
value |= (uint64_t)(uint16_t)(int16_t)j << 16;
value |= (uint64_t)(uint16_t)(int16_t)k << 32;
- auto x = (size_t)int_hash(value);
+ auto x = (size_t)hash_int(value);
//Debug {} << "bitset:" << i << j << k << "=" << x % size << "/" << x;
x %= size;
#if 1
@@ -33,7 +53,7 @@ void test_app::test_hash()
fm_abort("test/bitset: %zu collisions at iter %zu id %zu (%d;%d:%d)", num_collisions, iter, x, i, j, k);
#else
if ((void)max_collisions, bitset.test(x))
- fm_warn("test/bitset: %zu collisions at iter %zu id %zu (%d;%d:%d)", ++num_collisions, iter, x, i, j, k);
+ fm_warn("test/bitset: %zu collisions at iter %zu id %zu (%d;%d:%d)", ++num_collisions, iter, x, i, j, k);
#endif
bitset.set(x);
}
@@ -41,4 +61,12 @@ void test_app::test_hash()
}
}
+} // namespace
+
+void test_app::test_hash()
+{
+ test_simple();
+ test_collisions();
+}
+
} // namespace floormat
diff --git a/test/json/frameset-header2.json b/test/json/frameset-header2.json
new file mode 100644
index 00000000..8b32cabc
--- /dev/null
+++ b/test/json/frameset-header2.json
@@ -0,0 +1,9 @@
+{
+ "name": "foo",
+ "depth": 42,
+ "framesets": {
+ "w": {},
+ "n": {}
+ },
+ "frames": []
+}
diff --git a/test/wall-atlas.cpp b/test/wall-atlas.cpp
index b1cdd8d6..720c118f 100644
--- a/test/wall-atlas.cpp
+++ b/test/wall-atlas.cpp
@@ -4,18 +4,20 @@
#include "serialize/json-helper.hpp"
#include "loader/loader.hpp"
#include <algorithm>
+#include <Corrade/Containers/PairStl.h>
#include <Corrade/Utility/Path.h>
namespace floormat {
namespace ranges = std::ranges;
namespace test = floormat::Serialize::wall_test;
+using nlohmann::json;
namespace {
-void test_atlas_header(StringView path)
+Pair<wall_atlas_def, json> test_atlas_header(StringView path, StringView filename)
{
- auto j = json_helper::from_json_(Path::join(path, "frameset-header.json"_s));
+ auto j = json_helper::from_json_(Path::join(path, filename));
wall_atlas_def def;
test::read_atlas_header(j, def);
@@ -34,8 +36,10 @@ void test_atlas_header(StringView path)
fm_assert(def.frameset_indexes[W] == 1);
else if (def.frameset_indexes[N] == 1)
fm_assert(def.frameset_indexes[W] == 0);
+ else
+ fm_assert(false);
- std::fputs("", stdout);
+ return {std::move(def), std::move(j)};
}
} // namespace
@@ -46,7 +50,7 @@ void test_app::test_wall_atlas()
const auto path = Path::join(loader.TEMP_PATH, "test/json"_s);
fm_assert(Path::isDirectory(path));
- test_atlas_header(path);
+ test_atlas_header(path, "frameset-header.json"_s);
}