Diff xmit of ID player pools
This commit is contained in:
@@ -301,10 +301,13 @@ void AnimQueue::add(int64_t id, const AnimStep &step) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AnimQueue::valid() const {
|
bool AnimQueue::valid() const {
|
||||||
// Animqueue must have at least one step.
|
// Animqueue must have at least one step, and no more than 255.
|
||||||
if (steps_.empty()) {
|
if (steps_.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (steps_.size() > 255) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// First action should be blank.
|
// First action should be blank.
|
||||||
if (steps_[0].action_ != "") {
|
if (steps_[0].action_ != "") {
|
||||||
return false;
|
return false;
|
||||||
@@ -342,17 +345,19 @@ void AnimQueue::deserialize(StreamBuffer *sb) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimQueue::make_patch(const AnimQueue &auth, StreamBuffer *sb) const {
|
bool AnimQueue::make_patch(const AnimQueue &auth, StreamBuffer *sb) const {
|
||||||
// Sanity check.
|
// Sanity check.
|
||||||
assert(valid());
|
assert(valid());
|
||||||
assert(auth.valid());
|
assert(auth.valid());
|
||||||
|
|
||||||
// Special case: if we're already a perfect match, output 0 bytes.
|
// Special case: if we're already a perfect match, output 0 bytes.
|
||||||
if (exactly_equal(auth)) {
|
if (exactly_equal(auth)) {
|
||||||
return;
|
sb->write_uint8(0);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the first element.
|
// Write the first element.
|
||||||
|
sb->write_uint8(auth.steps_.size());
|
||||||
const AnimStep &first = auth.steps_[0];
|
const AnimStep &first = auth.steps_[0];
|
||||||
int match = 0;
|
int match = 0;
|
||||||
while ((match < int(steps_.size())) && (!steps_[match].state_equal(first))) {
|
while ((match < int(steps_.size())) && (!steps_[match].state_equal(first))) {
|
||||||
@@ -388,18 +393,19 @@ void AnimQueue::make_patch(const AnimQueue &auth, StreamBuffer *sb) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimQueue::apply_patch(StreamBuffer *sb) {
|
void AnimQueue::apply_patch(StreamBuffer *sb) {
|
||||||
// Special case: if there are zero bytes, no patch is needed.
|
int len = sb->read_uint8();
|
||||||
if (sb->at_eof()) {
|
if (len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode the diff, stop at eof.
|
// Decode the diff, stop at eof.
|
||||||
std::deque<AnimStep> old = std::move(steps_);
|
std::deque<AnimStep> old = std::move(steps_);
|
||||||
steps_.clear();
|
steps_.clear();
|
||||||
while (!sb->at_eof()) {
|
for (int i = 0; i < len; i++) {
|
||||||
uint8_t index = sb->read_uint8();
|
uint8_t index = sb->read_uint8();
|
||||||
if (index < 255) {
|
if (index < 255) {
|
||||||
assert(index < old.size());
|
assert(index < old.size());
|
||||||
@@ -412,9 +418,10 @@ void AnimQueue::apply_patch(StreamBuffer *sb) {
|
|||||||
int size = steps_.size();
|
int size = steps_.size();
|
||||||
if (size > 1) {
|
if (size > 1) {
|
||||||
steps_[size-1].echo(steps_[size-2]);
|
steps_[size-1].echo(steps_[size-2]);
|
||||||
|
} else {
|
||||||
|
steps_[0].keep_state_only();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
steps_.front().keep_state_only();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimStep &AnimQueue::back() const {
|
const AnimStep &AnimQueue::back() const {
|
||||||
@@ -559,7 +566,7 @@ LuaDefine(unittests_animqueue, "c") {
|
|||||||
aq.add(12345, stp);
|
aq.add(12345, stp);
|
||||||
|
|
||||||
sb.clear();
|
sb.clear();
|
||||||
aqds.make_patch(aq, &sb);
|
LuaAssert(L, aqds.make_patch(aq, &sb));
|
||||||
aqds.apply_patch(&sb);
|
aqds.apply_patch(&sb);
|
||||||
LuaAssert(L, aqds.exactly_equal(aq));
|
LuaAssert(L, aqds.exactly_equal(aq));
|
||||||
|
|
||||||
@@ -570,8 +577,19 @@ LuaDefine(unittests_animqueue, "c") {
|
|||||||
stp.set_plane("where");
|
stp.set_plane("where");
|
||||||
aq.add(232, stp);
|
aq.add(232, stp);
|
||||||
|
|
||||||
|
// Generate diffs, but add 4 extra bytes.
|
||||||
sb.clear();
|
sb.clear();
|
||||||
aqds.make_patch(aq, &sb);
|
LuaAssert(L, aqds.make_patch(aq, &sb));
|
||||||
|
sb.write_uint32(0);
|
||||||
|
|
||||||
|
// Apply the diffs, 4 extra bytes should remain.
|
||||||
|
aqds.apply_patch(&sb);
|
||||||
|
LuaAssert(L, sb.write_count() - sb.read_count() == 4);
|
||||||
|
LuaAssert(L, aqds.exactly_equal(aq));
|
||||||
|
|
||||||
|
// compare again, should be no differences.
|
||||||
|
sb.clear();
|
||||||
|
LuaAssert(L, !aqds.make_patch(aq, &sb));
|
||||||
aqds.apply_patch(&sb);
|
aqds.apply_patch(&sb);
|
||||||
LuaAssert(L, aqds.exactly_equal(aq));
|
LuaAssert(L, aqds.exactly_equal(aq));
|
||||||
|
|
||||||
@@ -579,7 +597,7 @@ LuaDefine(unittests_animqueue, "c") {
|
|||||||
aq.keep_only(1);
|
aq.keep_only(1);
|
||||||
|
|
||||||
sb.clear();
|
sb.clear();
|
||||||
aqds.make_patch(aq, &sb);
|
LuaAssert(L, aqds.make_patch(aq, &sb));
|
||||||
aqds.apply_patch(&sb);
|
aqds.apply_patch(&sb);
|
||||||
LuaAssert(L, aqds.exactly_equal(aq));
|
LuaAssert(L, aqds.exactly_equal(aq));
|
||||||
|
|
||||||
|
|||||||
@@ -168,11 +168,10 @@ public:
|
|||||||
|
|
||||||
// Difference transmission
|
// Difference transmission
|
||||||
//
|
//
|
||||||
// make_patch will emit zero bytes when there are no differences.
|
// When there are no differences, make_patch returns false but
|
||||||
// make_patch does not put a 'length' field for the patch as a whole.
|
// still writes out a valid patch.
|
||||||
// apply_patch must receive a patch followed by eof.
|
|
||||||
//
|
//
|
||||||
void make_patch(const AnimQueue &auth, StreamBuffer *sb) const;
|
bool make_patch(const AnimQueue &auth, StreamBuffer *sb) const;
|
||||||
void apply_patch(StreamBuffer *sb);
|
void apply_patch(StreamBuffer *sb);
|
||||||
|
|
||||||
// Get the final resting place after all animations are complete.
|
// Get the final resting place after all animations are complete.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "idalloc.hpp"
|
#include "idalloc.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
|
||||||
static int64_t nthbatch(int64_t n) {
|
static int64_t nthbatch(int64_t n) {
|
||||||
@@ -121,7 +122,15 @@ void IdPlayerPool::disable_fifo() {
|
|||||||
fifo_enabled_ = false;
|
fifo_enabled_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IdPlayerPool::purge() {
|
void IdPlayerPool::test_push_back(int64_t range) {
|
||||||
|
ranges_.push_back(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdPlayerPool::test_pop_front() {
|
||||||
|
ranges_.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdPlayerPool::test_clear_ranges() {
|
||||||
ranges_.clear();
|
ranges_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +193,96 @@ void IdPlayerPool::deserialize(StreamBuffer *sb) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IdPlayerPool::exactly_equal(const IdPlayerPool &other) const {
|
||||||
|
if (fifo_enabled_ != other.fifo_enabled_) return false;
|
||||||
|
if (ranges_.size() != other.ranges_.size()) return false;
|
||||||
|
for (int i = 0; i < int(ranges_.size()); i++) {
|
||||||
|
if (ranges_[i] != other.ranges_[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IdPlayerPool::valid() const {
|
||||||
|
if ((!fifo_enabled_) && (ranges_.size() > 0)) return false;
|
||||||
|
if (ranges_.size() > 250) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IdPlayerPool::make_patch(const IdPlayerPool &auth, StreamBuffer *sb) const {
|
||||||
|
assert(valid());
|
||||||
|
assert(auth.valid());
|
||||||
|
assert(global_->queue_fill() == auth.global_->queue_fill());
|
||||||
|
|
||||||
|
// The first byte.
|
||||||
|
// 0 no differences
|
||||||
|
// 1 fifo disabled and ranges empty
|
||||||
|
// 2+ fifo enabled and N-2 ranges.
|
||||||
|
//
|
||||||
|
if (exactly_equal(auth)) {
|
||||||
|
sb->write_uint8(0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!auth.fifo_enabled_) {
|
||||||
|
sb->write_uint8(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb->write_uint8(auth.ranges_.size() + 2);
|
||||||
|
|
||||||
|
// Build up an index of the known IDs.
|
||||||
|
std::map<int64_t, int> index;
|
||||||
|
for (int i = 0; i < int(ranges_.size()); i++) {
|
||||||
|
index[ranges_[i]] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Write the ranges, but encode known IDs in one byte.
|
||||||
|
for (int i = 0; i < int(auth.ranges_.size()); i++) {
|
||||||
|
int64_t n = auth.ranges_[i];
|
||||||
|
auto iter = index.find(n);
|
||||||
|
if (iter == index.end()) {
|
||||||
|
sb->write_uint8(255);
|
||||||
|
sb->write_int64(n);
|
||||||
|
} else {
|
||||||
|
int slot = iter->second;
|
||||||
|
sb->write_uint8(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdPlayerPool::apply_patch(StreamBuffer *sb) {
|
||||||
|
// read the header byte
|
||||||
|
int nranges = sb->read_uint8();
|
||||||
|
if (nranges == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nranges == 1) {
|
||||||
|
fifo_enabled_ = false;
|
||||||
|
ranges_.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fifo_enabled_ = true;
|
||||||
|
nranges -= 2;
|
||||||
|
|
||||||
|
std::deque<int64_t> old = std::move(ranges_);
|
||||||
|
ranges_.clear();
|
||||||
|
for (int i = 0; i < nranges; i++) {
|
||||||
|
int index = sb->read_uint8();
|
||||||
|
if (index < 255) {
|
||||||
|
assert(index < int(old.size()));
|
||||||
|
ranges_.push_back(old[index]);
|
||||||
|
} else {
|
||||||
|
ranges_.push_back(sb->read_int64());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LuaDefine(unittests_idalloc, "c") {
|
LuaDefine(unittests_idalloc, "c") {
|
||||||
IdGlobalPool gp;
|
IdGlobalPool gp;
|
||||||
IdPlayerPool pp(&gp);
|
IdPlayerPool pp(&gp);
|
||||||
@@ -244,7 +343,7 @@ LuaDefine(unittests_idalloc, "c") {
|
|||||||
LuaAssert(L, gp.get_batch() == nthbatch(2));
|
LuaAssert(L, gp.get_batch() == nthbatch(2));
|
||||||
|
|
||||||
// In the synchronous model, refill should do nothing.
|
// In the synchronous model, refill should do nothing.
|
||||||
pp.purge();
|
pp.test_clear_ranges();
|
||||||
pp.enable_fifo();
|
pp.enable_fifo();
|
||||||
gp.init_synch(3);
|
gp.init_synch(3);
|
||||||
pp.refill();
|
pp.refill();
|
||||||
@@ -255,7 +354,7 @@ LuaDefine(unittests_idalloc, "c") {
|
|||||||
|
|
||||||
// In the master model, with fifo disabled. Fifo should remain
|
// In the master model, with fifo disabled. Fifo should remain
|
||||||
// empty, but batches should be returned.
|
// empty, but batches should be returned.
|
||||||
pp.purge();
|
pp.test_clear_ranges();
|
||||||
pp.disable_fifo();
|
pp.disable_fifo();
|
||||||
gp.init_master(3);
|
gp.init_master(3);
|
||||||
pp.refill();
|
pp.refill();
|
||||||
@@ -265,7 +364,7 @@ LuaDefine(unittests_idalloc, "c") {
|
|||||||
LuaAssert(L, pp.get_batch() == nthbatch(1));
|
LuaAssert(L, pp.get_batch() == nthbatch(1));
|
||||||
|
|
||||||
// Test refill from master (with enabled fifo).
|
// Test refill from master (with enabled fifo).
|
||||||
pp.purge();
|
pp.test_clear_ranges();
|
||||||
pp.enable_fifo();
|
pp.enable_fifo();
|
||||||
gp.init_master(3);
|
gp.init_master(3);
|
||||||
pp.refill();
|
pp.refill();
|
||||||
@@ -285,7 +384,7 @@ LuaDefine(unittests_idalloc, "c") {
|
|||||||
LuaAssert(L, gp.get_batch() == nthbatch(6));
|
LuaAssert(L, gp.get_batch() == nthbatch(6));
|
||||||
|
|
||||||
// Try preparing a thread and salvaging a thread.
|
// Try preparing a thread and salvaging a thread.
|
||||||
pp.purge();
|
pp.test_clear_ranges();
|
||||||
pp.enable_fifo();
|
pp.enable_fifo();
|
||||||
gp.init_master(3);
|
gp.init_master(3);
|
||||||
lua_setnextid(L, 0);
|
lua_setnextid(L, 0);
|
||||||
@@ -328,12 +427,12 @@ LuaDefine(unittests_idalloc, "c") {
|
|||||||
gp.init_master(3);
|
gp.init_master(3);
|
||||||
gpds.init_synch(5);
|
gpds.init_synch(5);
|
||||||
LuaAssert(L, gp.get_batch() == nthbatch(0));
|
LuaAssert(L, gp.get_batch() == nthbatch(0));
|
||||||
pp.purge();
|
pp.test_clear_ranges();
|
||||||
pp.enable_fifo();
|
pp.enable_fifo();
|
||||||
pp.refill();
|
pp.refill();
|
||||||
LuaAssert(L, pp.fifo_enabled());
|
LuaAssert(L, pp.fifo_enabled());
|
||||||
LuaAssert(L, pp.size() == 3);
|
LuaAssert(L, pp.size() == 3);
|
||||||
ppds.purge();
|
ppds.test_clear_ranges();
|
||||||
pp.serialize(&sb);
|
pp.serialize(&sb);
|
||||||
ppds.deserialize(&sb);
|
ppds.deserialize(&sb);
|
||||||
LuaAssert(L, ppds.fifo_enabled());
|
LuaAssert(L, ppds.fifo_enabled());
|
||||||
@@ -342,5 +441,41 @@ LuaDefine(unittests_idalloc, "c") {
|
|||||||
LuaAssert(L, ppds.get_batch() == nthbatch(2));
|
LuaAssert(L, ppds.get_batch() == nthbatch(2));
|
||||||
LuaAssert(L, ppds.get_batch() == nthbatch(3));
|
LuaAssert(L, ppds.get_batch() == nthbatch(3));
|
||||||
|
|
||||||
|
// Difference transmit compare two empty pools.
|
||||||
|
gp.init_master(3);
|
||||||
|
gpds.init_master(3);
|
||||||
|
pp.test_clear_ranges();
|
||||||
|
ppds.test_clear_ranges();
|
||||||
|
pp.enable_fifo();
|
||||||
|
ppds.enable_fifo();
|
||||||
|
|
||||||
|
// Check case: no differences.
|
||||||
|
sb.clear();
|
||||||
|
LuaAssert(L, !ppds.make_patch(pp, &sb));
|
||||||
|
ppds.apply_patch(&sb);
|
||||||
|
LuaAssert(L, ppds.exactly_equal(pp));
|
||||||
|
|
||||||
|
// Add some values to master pool
|
||||||
|
pp.test_push_back(123);
|
||||||
|
pp.test_push_back(456);
|
||||||
|
|
||||||
|
// transmit and compare. Add extra bytes
|
||||||
|
sb.clear();
|
||||||
|
LuaAssert(L, ppds.make_patch(pp, &sb));
|
||||||
|
sb.write_uint32(0);
|
||||||
|
ppds.apply_patch(&sb);
|
||||||
|
LuaAssert(L, sb.write_count() - sb.read_count() == 4);
|
||||||
|
LuaAssert(L, ppds.exactly_equal(pp));
|
||||||
|
|
||||||
|
// Pop a value from master pool
|
||||||
|
pp.test_pop_front();
|
||||||
|
pp.test_push_back(789);
|
||||||
|
|
||||||
|
// transmit and compare.
|
||||||
|
sb.clear();
|
||||||
|
LuaAssert(L, ppds.make_patch(pp, &sb));
|
||||||
|
ppds.apply_patch(&sb);
|
||||||
|
LuaAssert(L, ppds.exactly_equal(pp));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,9 +153,6 @@ public:
|
|||||||
// Refill the fifo of batches from the global pool.
|
// Refill the fifo of batches from the global pool.
|
||||||
void refill();
|
void refill();
|
||||||
|
|
||||||
// Discard all batches in the fifo. This is only for unit testing.
|
|
||||||
void purge();
|
|
||||||
|
|
||||||
// Get a batch from the fifo. Also refills the fifo.
|
// Get a batch from the fifo. Also refills the fifo.
|
||||||
int64_t get_batch();
|
int64_t get_batch();
|
||||||
|
|
||||||
@@ -171,10 +168,33 @@ public:
|
|||||||
// Return the size of the queue.
|
// Return the size of the queue.
|
||||||
int size() { return ranges_.size(); }
|
int size() { return ranges_.size(); }
|
||||||
|
|
||||||
|
// Return true if the two pools are identical.
|
||||||
|
bool exactly_equal(const IdPlayerPool &other) const;
|
||||||
|
|
||||||
|
// Check that the pool is valid.
|
||||||
|
bool valid() const;
|
||||||
|
|
||||||
|
// unit testing functions.
|
||||||
|
//
|
||||||
|
// These let you manipulate the deque explicitly. But that's
|
||||||
|
// only useful for unit testing.
|
||||||
|
//
|
||||||
|
void test_push_back(int64_t range);
|
||||||
|
void test_pop_front();
|
||||||
|
void test_clear_ranges();
|
||||||
|
|
||||||
// Serialize to or deserialize from a streambuffer.
|
// Serialize to or deserialize from a streambuffer.
|
||||||
// Caution: the pointer to the global pool is not serialized or deserialized.
|
// Caution: the pointer to the global pool is not serialized or deserialized.
|
||||||
void serialize(StreamBuffer *sb);
|
void serialize(StreamBuffer *sb);
|
||||||
void deserialize(StreamBuffer *sb);
|
void deserialize(StreamBuffer *sb);
|
||||||
|
|
||||||
|
// Difference transmission
|
||||||
|
//
|
||||||
|
// When there are no differences, make_patch returns false but
|
||||||
|
// still writes out a valid patch.
|
||||||
|
//
|
||||||
|
bool make_patch(const IdPlayerPool &auth, StreamBuffer *sb) const;
|
||||||
|
void apply_patch(StreamBuffer *sb);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // IDALLOC_HPP
|
#endif // IDALLOC_HPP
|
||||||
|
|||||||
Reference in New Issue
Block a user