diff --git a/core/admin.c b/core/admin.c
index 3e5ddfe721ea3df6c0cde37f5b931c317d3dfd74..5f4e196e0600a8920120f962c0cc7f897d9f62b7 100644
--- a/core/admin.c
+++ b/core/admin.c
@@ -1452,8 +1452,7 @@ case 107:
 #line 199 "core/admin.rl"
 
 
-	fiber->rbuf->len -= (void *)pe - (void *)fiber->rbuf->data;
-	fiber->rbuf->data = pe;
+	tbuf_ltrim(fiber->rbuf, (void *)pe - (void *)fiber->rbuf->data);
 
 	if (p != pe) {
 		start(out);
diff --git a/core/admin.rl b/core/admin.rl
index bed4f22a8df8fc99bdb130506c07c2bca68547d3..5352e2b5b26d4715405595a9631e7da413a1dcbe 100644
--- a/core/admin.rl
+++ b/core/admin.rl
@@ -198,8 +198,7 @@ admin_dispatch(void)
 		write exec;
 	}%%
 
-	fiber->rbuf->len -= (void *)pe - (void *)fiber->rbuf->data;
-	fiber->rbuf->data = pe;
+	tbuf_ltrim(fiber->rbuf, (void *)pe - (void *)fiber->rbuf->data);
 
 	if (p != pe) {
 		start(out);
diff --git a/core/tbuf.c b/core/tbuf.c
index 52a2dc48da1b59617995feb3e6f41ec2d16618bb..bb9fb1369641ce0a1c182e186f0cb07faf9ad600 100644
--- a/core/tbuf.c
+++ b/core/tbuf.c
@@ -124,6 +124,19 @@ tbuf_peek(struct tbuf *b, size_t count)
 	return NULL;
 }
 
+void *
+tbuf_ltrim(struct tbuf *b, size_t count)
+{
+	void *p = NULL;
+
+	tbuf_assert(b);
+	if (count <= b->len) {
+		p = memmove(b->data, b->data + count, b->len - count);
+		b->len -= count;
+	}
+	return p;
+}
+
 size_t
 tbuf_reserve(struct tbuf *b, size_t count)
 {
diff --git a/include/tbuf.h b/include/tbuf.h
index 49c72f301e46ba6eccd7e50fdd92af89efd87ccc..e45d44fc094c24c685825d5085f4015c80fdca94 100644
--- a/include/tbuf.h
+++ b/include/tbuf.h
@@ -63,6 +63,7 @@ struct tbuf *tbuf_split(struct tbuf *e, size_t at);
 size_t tbuf_reserve(struct tbuf *b, size_t count);
 void tbuf_reset(struct tbuf *b);
 void *tbuf_peek(struct tbuf *b, size_t count);
+void *tbuf_ltrim(struct tbuf *b, size_t count);
 
 void tbuf_append_field(struct tbuf *b, void *f);
 void tbuf_vprintf(struct tbuf *b, const char *format, va_list ap)