Serialization for idalloc and animqueue

This commit is contained in:
2021-03-13 13:05:00 -05:00
parent 2e0befe817
commit 4426fa157a
7 changed files with 289 additions and 39 deletions

View File

@@ -6,15 +6,21 @@ AnimStep::AnimStep() {}
AnimStep::~AnimStep() {}
AnimQueue::AnimQueue() {
id_ = 0;
size_limit_ = 10; // Default size limit.
clear_steps();
}
void AnimQueue::clear_steps() {
steps_.clear();
steps_.emplace_back();
AnimStep &init = steps_.back();
init.id_ = 0;
init.bits_ = AnimStep::HAS_EVERYTHING;
init.facing_ = 0;
init.xyz_ = util::XYZ(0,0,0);
init.graphic_ = "nothing";
init.plane_ = "nowhere";
init.bits_ = AnimStep::HAS_EVERYTHING;
}
void AnimQueue::set_size_limit(int n) {
@@ -163,6 +169,94 @@ void AnimQueue::set_plane(const std::string &p) {
last.plane_ = p;
}
bool AnimQueue::valid() {
// Animqueue must have at least one step.
if (steps_.empty()) {
return false;
}
// First action should be blank.
if (steps_[0].action_ != "") {
return false;
}
// First step should have all bits.
if (steps_[0].bits_ != AnimStep::HAS_EVERYTHING) {
return false;
}
// Any unset bit should correspond to a value copied from the previous step.
for (size_t i = 1; i < steps_.size(); i++) {
const AnimStep &prev = steps_[i - 1];
const AnimStep &curr = steps_[i];
if (!curr.has_facing() && (curr.facing_ != prev.facing_)) {
return false;
}
if (!curr.has_xyz() && (curr.xyz_ != prev.xyz_)) {
return false;
}
if (!curr.has_graphic() && (curr.graphic_ != prev.graphic_)) {
return false;
}
if (!curr.has_plane() && (curr.plane_ != prev.plane_)) {
return false;
}
}
return true;
}
void AnimQueue::serialize(StreamBuffer *sb) {
assert(valid()); // can't serialize an invalid animqueue.
sb->write_int64(id_);
sb->write_int32(size_limit_);
sb->write_size(steps_.size());
for (const AnimStep &step : steps_) {
sb->write_int64(step.id_);
sb->write_int16(step.bits_);
sb->write_string(step.action_);
if (step.has_facing()) {
sb->write_float(step.facing_);
}
if (step.has_xyz()) {
sb->write_float(step.xyz_.x);
sb->write_float(step.xyz_.y);
sb->write_float(step.xyz_.z);
}
if (step.has_graphic()) {
sb->write_string(step.graphic_);
}
if (step.has_plane()) {
sb->write_string(step.plane_);
}
}
}
void AnimQueue::deserialize(StreamBuffer *sb) {
id_ = sb->read_int64();
size_limit_ = sb->read_int32();
size_t nsteps = sb->read_size();
steps_.clear();
steps_.resize(nsteps);
for (size_t i = 0; i < nsteps; i++) {
AnimStep &step = steps_[i];
if (i > 0) step = steps_[i - 1];
step.id_ = sb->read_int64();
step.bits_ = sb->read_int16();
step.action_ = sb->read_string();
if (step.has_facing()) {
step.facing_ = sb->read_float();
}
if (step.has_xyz()) {
step.xyz_.x = sb->read_float();
step.xyz_.y = sb->read_float();
step.xyz_.z = sb->read_float();
}
if (step.has_graphic()) {
step.graphic_ = sb->read_string();
}
if (step.has_plane()) {
step.plane_ = sb->read_string();
}
}
}
const std::string &AnimQueue::get_graphic() const {
const AnimStep &last = steps_.back();
return last.graphic_;
@@ -181,8 +275,11 @@ const util::XYZ &AnimQueue::get_xyz() const {
LuaDefine(unittests_animqueue, "c") {
// Check initial state.
AnimQueue aq;
StreamBuffer sb;
AnimQueue aqds;
aq.set_size_limit(3);
LuaAssert(L, aq.valid());
LuaAssert(L, aq.size() == 1);
const AnimStep *st = &aq.nth(0);
LuaAssert(L, st->id() == 0);
@@ -195,6 +292,7 @@ LuaDefine(unittests_animqueue, "c") {
// Add a step.
aq.add(12345, "walk");
LuaAssert(L, aq.valid());
LuaAssert(L, aq.size() == 2);
st = &aq.nth(1);
LuaAssert(L, st->id() == 12345);
@@ -218,6 +316,7 @@ LuaDefine(unittests_animqueue, "c") {
aq.set_graphic("something");
LuaAssert(L, st->graphic() == "something");
LuaAssert(L, st->bits() == (AnimStep::HAS_FACING | AnimStep::HAS_XYZ | AnimStep::HAS_PLANE | AnimStep::HAS_GRAPHIC));
LuaAssert(L, aq.valid());
// Exceed the length limit, dropping first element.
aq.add(12346, "walk");
@@ -228,5 +327,56 @@ LuaDefine(unittests_animqueue, "c") {
LuaAssert(L, aq.nth(0).bits() == AnimStep::HAS_EVERYTHING);
LuaAssert(L, aq.nth(1).id() == 12346);
LuaAssert(L, aq.nth(2).id() == 12347);
LuaAssert(L, aq.valid());
// Test serialization and deserialization.
aq.set_id(123);
aq.set_size_limit(5);
aq.clear_steps();
aq.add(12345, "walk");
aq.set_xyz(util::XYZ(3,4,5));
aq.add(12346, "setgraphic");
aq.set_graphic("banana");
aq.add(12347, "setfacing");
aq.set_facing(301.0);
aq.serialize(&sb);
aqds.deserialize(&sb);
LuaAssert(L, aqds.get_id() == 123);
LuaAssert(L, aqds.size_limit() == 5);
LuaAssert(L, aqds.size() == 4);
LuaAssert(L, aqds.nth(0).id() == 0);
LuaAssert(L, aqds.nth(0).bits() == AnimStep::HAS_EVERYTHING);
LuaAssert(L, aqds.nth(0).action() == "");
LuaAssert(L, aqds.nth(0).facing() == 0.0);
LuaAssert(L, aqds.nth(0).xyz() == util::XYZ(0,0,0));
LuaAssert(L, aqds.nth(0).graphic() == "nothing");
LuaAssert(L, aqds.nth(0).plane() == "nowhere");
LuaAssert(L, aqds.nth(1).id() == 12345);
LuaAssert(L, aqds.nth(1).bits() == AnimStep::HAS_XYZ);
LuaAssert(L, aqds.nth(1).action() == "walk");
LuaAssert(L, aqds.nth(1).facing() == 0.0);
LuaAssert(L, aqds.nth(1).xyz() == util::XYZ(3,4,5));
LuaAssert(L, aqds.nth(1).graphic() == "nothing");
LuaAssert(L, aqds.nth(1).plane() == "nowhere");
LuaAssert(L, aqds.nth(2).id() == 12346);
LuaAssert(L, aqds.nth(2).bits() == AnimStep::HAS_GRAPHIC);
LuaAssert(L, aqds.nth(2).action() == "setgraphic");
LuaAssert(L, aqds.nth(2).facing() == 0.0);
LuaAssert(L, aqds.nth(2).xyz() == util::XYZ(3,4,5));
LuaAssert(L, aqds.nth(2).graphic() == "banana");
LuaAssert(L, aqds.nth(2).plane() == "nowhere");
LuaAssert(L, aqds.nth(3).id() == 12347);
LuaAssert(L, aqds.nth(3).bits() == AnimStep::HAS_FACING);
LuaAssert(L, aqds.nth(3).action() == "setfacing");
LuaAssert(L, aqds.nth(3).facing() == 301.0);
LuaAssert(L, aqds.nth(3).xyz() == util::XYZ(3,4,5));
LuaAssert(L, aqds.nth(3).graphic() == "banana");
LuaAssert(L, aqds.nth(3).plane() == "nowhere");
return 0;
}

View File

@@ -29,6 +29,7 @@
#include <deque>
#include <cassert>
#include <unordered_map>
#include "streambuffer.hpp"
#include "util.hpp"
@@ -45,8 +46,8 @@ public:
private:
int64_t id_;
int16_t bits_;
std::string action_;
int bits_;
float facing_;
util::XYZ xyz_;
@@ -58,8 +59,8 @@ public:
~AnimStep();
int64_t id() const { return id_; }
const std::string &action() const { return action_; }
int bits() const { return bits_; }
const std::string &action() const { return action_; }
double facing() const { return facing_; }
util::XYZ xyz() const { return xyz_; }
@@ -75,15 +76,21 @@ public:
class AnimQueue {
private:
int64_t id_;
int size_limit_;
int32_t size_limit_;
std::deque<AnimStep> steps_;
public:
AnimQueue();
int64_t get_id() const { return id_; }
void set_id(int64_t id) { id_ = id; }
const AnimStep &nth(int n) const { return steps_[n]; }
int size() const { return steps_.size(); }
size_t size() const { return steps_.size(); }
int32_t size_limit() const { return size_limit_; }
// Clear the steps. Doesn't affect size_limit or id.
void clear_steps();
// Mutators to create new steps from C++
//
void add(int64_t id, const std::string &action);
@@ -92,6 +99,10 @@ public:
void set_graphic(const std::string &g);
void set_plane(const std::string &p);
// Serialize or deserialize to a StreamBuffer
void serialize(StreamBuffer *sb);
void deserialize(StreamBuffer *sb);
// Mutator to create new steps from lua.
//
// Lua stack must contain a table, which may contain:
@@ -113,8 +124,11 @@ public:
const std::string &get_plane() const;
const util::XYZ &get_xyz() const;
// Functions for unit testing.
void set_size_limit(int n);
// (For testing): change the size limit.
void set_size_limit(int32_t n);
// (For testing): make sure the invariants are preserved.
bool valid();
};
#endif // ANIMQUEUE_HPP

View File

@@ -84,24 +84,24 @@ int64_t IdGlobalPool::alloc_id_for_thread(lua_State *L) {
}
void IdGlobalPool::serialize(StreamBuffer *sb) {
sb->write_int64(salvaged_.size());
sb->write_int64(next_batch_);
sb->write_int64(next_id_);
sb->write_int32(queue_fill_);
sb->write_size(salvaged_.size());
for (int64_t batch : salvaged_) {
sb->write_int64(batch);
}
sb->write_int64(next_batch_);
sb->write_int64(next_id_);
sb->write_int64(queue_fill_);
}
void IdGlobalPool::deserialize(StreamBuffer *sb) {
int64_t salvaged_size = sb->read_int64();
salvaged_.resize(salvaged_size);
for (int i=0; i < salvaged_size; i++) {
salvaged_[i] = sb->read_int64();
}
next_batch_ = sb->read_int64();
next_id_ = sb->read_int64();
queue_fill_ = sb->read_int64();
queue_fill_ = sb->read_int32();
size_t salvaged_size = sb->read_size();
salvaged_.resize(salvaged_size);
for (int i=0; i < int(salvaged_size); i++) {
salvaged_[i] = sb->read_int64();
}
}
IdPlayerPool::IdPlayerPool(IdGlobalPool *gp) {
@@ -155,16 +155,16 @@ void IdPlayerPool::prepare_thread(lua_State *L) {
}
void IdPlayerPool::serialize(StreamBuffer *sb) {
sb->write_int64(ranges_.size());
sb->write_size(ranges_.size());
for (int64_t batch : ranges_) {
sb->write_int64(batch);
}
}
void IdPlayerPool::deserialize(StreamBuffer *sb) {
int64_t ranges_size = sb->read_int64();
size_t ranges_size = sb->read_size();
ranges_.resize(ranges_size);
for (int i=0; i < ranges_size; i++) {
for (int i=0; i < int(ranges_size); i++) {
ranges_[i] = sb->read_int64();
}
}
@@ -172,6 +172,9 @@ void IdPlayerPool::deserialize(StreamBuffer *sb) {
LuaDefine(unittests_idalloc, "c") {
IdGlobalPool gp;
IdPlayerPool pp(&gp);
IdGlobalPool gpds;
IdPlayerPool ppds(&gpds);
StreamBuffer sb;
// Synchronous pools produce IDs starting at 0x001E000000000000
gp.init_synch(3);
@@ -277,5 +280,35 @@ LuaDefine(unittests_idalloc, "c") {
LuaAssert(L, gp.alloc_id_for_thread(L) == 0x0010000000000000);
LuaAssert(L, lua_getnextid(L) == 0);
// Serialize and deserialize a global pool.
gp.init_master(3);
gpds.init_master(10);
LuaAssert(L, gp.get_one() == 0x0010000000000000);
LuaAssert(L, gp.get_batch() == nthbatch(0));
gp.salvage(nthbatch(182));
gp.salvage(nthbatch(183));
gp.serialize(&sb);
gpds.deserialize(&sb);
LuaAssert(L, gpds.queue_fill() == 3);
LuaAssert(L, gpds.get_one() == 0x0010000000000001);
LuaAssert(L, gpds.get_batch() == nthbatch(183));
LuaAssert(L, gpds.get_batch() == nthbatch(182));
LuaAssert(L, gpds.get_batch() == nthbatch(1));
// Serialize and deserialize a player pool.
gp.init_master(3);
gpds.init_synch(5);
LuaAssert(L, gp.get_batch() == nthbatch(0));
pp.purge();
pp.refill();
LuaAssert(L, pp.size() == 3);
ppds.purge();
pp.serialize(&sb);
ppds.deserialize(&sb);
LuaAssert(L, ppds.size() == 3);
LuaAssert(L, ppds.get_batch() == nthbatch(1));
LuaAssert(L, ppds.get_batch() == nthbatch(2));
LuaAssert(L, ppds.get_batch() == nthbatch(3));
return 0;
}

View File

@@ -76,7 +76,7 @@ private:
std::vector<int64_t> salvaged_;
int64_t next_batch_;
int64_t next_id_;
int queue_fill_;
int32_t queue_fill_;
friend int unittests_idalloc(lua_State *L);
public:
@@ -120,10 +120,8 @@ public:
// batch if possible. If not, fetches one ID from the global pool.
int64_t alloc_id_for_thread(lua_State *L);
// Serialize to a streambuffer.
// Serialize to or deserialize from a streambuffer.
void serialize(StreamBuffer *sb);
// Deserialize from a streambuffer.
void deserialize(StreamBuffer *sb);
};
@@ -163,10 +161,9 @@ public:
// Return the size of the queue.
int size() { return ranges_.size(); }
// Serialize to a streambuffer.
// Serialize to or deserialize from a streambuffer.
// Caution: the pointer to the global pool is not serialized or deserialized.
void serialize(StreamBuffer *sb);
// Deserialize from a streambuffer.
void deserialize(StreamBuffer *sb);
};

View File

@@ -139,6 +139,18 @@ void StreamBuffer::write_int64(int64_t v) {
write_cursor_ += 8;
}
void StreamBuffer::write_float(float f) {
make_space(4);
memcpy(write_cursor_, &f, 4);
write_cursor_ += 4;
}
void StreamBuffer::write_double(double d) {
make_space(8);
memcpy(write_cursor_, &d, 8);
write_cursor_ += 8;
}
void StreamBuffer::write_bytes(const char *s, int64_t len) {
make_space(len);
memcpy(write_cursor_, s, len);
@@ -212,6 +224,34 @@ int64_t StreamBuffer::read_int64() {
return v;
}
float StreamBuffer::read_float() {
check_available(4);
float f;
memcpy(&f, read_cursor_, 4);
read_cursor_ += 4;
return f;
}
double StreamBuffer::read_double() {
check_available(8);
double d;
memcpy(&d, read_cursor_, 8);
read_cursor_ += 8;
return d;
}
size_t StreamBuffer::read_size() {
return read_size_limit(0xFFFFFFF);
}
size_t StreamBuffer::read_size_limit(size_t limit) {
int64_t value = read_int64();
if ((value < 0)||(value > int64_t(limit))) {
throw StreamCorruption();
}
return size_t(value);
}
const char *StreamBuffer::read_bytes(int64_t bytes) {
check_available(bytes);
char *data = read_cursor_;
@@ -219,7 +259,11 @@ const char *StreamBuffer::read_bytes(int64_t bytes) {
return data;
}
std::string StreamBuffer::read_string(int64_t max_allowed) {
std::string StreamBuffer::read_string() {
return read_string_limit(0xFFFFFFF);
}
std::string StreamBuffer::read_string_limit(int64_t max_allowed) {
int64_t len = read_uint8();
if (len == 255) {
len = read_int64();
@@ -388,9 +432,9 @@ LuaDefine(unittests_streambuffer, "c") {
sb11.write_string("");
sb11.write_string("de");
assert(sb11.layout_is(0, 8, 3));
assert(sb11.read_string(1000) == "abc");
assert(sb11.read_string(1000) == "");
assert(sb11.read_string(1000) == "de");
assert(sb11.read_string() == "abc");
assert(sb11.read_string() == "");
assert(sb11.read_string() == "de");
return 0;
}

View File

@@ -308,18 +308,22 @@ public:
char *alloc_space(int64_t bytes);
void wrote_space(int64_t bytes);
// Write values into the buffer.
// Write numbers into the buffer.
void write_int8(int8_t v);
void write_int16(int16_t v);
void write_int32(int32_t v);
void write_int64(int64_t v);
void write_float(float f);
void write_double(double d);
void write_uint8(uint8_t v) { write_int8(v); }
void write_uint16(uint16_t v) { write_int16(v); }
void write_uint32(uint32_t v) { write_int32(v); }
void write_uint64(uint64_t v) { write_int64(v); }
void write_bytes(const char *bytes, int64_t len);
void write_size(size_t sz) { write_int64(sz); }
// Write strings or blocks of bytes into the buffer.
void write_string(const std::string &s);
void write_bytes(const char *bytes, int64_t len);
void write_ztbytes(const char *bytes);
// Overwrite values previously written to the buffer.
@@ -332,19 +336,26 @@ public:
void overwrite_uint32(int64_t write_count_after, uint32_t v) { overwrite_int32(write_count_after, v); }
void overwrite_uint64(int64_t write_count_after, uint64_t v) { overwrite_int64(write_count_after, v); }
// Read integers from the buffer. May throw StreamEof.
// Read numbers from the buffer. May throw StreamEof.
int8_t read_int8();
int16_t read_int16();
int32_t read_int32();
int64_t read_int64();
float read_float();
double read_double();
uint8_t read_uint8() { return read_int8(); }
uint16_t read_uint16() { return read_int16(); }
uint32_t read_uint32() { return read_int32(); }
uint64_t read_uint64() { return read_int64(); }
// Read a string of no more than the specified length. May throw StreamEof
// or StreamCorruption.
std::string read_string(int64_t max_allowed);
// May throw StreamEof or StreamCorruption.
size_t read_size();
size_t read_size_limit(size_t limit);
// Read a string of no more than the specified length.
// May throw StreamEof or StreamCorruption.
std::string read_string();
std::string read_string_limit(int64_t max_allowed);
// Read a block of bytes. May throw StreamEof.
const char *read_bytes(int64_t bytes);

View File

@@ -52,7 +52,8 @@ struct XYZ {
float x, y, z;
XYZ() { x=0; y=0; z=0; }
XYZ(float ix, float iy, float iz) { x=ix; y=iy; z=iz; }
bool operator ==(const XYZ &o) { return x==o.x && y == o.y && z==o.z; }
bool operator ==(const XYZ &o) const { return x==o.x && y == o.y && z==o.z; }
bool operator !=(const XYZ &o) const { return x!=o.x || y != o.y || z!=o.z; }
};
std::ostream & operator << (std::ostream &out, const XYZ &xyz);