#include "printbuffer.hpp" #include #include PrintBuffer::PrintBuffer(util::WorldType wt) { world_type_ = wt; clear(); } static int first_line_len(const char *text, int len) { for (int i = 0; i < len; i++) { if (text[i] == '\n') return i; } return len; } void PrintBuffer::add_line(const char *text, int len) { lines_.emplace_back(text, len); if ((world_type_ == util::WORLD_TYPE_MASTER)||(world_type_ == util::WORLD_TYPE_STANDALONE)) { first_unchecked_ = first_line_ + int(lines_.size()); } } void PrintBuffer::add_string(const char *text, int len) { while (true) { int fll = first_line_len(text, len); if (fll == len) { if (len > 0) { add_line(text, len); } return; } else { add_line(text, fll); text += (fll + 1); len -= (fll + 1); } } } void PrintBuffer::add_string(const std::string &s) { add_string(s.c_str(), s.size()); } void PrintBuffer::discard_upto(int n) { while ((!lines_.empty()) && (first_line_ < n)) { lines_.pop_front(); first_line_ += 1; } if (first_unchecked_ < first_line_) { first_unchecked_ = first_line_; } } void PrintBuffer::clear() { first_line_ = 0; first_unchecked_ = 0; lines_.clear(); } std::string PrintBuffer::debug_string() const { std::ostringstream oss; oss << first_line_ << "," << first_unchecked_ << ":"; for (int i = 0; i < int(lines_.size()); i++) { oss << lines_[i] << ";"; } return oss.str(); } void PrintBuffer::diff(const PrintBuffer &auth, StreamBuffer *sb) const { // Currently, the implementation is simple. The synchronous model discards // all prediction lines from its buffer, then the server retransmits all // those lines. So this barely counts as difference transmission - it's // just transmission, regardless of what was in the synchronous model. I // think that's okay for the text console. // Ask the client to discard anything that precedes auth_first. sb->write_int32(auth.first_line_); sb->write_int32(auth.last_line()); for (int i = first_unchecked_; i < auth.last_line(); i++) { std::string line; if (i >= auth.first_line_) line = auth.nth(i); sb->write_string(line); } } void PrintBuffer::patch(StreamBuffer *sb) { int auth_first = sb->read_int32(); int auth_last = sb->read_int32(); lines_.resize(first_unchecked_ - first_line_); for (int i = first_unchecked_; i < auth_last; i++) { lines_.emplace_back(sb->read_string()); } first_unchecked_ = first_line_ + lines_.size(); discard_upto(auth_first); if (first_line_ < auth_first) { first_line_ = auth_first; } } LuaDefine(unittests_printbuffer, "c") { PrintBuffer pbm(util::WORLD_TYPE_MASTER); PrintBuffer pbs(util::WORLD_TYPE_S_SYNC); StreamBuffer sb; LuaAssertStrEq(L, pbm.debug_string(), "0,0:"); pbm.add_string("foo\nbar\nbaz\n"); LuaAssertStrEq(L, pbm.debug_string(), "0,3:foo;bar;baz;"); pbm.add_string("a\nb\nc"); LuaAssertStrEq(L, pbm.debug_string(), "0,6:foo;bar;baz;a;b;c;"); pbm.discard_upto(2); LuaAssertStrEq(L, pbm.debug_string(), "2,6:baz;a;b;c;"); pbm.discard_upto(8); LuaAssertStrEq(L, pbm.debug_string(), "6,6:"); LuaAssertStrEq(L, pbs.debug_string(), "0,0:"); pbs.add_string("foo\nbar\nbaz\n"); LuaAssertStrEq(L, pbs.debug_string(), "0,0:foo;bar;baz;"); pbs.add_string("a\nb\nc"); LuaAssertStrEq(L, pbs.debug_string(), "0,0:foo;bar;baz;a;b;c;"); pbs.discard_upto(2); LuaAssertStrEq(L, pbs.debug_string(), "2,2:baz;a;b;c;"); pbs.discard_upto(8); LuaAssertStrEq(L, pbs.debug_string(), "6,6:"); pbm.clear(); pbs.clear(); sb.clear(); pbm.add_string("foo\nbar\n"); pbs.diff(pbm, &sb); pbs.patch(&sb); LuaAssertStrEq(L, pbs.debug_string(), "0,2:foo;bar;"); pbm.clear(); pbm.add_string("foo\nyow\nding\ndong\n"); pbs.diff(pbm, &sb); pbs.patch(&sb); LuaAssertStrEq(L, pbs.debug_string(), "0,4:foo;bar;ding;dong;"); pbs.discard_upto(2); LuaAssertStrEq(L, pbs.debug_string(), "2,4:ding;dong;"); pbs.diff(pbm, &sb); pbs.patch(&sb); LuaAssertStrEq(L, pbs.debug_string(), "2,4:ding;dong;"); pbs.add_string("boy\nhowdy\n"); LuaAssertStrEq(L, pbs.debug_string(), "2,4:ding;dong;boy;howdy;"); pbs.diff(pbm, &sb); pbs.patch(&sb); LuaAssertStrEq(L, pbs.debug_string(), "2,4:ding;dong;"); pbs.add_string("boy\nhowdy\nyeah\nbaby\nget\ndown\n"); LuaAssertStrEq(L, pbs.debug_string(), "2,4:ding;dong;boy;howdy;yeah;baby;get;down;"); pbs.discard_upto(5); LuaAssertStrEq(L, pbs.debug_string(), "5,5:howdy;yeah;baby;get;down;"); pbs.diff(pbm, &sb); pbs.patch(&sb); LuaAssertStrEq(L, pbs.debug_string(), "5,5:"); return 0; }