Add support for animate replace=true

This commit is contained in:
2023-10-03 18:17:24 -04:00
parent c1594a1d83
commit edea43839f
3 changed files with 187 additions and 122 deletions

View File

@@ -21,7 +21,7 @@ static const char *vtname(AnimValueType vt) {
}
uint64_t hash_encstep(uint64_t prev, std::string_view s) {
static uint64_t hash_encstep(uint64_t prev, std::string_view s) {
return util::hash_string(util::HashValue(123, prev), s).first;
}
@@ -420,102 +420,114 @@ void AnimCoreState::decode(std::string_view s) {
}
int AnimQueue::get_size_limit() const {
if (encqueue_ == nullptr) return 0;
StreamBuffer sb(*encqueue_);
return sb.read_uint8();
}
int AnimQueue::get_actual_size() const {
if (encqueue_ == nullptr) return 0;
StreamBuffer sb(*encqueue_);
sb.read_bytes(1);
return sb.read_uint8();
}
uint64_t AnimQueue::get_final_hash() const {
if (encqueue_ == nullptr) return 0;
StreamBuffer sb(*encqueue_);
sb.read_bytes(2);
return sb.read_uint64();
}
std::string_view AnimQueue::get_final_encstep() const {
if (encqueue_ == nullptr) return std::string_view();
StreamBuffer sb(*encqueue_);
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));
AnimQueue::QueueRange AnimQueue::get_range(int lo, int hi) {
// Clamp lo and hi to the valid range (0 to actual_size).
//
int actual_size = get_actual_size();
if (lo < 0) lo = 0;
if (hi > actual_size) hi = actual_size;
// You must either add a new step or retain an old step. The queue can't be empty.
assert(keepold || add);
// Abort early if the range is empty. This avoids several edge cases.
//
if (lo >= hi) return QueueRange(0, std::string_view());
// 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);
// Get the entries.
//
std::string_view queueview(*encqueue_);
StreamBuffer sb(queueview);
sb.read_bytes(2); // Skip over the header.
for (int i = 0; i < lo; i++) {
sb.read_uint64();
sb.read_string_view();
}
// 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);
int pos1 = sb.total_reads();
for (int i = lo; i < hi; i++) {
sb.read_uint64();
sb.read_string_view();
}
int pos2 = sb.total_reads();
return QueueRange(hi-lo, queueview.substr(pos1, pos2 - pos1));
}
// Finally, encode everything into a binary blob.
uint64_t AnimQueue::hash_encstep(const QueueRange &prev, std::string_view s) {
uint64_t prev_hash = 0;
if (prev.size > 0) {
StreamBuffer retsb(prev.entries);
prev_hash = retsb.read_uint64();
}
return ::hash_encstep(prev_hash, s);
}
void AnimQueue::update_encqueue(int limit, bool add, std::string_view add_enc, int keeplo, int keephi) {
// Get the retained entries.
QueueRange keeprange = get_range(keeplo, keephi);
// Encode everything into a binary blob.
StreamBuffer result;
result.write_uint8(limit);
result.write_uint8(new_size);
result.write_uint8(keeprange.size + (add ? 1:0));
if (add) {
uint64_t add_hash = hash_encstep(keeprange, add_enc);
result.write_uint64(add_hash);
result.write_string(add_enc);
}
result.write_bytes(retain);
result.write_bytes(keeprange.entries);
// Replace the shared string.
encqueue_ = std::make_shared<std::string>(result.view());
}
AnimQueue::AnimQueue() {
update_encqueue(10, true, AnimState().encode(), false);
update_encqueue(10, true, AnimState().encode(), 0, 0);
}
void AnimQueue::clear(const AnimState &state) {
update_encqueue(get_size_limit(), true, state.encode(), false);
update_encqueue(get_size_limit(), true, state.encode(), 0, 0);
}
void AnimQueue::clear() {
update_encqueue(get_size_limit(), true, AnimState().encode(), false);
update_encqueue(get_size_limit(), true, AnimState().encode(), 0, 0);
}
void AnimQueue::set_limit(int limit) {
update_encqueue(limit, false, "", true);
assert((limit >= 2) && (limit <= 250));
update_encqueue(limit, false, std::string_view(), 0, limit);
}
void AnimQueue::add(const AnimState &state) {
update_encqueue(get_size_limit(), true, state.encode(), true);
int limit = get_size_limit();
update_encqueue(limit, true, state.encode(), 0, limit - 1);
}
void AnimQueue::replace(const AnimState &state) {
int limit = get_size_limit();
update_encqueue(limit, true, state.encode(), 1, limit);
}
void AnimQueue::serialize(StreamBuffer *sb) const {