Overhaul encqueue to add a header: size_limit and actual_size
This commit is contained in:
@@ -419,78 +419,110 @@ void AnimCoreState::decode(std::string_view s) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just return the hash of the very last step. (Steps are stored last to first).
|
int AnimQueue::get_size_limit() const {
|
||||||
static uint64_t encqueue_final_hash(std::string_view encqueue) {
|
StreamBuffer sb(*encqueue_);
|
||||||
StreamBuffer sb(encqueue);
|
return sb.read_uint8();
|
||||||
uint64_t hash = sb.read_uint64();
|
|
||||||
return hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the encstep for the final step of the animation queue.
|
int AnimQueue::get_actual_size() const {
|
||||||
static std::string_view encqueue_final_encstep(std::string_view encqueue) {
|
StreamBuffer sb(*encqueue_);
|
||||||
StreamBuffer sb(encqueue);
|
sb.read_bytes(1);
|
||||||
sb.read_uint64();
|
return sb.read_uint8();
|
||||||
std::string_view result = sb.read_string_view();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the encqueue for the first N steps.
|
uint64_t AnimQueue::get_final_hash() const {
|
||||||
// If there aren't that many steps, then just return them all.
|
StreamBuffer sb(*encqueue_);
|
||||||
std::string_view encqueue_finaln(std::string_view encqueue, int n) {
|
sb.read_bytes(2);
|
||||||
StreamBuffer sb(encqueue);
|
return sb.read_uint64();
|
||||||
while ((n > 0) && (!sb.empty())) {
|
}
|
||||||
sb.read_uint64();
|
|
||||||
uint64_t slen = sb.read_length();
|
std::string_view AnimQueue::get_final_encstep() const {
|
||||||
sb.read_bytes(slen);
|
StreamBuffer sb(*encqueue_);
|
||||||
n -= 1;
|
sb.read_bytes(10);
|
||||||
|
return sb.read_string_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimQueue::update_encqueue(int limit, bool add, std::string_view add_enc, bool keepold) {
|
||||||
|
// Make sure the size limit is reasonable.
|
||||||
|
assert((limit >= 2) && (limit <= 250));
|
||||||
|
|
||||||
|
// You must either add a new step or retain an old step. The queue can't be empty.
|
||||||
|
assert(keepold || add);
|
||||||
|
|
||||||
|
// Find out how many old steps we'll be retaining, ignoring the size limit.
|
||||||
|
int nretain = 0;
|
||||||
|
if (keepold) nretain = get_actual_size();
|
||||||
|
|
||||||
|
// If retaining all steps would overflow the size limit, retain fewer.
|
||||||
|
int retain_limit = limit;
|
||||||
|
if (add) retain_limit -= 1;
|
||||||
|
if (nretain > retain_limit) nretain = retain_limit;
|
||||||
|
|
||||||
|
// Calculate the new size of the queue.
|
||||||
|
int new_size = add ? (nretain + 1) : nretain;
|
||||||
|
|
||||||
|
// If we're retaining steps, extract them from the old queue.
|
||||||
|
std::string_view retain;
|
||||||
|
if (nretain > 0) {
|
||||||
|
std::string_view oldqueue(*encqueue_);
|
||||||
|
StreamBuffer sb(oldqueue);
|
||||||
|
sb.read_bytes(2); // Skip over the header.
|
||||||
|
int pos1 = sb.total_reads();
|
||||||
|
for (int i = 0; i < nretain; i++) {
|
||||||
|
sb.read_uint64();
|
||||||
|
sb.read_string_view();
|
||||||
|
}
|
||||||
|
int pos2 = sb.total_reads();
|
||||||
|
retain = oldqueue.substr(pos1, pos2 - pos1);
|
||||||
}
|
}
|
||||||
return encqueue.substr(0, sb.total_reads());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// If we're adding a step, calculate its hash.
|
||||||
|
uint64_t add_hash = 0;
|
||||||
|
if (add) {
|
||||||
|
uint64_t prev_hash = 0;
|
||||||
|
if (nretain > 0) prev_hash = get_final_hash();
|
||||||
|
add_hash = hash_encstep(prev_hash, add_enc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, encode everything into a binary blob.
|
||||||
|
StreamBuffer result;
|
||||||
|
result.write_uint8(limit);
|
||||||
|
result.write_uint8(new_size);
|
||||||
|
if (add) {
|
||||||
|
result.write_uint64(add_hash);
|
||||||
|
result.write_string(add_enc);
|
||||||
|
}
|
||||||
|
result.write_bytes(retain);
|
||||||
|
|
||||||
|
// Replace the shared string.
|
||||||
|
encqueue_ = std::make_shared<std::string>(result.view());
|
||||||
|
}
|
||||||
|
|
||||||
AnimQueue::AnimQueue() {
|
AnimQueue::AnimQueue() {
|
||||||
size_limit_ = 10; // Default size limit.
|
update_encqueue(10, true, AnimState().encode(), false);
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimQueue::clear() {
|
|
||||||
AnimState state;
|
|
||||||
clear(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimQueue::clear(const AnimState &state) {
|
void AnimQueue::clear(const AnimState &state) {
|
||||||
StreamBuffer result;
|
update_encqueue(get_size_limit(), true, state.encode(), false);
|
||||||
eng::string encstep = state.encode();
|
|
||||||
uint64_t hash = hash_encstep(0, encstep);
|
|
||||||
result.write_uint64(hash);
|
|
||||||
result.write_string(encstep);
|
|
||||||
encqueue_ = std::make_shared<std::string>(result.view());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimQueue::set_limit(int nkeep) {
|
void AnimQueue::clear() {
|
||||||
assert((nkeep >= 2) && (nkeep <= 250));
|
update_encqueue(get_size_limit(), true, AnimState().encode(), false);
|
||||||
size_limit_ = nkeep;
|
}
|
||||||
encqueue_ = std::make_shared<std::string>(encqueue_finaln(*encqueue_, nkeep));
|
|
||||||
|
void AnimQueue::set_limit(int limit) {
|
||||||
|
update_encqueue(limit, false, "", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimQueue::add(const AnimState &state) {
|
void AnimQueue::add(const AnimState &state) {
|
||||||
uint64_t previoushash = encqueue_final_hash(*encqueue_);
|
update_encqueue(get_size_limit(), true, state.encode(), true);
|
||||||
eng::string encstep = state.encode();
|
|
||||||
uint64_t hash = hash_encstep(previoushash, encstep);
|
|
||||||
StreamBuffer result;
|
|
||||||
result.write_uint64(hash);
|
|
||||||
result.write_string(encstep);
|
|
||||||
result.write_bytes(encqueue_finaln(*encqueue_, size_limit_ - 1));
|
|
||||||
encqueue_ = std::make_shared<std::string>(result.view());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimQueue::serialize(StreamBuffer *sb) const {
|
void AnimQueue::serialize(StreamBuffer *sb) const {
|
||||||
sb->write_uint8(size_limit_);
|
|
||||||
sb->write_string(*encqueue_);
|
sb->write_string(*encqueue_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimQueue::deserialize(StreamBuffer *sb) {
|
void AnimQueue::deserialize(StreamBuffer *sb) {
|
||||||
size_limit_ = sb->read_uint8();
|
|
||||||
encqueue_ = std::make_shared<std::string>(sb->read_string_view());
|
encqueue_ = std::make_shared<std::string>(sb->read_string_view());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,55 +530,52 @@ bool AnimQueue::diff(const AnimQueue &auth, StreamBuffer *sb) const {
|
|||||||
// Fast check for exactly equivalent. If equivalent, skip all the work.
|
// Fast check for exactly equivalent. If equivalent, skip all the work.
|
||||||
if (exactly_equal_fast(auth)) {
|
if (exactly_equal_fast(auth)) {
|
||||||
assert(exactly_equal(auth));
|
assert(exactly_equal(auth));
|
||||||
sb->write_uint8(255);
|
sb->write_bool(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: maybe send less data?
|
// TODO: maybe send less data?
|
||||||
sb->write_uint8(0);
|
sb->write_bool(true);
|
||||||
sb->write_uint32(auth.size_limit_);
|
|
||||||
sb->write_string(*auth.encqueue_);
|
sb->write_string(*auth.encqueue_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimQueue::patch(StreamBuffer *sb, DebugCollector *dbc) {
|
void AnimQueue::patch(StreamBuffer *sb, DebugCollector *dbc) {
|
||||||
int nsteps = sb->read_uint8();
|
bool changed = sb->read_bool();
|
||||||
if (nsteps == 255) {
|
if (!changed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DebugLine(dbc) << "AnimQueue modified";
|
DebugLine(dbc) << "AnimQueue modified";
|
||||||
|
encqueue_ = std::make_shared<std::string>(sb->read_string_view());
|
||||||
size_limit_ = sb->read_uint32();
|
|
||||||
std::string_view steps = sb->read_string_view();
|
|
||||||
encqueue_ = std::make_shared<std::string>(steps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimQueue::exactly_equal(const AnimQueue &other) const {
|
bool AnimQueue::exactly_equal(const AnimQueue &other) const {
|
||||||
if (size_limit_ != other.size_limit_) return false;
|
|
||||||
if (*encqueue_ != *other.encqueue_) return false;
|
if (*encqueue_ != *other.encqueue_) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimQueue::exactly_equal_fast(const AnimQueue &other) const {
|
bool AnimQueue::exactly_equal_fast(const AnimQueue &other) const {
|
||||||
if (size_limit_ != other.size_limit_) return false;
|
|
||||||
if (encqueue_->size() != other.encqueue_->size()) return false;
|
if (encqueue_->size() != other.encqueue_->size()) return false;
|
||||||
if (encqueue_->compare(0, 8, *other.encqueue_) != 0) return false;
|
if (encqueue_->compare(0, 10, *other.encqueue_) != 0) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimQueue::print_debug_string(eng::ostringstream &oss, bool full) const {
|
void AnimQueue::print_debug_string(eng::ostringstream &oss, bool full) const {
|
||||||
bool first = true;
|
bool first = true;
|
||||||
if (full) {
|
|
||||||
oss << "limit=" << size_limit();
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
// Break out the steps.
|
// Break out the steps.
|
||||||
eng::vector<std::string_view> encsteps;
|
eng::vector<std::string_view> encsteps;
|
||||||
StreamBuffer sb(*encqueue_);
|
StreamBuffer sb(*encqueue_);
|
||||||
while (!sb.empty()) {
|
int size_limit = sb.read_uint8();
|
||||||
|
int actual_size = sb.read_uint8();
|
||||||
|
if (full) {
|
||||||
|
oss << "limit=" << size_limit;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < actual_size; i++) {
|
||||||
sb.read_uint64();
|
sb.read_uint64();
|
||||||
encsteps.push_back(sb.read_string_view());
|
encsteps.push_back(sb.read_string_view());
|
||||||
}
|
}
|
||||||
|
assert(sb.empty());
|
||||||
for (int i = encsteps.size() - 1; i >= 0; i --) {
|
for (int i = encsteps.size() - 1; i >= 0; i --) {
|
||||||
if (!first) oss << "; ";
|
if (!first) oss << "; ";
|
||||||
AnimState state(encsteps[i]);
|
AnimState state(encsteps[i]);
|
||||||
@@ -568,21 +597,21 @@ eng::string AnimQueue::full_debug_string() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AnimCoreState AnimQueue::get_final_core_state() const {
|
AnimCoreState AnimQueue::get_final_core_state() const {
|
||||||
std::string_view encstep = encqueue_final_encstep(*encqueue_);
|
std::string_view encstep = get_final_encstep();
|
||||||
AnimCoreState result;
|
AnimCoreState result;
|
||||||
result.decode(encstep);
|
result.decode(encstep);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimState AnimQueue::get_final_persistent() const {
|
AnimState AnimQueue::get_final_persistent() const {
|
||||||
std::string_view encstep = encqueue_final_encstep(*encqueue_);
|
std::string_view encstep = get_final_encstep();
|
||||||
AnimState result;
|
AnimState result;
|
||||||
result.decode_persistent(encstep);
|
result.decode_persistent(encstep);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimState AnimQueue::get_final_everything() const {
|
AnimState AnimQueue::get_final_everything() const {
|
||||||
std::string_view encstep = encqueue_final_encstep(*encqueue_);
|
std::string_view encstep = get_final_encstep();
|
||||||
AnimState result;
|
AnimState result;
|
||||||
result.decode(encstep);
|
result.decode(encstep);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -64,16 +64,19 @@
|
|||||||
// value, which is a function that accepts the encstep and also the hash
|
// value, which is a function that accepts the encstep and also the hash
|
||||||
// of the previous encstep. Note that the hash is not part of the encstep.
|
// of the previous encstep. Note that the hash is not part of the encstep.
|
||||||
//
|
//
|
||||||
// An animation queue consists of a list of steps. Each step has a hash
|
// A serialized animation queue consists of the following information:
|
||||||
// and an encstep. An animation queue is serialized as follows:
|
|
||||||
//
|
//
|
||||||
// for all animation steps, starting with the most recent, do:
|
// write_uint8(size_limit);
|
||||||
// write_uint64(hash)
|
// write_uint8(actual_size);
|
||||||
// write_string(encstep)
|
// for all animation steps, starting with the most recent, do:
|
||||||
|
// write_uint64(hash)
|
||||||
|
// write_string(encstep)
|
||||||
//
|
//
|
||||||
// The encoded string produced by the loop above is called an "encqueue",
|
// The encoded string produced by the loop above is called an "encqueue",
|
||||||
// because it encodes everything in the animation queue (except for the
|
// because it encodes everything in the animation queue.
|
||||||
// size limit, which is separate).
|
//
|
||||||
|
// Note that the 'serialize' routine for animation queues just returns
|
||||||
|
// the encqueue string, which is the whole thing.
|
||||||
//
|
//
|
||||||
// Since the steps in an encqueue are stored most-recent first, if you
|
// Since the steps in an encqueue are stored most-recent first, if you
|
||||||
// want some information about the most recent animation entry, you
|
// want some information about the most recent animation entry, you
|
||||||
@@ -249,18 +252,14 @@ public:
|
|||||||
//
|
//
|
||||||
AnimQueue();
|
AnimQueue();
|
||||||
|
|
||||||
// Size limit.
|
// Clear the steps to an initial state.
|
||||||
//
|
|
||||||
int32_t size_limit() const { return size_limit_; }
|
|
||||||
|
|
||||||
// Clear and set the initial state.
|
|
||||||
//
|
//
|
||||||
void clear();
|
void clear();
|
||||||
void clear(const AnimState &initial);
|
void clear(const AnimState &initial);
|
||||||
|
|
||||||
// Set the size limit. Must be 2-250
|
// Change the size limit.
|
||||||
//
|
//
|
||||||
void set_limit(int n);
|
void set_limit(int limit);
|
||||||
|
|
||||||
// Add an animation step.
|
// Add an animation step.
|
||||||
//
|
//
|
||||||
@@ -317,8 +316,22 @@ public:
|
|||||||
util::SharedStdString get_encoded_queue() const { return encqueue_; }
|
util::SharedStdString get_encoded_queue() const { return encqueue_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int size_limit_;
|
// Update the encoded queue.
|
||||||
|
//
|
||||||
|
// You must specify the new size limit.
|
||||||
|
// You may optionally specify an encstep to add.
|
||||||
|
// If keepold, then old steps will be retained up to the size limit.
|
||||||
|
//
|
||||||
|
void update_encqueue(int limit, bool add, std::string_view add_enc, bool keepold);
|
||||||
|
|
||||||
|
// Read values from the header of the encqueue.
|
||||||
|
//
|
||||||
|
int get_size_limit() const;
|
||||||
|
int get_actual_size() const;
|
||||||
|
uint64_t get_final_hash() const;
|
||||||
|
std::string_view get_final_encstep() const;
|
||||||
|
|
||||||
|
private:
|
||||||
// Note: this is stored as a std::string, not an eng::string, because the
|
// Note: this is stored as a std::string, not an eng::string, because the
|
||||||
// ownership ends up being shared between us and the graphics engine. We
|
// ownership ends up being shared between us and the graphics engine. We
|
||||||
// can't have the graphics engine affecting the behavior of the engine heap.
|
// can't have the graphics engine affecting the behavior of the engine heap.
|
||||||
|
|||||||
Reference in New Issue
Block a user