From 908fe4190de8e7a6539a75b5019104e7930ba4bb Mon Sep 17 00:00:00 2001
From: Roman Tsisyk <roman@tsisyk.com>
Date: Fri, 14 Jun 2013 17:00:25 +0400
Subject: [PATCH] Replace direct access to tuple->data in obvious cases

---
 cmake/compiler.cmake     |   1 +
 src/box/bitset_index.cc  |   9 +-
 src/box/box_lua.cc       |  91 +++----
 src/box/hash_index.cc    |   8 +-
 src/box/space.cc         |  10 +-
 src/box/tuple.cc         |  89 ++++++-
 src/box/tuple.h          |  72 +++++-
 src/memcached-grammar.cc | 521 +++++++++++++++++++--------------------
 src/memcached-grammar.rl |  23 +-
 src/memcached.cc         |  34 +--
 test/box/lua.result      |   2 +-
 11 files changed, 488 insertions(+), 372 deletions(-)

diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake
index 4dda027c46..bb1c13c4c8 100644
--- a/cmake/compiler.cmake
+++ b/cmake/compiler.cmake
@@ -99,6 +99,7 @@ macro(enable_tnt_compile_flags)
         "-Wextra"
         "-Wno-sign-compare"
         "-Wno-strict-aliasing"
+        "-Wno-error=deprecated-declarations"
     )
 
     if (CMAKE_COMPILER_IS_GNUCXX)
diff --git a/src/box/bitset_index.cc b/src/box/bitset_index.cc
index 673dc5f2a4..ec02153900 100644
--- a/src/box/bitset_index.cc
+++ b/src/box/bitset_index.cc
@@ -219,11 +219,10 @@ BitsetIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple,
 	}
 
 	if (new_tuple != NULL) {
-		const char *field = tuple_field(new_tuple,
-						key_def->parts[0].fieldno);
-		assert (field != NULL);
-		size_t bitset_key_size = (size_t) load_varint32(&field);
-		const char *bitset_key = field;
+		const char *fb, *fe;
+		tuple_field(new_tuple, key_def->parts[0].fieldno, &fb, &fe);
+		size_t bitset_key_size = fe - fb;
+		const char *bitset_key = fb;
 
 		size_t value = tuple_to_value(new_tuple);
 		if (bitset_index_insert(&index, bitset_key,
diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc
index 716478f74b..6791bdec12 100644
--- a/src/box/box_lua.cc
+++ b/src/box/box_lua.cc
@@ -164,19 +164,17 @@ lbox_tuple_slice(struct lua_State *L)
 	if (end <= start)
 		luaL_error(L, "tuple.slice(): start must be less than end");
 
-	const char *field = tuple->data;
-	u32 fieldno = 0;
 	u32 stop = end - 1;
 
-	while (field < tuple->data + tuple->bsize) {
-		size_t len = load_varint32(&field);
-		if (fieldno >= start) {
-			lua_pushlstring(L, field, len);
-			if (fieldno == stop)
+	struct tuple_iterator it;
+	const char *fb, *fe;
+	tuple_seek(&it, tuple, 0, &fb, &fe);
+	for (uint32_t field_no = 0; tuple_next(&it); field_no++) {
+		if (field_no >= start) {
+			lua_pushlstring(L, fb, fe - fb);
+			if (field_no == stop)
 				break;
 		}
-		field += len;
-		fieldno += 1;
 	}
 	return end - start;
 }
@@ -306,7 +304,8 @@ lbox_tuple_transform(struct lua_State *L)
 			break;
 		}
 	}
-	memcpy(ptr, tuple_field(tuple, offset + len), lr[1]);
+
+	memcpy(ptr, tuple_field_old(tuple, offset + len), lr[1]);
 
 	lbox_pushtuple(L, dest);
 	return 1;
@@ -330,15 +329,17 @@ tuple_find(struct lua_State *L, struct tuple *tuple, size_t offset,
 	int idx = offset;
 	if (idx >= tuple->field_count)
 		return 0;
-	const char *field = tuple_field(tuple, idx);
-	while (field < tuple->data + tuple->bsize) {
-		size_t len = load_varint32(&field);
-		if (len == key_size && (memcmp(field, key, len) == 0)) {
+
+	struct tuple_iterator it;
+	const char *fb, *fe;
+	tuple_seek(&it, tuple, idx, &fb, &fe);
+	while (tuple_next(&it)) {
+		uint32_t len = fe - fb;
+		if (len == key_size && (memcmp(fb, key, len) == 0)) {
 			lua_pushinteger(L, idx);
 			if (!all)
 				break;
 		}
-		field += len;
 		idx++;
 	}
 	return lua_gettop(L) - top;
@@ -398,12 +399,12 @@ static int
 lbox_tuple_unpack(struct lua_State *L)
 {
 	struct tuple *tuple = lua_checktuple(L, 1);
-	const char *field = tuple->data;
 
-	while (field < tuple->data + tuple->bsize) {
-		size_t len = load_varint32(&field);
-		lua_pushlstring(L, field, len);
-		field = field + len;
+	struct tuple_iterator it;
+	const char *fb, *fe;
+	tuple_seek(&it, tuple, 0, &fb, &fe);
+	while (tuple_next(&it)) {
+		lua_pushlstring(L, fb, fe - fb);
 	}
 	assert(lua_gettop(L) == tuple->field_count + 1);
 	return tuple->field_count;
@@ -415,13 +416,14 @@ lbox_tuple_totable(struct lua_State *L)
 	struct tuple *tuple = lua_checktuple(L, 1);
 	lua_newtable(L);
 	int index = 1;
-	const char *field = tuple->data;
-	while (field < tuple->data + tuple->bsize) {
-		size_t len = load_varint32(&field);
+
+	struct tuple_iterator it;
+	const char *fb, *fe;
+	tuple_seek(&it, tuple, 0, &fb, &fe);
+	while (tuple_next(&it)) {
 		lua_pushnumber(L, index++);
-		lua_pushlstring(L, field, len);
+		lua_pushlstring(L, fb, fe - fb);
 		lua_rawset(L, -3);
-		field += len;
 	}
 	return 1;
 }
@@ -443,9 +445,9 @@ lbox_tuple_index(struct lua_State *L)
 		if (i >= tuple->field_count)
 			luaL_error(L, "%s: index %d is out of bounds (0..%d)",
 				   tuplelib_name, i, tuple->field_count-1);
-		const char *field = tuple_field(tuple, i);
-		u32 len = load_varint32(&field);
-		lua_pushlstring(L, field, len);
+		const char *fb, *fe;
+		tuple_field(tuple, i, &fb, &fe);
+		lua_pushlstring(L, fb, fe - fb);
 		return 1;
 	}
 	/* If we got a string, try to find a method for it. */
@@ -461,7 +463,7 @@ lbox_tuple_tostring(struct lua_State *L)
 	struct tuple *tuple = lua_checktuple(L, 1);
 	/* @todo: print the tuple */
 	struct tbuf *tbuf = tbuf_new(fiber->gc_pool);
-	tuple_print(tbuf, tuple->field_count, tuple->data);
+	tuple_print(tbuf, tuple);
 	lua_pushlstring(L, tbuf->data, tbuf->size);
 	return 1;
 }
@@ -491,26 +493,25 @@ lbox_tuple_next(struct lua_State *L)
 {
 	struct tuple *tuple = lua_checktuple(L, 1);
 	int argc = lua_gettop(L) - 1;
-	const char *field = NULL;
-	size_t len;
 
+	u32 field_no;
 	if (argc == 0 || (argc == 1 && lua_type(L, 2) == LUA_TNIL))
-		field = tuple->data;
-	else if (argc == 1 && lua_islightuserdata(L, 2))
-		field = (char *) lua_touserdata(L, 2);
+		field_no = 0;
+	else if (argc == 1 && lua_type(L, 2) == LUA_TNUMBER)
+		field_no = lua_tointeger(L, 2);
 	else
-		luaL_error(L, "tuple.next(): bad arguments");
-
-	(void)field;
-	assert(field >= tuple->data);
-	if (field < tuple->data + tuple->bsize) {
-		len = load_varint32(&field);
-		lua_pushlightuserdata(L, (void *) (field + len));
-		lua_pushlstring(L, field, len);
-		return 2;
+		return luaL_error(L, "tuple.next(): bad arguments");
+
+	if (field_no >= tuple->field_count) {
+		lua_pushnil(L);
+		return 1;
 	}
-	lua_pushnil(L);
-	return  1;
+
+	const char *fb, *fe;
+	tuple_field(tuple, field_no, &fb, &fe);
+	lua_pushinteger(L, field_no + 1);
+	lua_pushlstring(L, fb, fe - fb);
+	return 2;
 }
 
 /** Iterator over tuple fields. Adapt lbox_tuple_next
diff --git a/src/box/hash_index.cc b/src/box/hash_index.cc
index a18d4d99fc..045e745d28 100644
--- a/src/box/hash_index.cc
+++ b/src/box/hash_index.cc
@@ -319,7 +319,7 @@ HashIndex::findByTuple(struct tuple *tuple) const
 		tnt_raise(IllegalParams, "tuple must have all indexed fields");
 
 	/* Hash index currently is always single-part. */
-	const char *field = tuple_field(tuple, key_def->parts[0].fieldno);
+	const char *field = tuple_field_old(tuple, key_def->parts[0].fieldno);
 	return findByKey(field, 1);
 }
 
@@ -340,7 +340,7 @@ int32_key_to_node(const char *key)
 static inline struct mh_i32ptr_node_t
 int32_tuple_to_node(struct tuple *tuple, struct key_def *key_def)
 {
-	const char *field = tuple_field(tuple, key_def->parts[0].fieldno);
+	const char *field = tuple_field_old(tuple, key_def->parts[0].fieldno);
 	struct mh_i32ptr_node_t node = int32_key_to_node(field);
 	node.val = tuple;
 	return node;
@@ -517,7 +517,7 @@ int64_key_to_node(const char *key)
 static inline struct mh_i64ptr_node_t
 int64_tuple_to_node(struct tuple *tuple, struct key_def *key_def)
 {
-	const char *field = tuple_field(tuple, key_def->parts[0].fieldno);
+	const char *field = tuple_field_old(tuple, key_def->parts[0].fieldno);
 	struct mh_i64ptr_node_t node = int64_key_to_node(field);
 	node.val = tuple;
 	return node;
@@ -685,7 +685,7 @@ Hash64Index::initIterator(struct iterator *ptr, enum iterator_type type,
 static inline struct mh_lstrptr_node_t
 lstrptr_tuple_to_node(struct tuple *tuple, struct key_def *key_def)
 {
-	const char *field = tuple_field(tuple, key_def->parts[0].fieldno);
+	const char *field = tuple_field_old(tuple, key_def->parts[0].fieldno);
 	if (field == NULL)
 		tnt_raise(ClientError, ER_NO_SUCH_FIELD,
 			  key_def->parts[0].fieldno);
diff --git a/src/box/space.cc b/src/box/space.cc
index 16a91536e3..58e8a2d86e 100644
--- a/src/box/space.cc
+++ b/src/box/space.cc
@@ -175,15 +175,15 @@ space_validate_tuple(struct space *sp, struct tuple *new_tuple)
 			  "tuple field count must match space cardinality");
 
 	/* Sweep through the tuple and check the field sizes. */
-	const char *data = new_tuple->data;
-	for (u32 f = 0; f < sp->max_fieldno; ++f) {
-		/* Get the size of the current field and advance. */
-		u32 len = load_varint32(&data);
-		data += len;
+	struct tuple_iterator it;
+	const char *fb, *fe;
+	tuple_seek(&it, new_tuple, 0, &fb, &fe);
+	for (uint32_t f = 0; f < sp->max_fieldno && tuple_next(&it); f++) {
 		/*
 		 * Check fixed size fields (NUM and NUM64) and
 		 * skip undefined size fields (STRING and UNKNOWN).
 		 */
+		uint32_t len = fe - fb;
 		if (sp->field_types[f] == NUM) {
 			if (len != sizeof(u32))
 				tnt_raise(ClientError, ER_KEY_FIELD_TYPE,
diff --git a/src/box/tuple.cc b/src/box/tuple.cc
index fe6c65fbe1..c0d8506a00 100644
--- a/src/box/tuple.cc
+++ b/src/box/tuple.cc
@@ -89,7 +89,7 @@ next_field(const char *f)
  * @returns field data if field exists or NULL
  */
 const char *
-tuple_field(struct tuple *tuple, u32 i)
+tuple_field_old(struct tuple *tuple, u32 i)
 {
 	const char *field = tuple->data;
 
@@ -102,11 +102,73 @@ tuple_field(struct tuple *tuple, u32 i)
 	return field;
 }
 
+void
+tuple_field(const struct tuple *tuple, uint32_t field_no,
+	    const char **begin, const char **end)
+{
+	const char *data = tuple->data;
+
+	if (field_no >= tuple->field_count)
+		tnt_raise(IllegalParams, "field_no is out of range");
+
+	while (field_no-- > 0)
+		data = next_field(data);
+
+	uint32_t len = load_varint32(&data);
+	*begin = data;
+	*end = data + len;
+}
+
+void
+tuple_seek(struct tuple_iterator *it, const struct tuple *tuple,
+	   uint32_t field_no, const char **begin, const char **end)
+{
+	const char *data = tuple->data;
+
+	if (field_no >= tuple->field_count)
+		tnt_raise(IllegalParams, "field_no is out of range");
+
+	while (field_no-- > 0)
+		data = next_field(data);
+
+	it->tuple = tuple;
+	it->cur = data;
+	it->begin = begin;
+	it->end = end;
+	*it->begin = NULL;
+	*it->end = NULL;
+}
+
+struct tuple_iterator *
+tuple_next(struct tuple_iterator *it)
+{
+	if (it->cur == it->tuple->data + it->tuple->bsize) {
+		/* No more fields in the tuple*/
+		it->cur = NULL;
+		return NULL;
+	} else if (it->cur == NULL) {
+		/* Sanity check: second call to next() is invalid */
+		tnt_raise(IllegalParams, "field_no is out of range");
+	}
+
+	uint32_t len = load_varint32(&it->cur);
+	*it->begin = it->cur;
+	*it->end = it->cur + len;
+	it->cur += len;
+
+	if (it->cur <= it->tuple->data + it->tuple->bsize) {
+		return it;
+	} else /* it->cur > it->tuple->data + it->tuple->bsize */ {
+		tnt_raise(IllegalParams, "invalid tuple");
+	}
+}
+
+
 /** print field to tbuf */
 static void
-print_field(struct tbuf *buf, const char *f)
+print_field(struct tbuf *buf, const char *f, const char *end)
 {
-	uint32_t size = load_varint32(&f);
+	uint32_t size = (end - f);
 	switch (size) {
 	case 2:
 		tbuf_printf(buf, "%hu", *(u16 *)f);
@@ -137,21 +199,28 @@ print_field(struct tbuf *buf, const char *f)
  * key: { value, value, value }
  */
 void
-tuple_print(struct tbuf *buf, u32 field_count, const char *f)
+tuple_print(struct tbuf *buf, const struct tuple *tuple)
 {
-	if (field_count == 0) {
+	if (tuple->field_count == 0) {
 		tbuf_printf(buf, "'': {}");
 		return;
 	}
 
-	print_field(buf, f);
+	struct tuple_iterator it;
+	const char *fb, *fe;
+	tuple_seek(&it, tuple, 0, &fb, &fe);
+	tuple_next(&it);
+	print_field(buf, fb, fe);
 	tbuf_printf(buf, ": {");
-	f = next_field(f);
 
-	for (u32 i = 1; i < field_count; i++, f = next_field(f)) {
-		print_field(buf, f);
-		if (likely(i + 1 < field_count))
+	uint32_t field_no = 1;
+	for (; tuple_next(&it); field_no++) {
+		print_field(buf, fb, fe);
+		if (likely(field_no + 1 < tuple->field_count)) {
 			tbuf_printf(buf, ", ");
+		}
 	}
+	assert (field_no == tuple->field_count);
+
 	tbuf_printf(buf, "}");
 }
diff --git a/src/box/tuple.h b/src/box/tuple.h
index cf55551ef8..e2d67798a5 100644
--- a/src/box/tuple.h
+++ b/src/box/tuple.h
@@ -76,15 +76,79 @@ tuple_ref(struct tuple *tuple, int count);
  *
  * @returns field data if the field exists, or NULL
  */
-const char *
-tuple_field(struct tuple *tuple, u32 i);
+__attribute__((deprecated)) const char *
+tuple_field_old(struct tuple *tuple, u32 i);
 
 /**
- * Print a tuple in yaml-compatible mode to tbuf:
+ * @brief Return field data of the field
+ * @param tuple tuple
+ * @param field_no field number
+ * @param begin pointer where the start of field data will be stored
+ * @param end pointer where the end of field data + 1 will be stored
+ * @throws IllegalParams if \a field_no is out of range
+ */
+void
+tuple_field(const struct tuple *tuple, uint32_t field_no,
+	    const char **begin, const char **end);
+
+/**
+ * @brief Tuple Interator
+ */
+struct tuple_iterator {
+	/** @cond false **/
+	/* Result */
+	const char **begin;
+	const char **end;
+	/* State */
+	const struct tuple *tuple;
+	const char *cur;
+	/** @endcond **/
+};
+
+/**
+ * @brief Seek tuple iterator to position \a field_no
+ *
+ * A workflow example:
+ * @code
+ * struct tuple_iterator it;
+ * const char *fb, *fe;
+ * tuple_seek(&it, tuple, 0, &fb, &fe);
+ * while (tuple_next(&it)) {
+ *      // field_data = fb
+ *	// field_size = fe-fb
+ *	lua_pushlstring(L, fb, fe - fb);
+ * }
+ * @endcode
+ *
+ * @param it tuple iterator
+ * @param tuple tuple
+ * @param field_no a field number to seek
+ * @param begin pointer where the start of field data will be stored
+ * @param end pointer where the end of field data + 1 will be stored
+ */
+void
+tuple_seek(struct tuple_iterator *it, const struct tuple *tuple,
+	   uint32_t field_no, const char **begin, const char **end);
+
+/**
+ * @brief Iterate to the next position
+ * @param it tuple iterator
+ * @retval \a it if the iterator has tuple
+ * @retval NULL if where is no more tuples (values of \a begin \a end
+ * are not specifed in this case)
+ */
+struct tuple_iterator *
+tuple_next(struct tuple_iterator *it);
+
+/**
+ * @brief Print a tuple in yaml-compatible mode to tbuf:
  * key: { value, value, value }
+ *
+ * @param buf tbuf
+ * @param tuple tuple
  */
 void
-tuple_print(struct tbuf *buf, u32 field_count, const char *f);
+tuple_print(struct tbuf *buf, const struct tuple *tuple);
 
 /** Tuple length when adding to iov. */
 static inline size_t tuple_len(struct tuple *tuple)
diff --git a/src/memcached-grammar.cc b/src/memcached-grammar.cc
index b526f6147f..8f8515f4c2 100644
--- a/src/memcached-grammar.cc
+++ b/src/memcached-grammar.cc
@@ -146,7 +146,7 @@ case 5:
 		goto st0;
 	goto tr15;
 tr15:
-#line 224 "src/memcached-grammar.rl"
+#line 219 "src/memcached-grammar.rl"
 	{
 			fstart = p;
 			for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++);
@@ -176,7 +176,7 @@ case 7:
 		goto tr17;
 	goto st0;
 tr17:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st8;
 st8:
@@ -190,7 +190,7 @@ case 8:
 		goto st8;
 	goto st0;
 tr18:
-#line 246 "src/memcached-grammar.rl"
+#line 241 "src/memcached-grammar.rl"
 	{flags = memcached_natoq(fstart, p);}
 	goto st9;
 st9:
@@ -204,7 +204,7 @@ case 9:
 		goto tr21;
 	goto st0;
 tr21:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st10;
 st10:
@@ -218,7 +218,7 @@ case 10:
 		goto st10;
 	goto st0;
 tr22:
-#line 239 "src/memcached-grammar.rl"
+#line 234 "src/memcached-grammar.rl"
 	{
 			exptime = memcached_natoq(fstart, p);
 			if (exptime > 0 && exptime <= 60*60*24*30)
@@ -236,7 +236,7 @@ case 11:
 		goto tr25;
 	goto st0;
 tr25:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st12;
 st12:
@@ -253,11 +253,11 @@ case 12:
 		goto st12;
 	goto st0;
 tr26:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -280,7 +280,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -297,9 +297,9 @@ case 12:
 		}
 	goto st197;
 tr30:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -322,7 +322,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -339,11 +339,11 @@ case 12:
 		}
 	goto st197;
 tr39:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -366,7 +366,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -383,11 +383,11 @@ case 12:
 		}
 	goto st197;
 tr58:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -410,7 +410,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -419,35 +419,33 @@ case 12:
 #line 97 "src/memcached-grammar.rl"
 	{
 			struct tbuf *b;
-			const char *value;
-			u32 value_len;
+			const char *fb, *fe;
 
 			key = tbuf_read_field(keys);
 			struct tuple *tuple = memcached_find(key);
 			if (tuple == NULL) {
 				obuf_dup(out, "NOT_STORED\r\n", 12);
 			} else {
-				value = tuple_field(tuple, 3);
-				value_len = load_varint32(&value);
+				tuple_field(tuple, 3, &fb, &fe);
 				b = tbuf_new(fiber->gc_pool);
 				if (append) {
-					tbuf_append(b, value, value_len);
+					tbuf_append(b, fb, fe - fb);
 					tbuf_append(b, data, bytes);
 				} else {
 					tbuf_append(b, data, bytes);
-					tbuf_append(b, value, value_len);
+					tbuf_append(b, fb, fe - fb);
 				}
 
-				bytes += value_len;
+				bytes += (fe - fb);
 				data = b->data;
 				STORE;
 			}
 		}
 	goto st197;
 tr62:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -470,7 +468,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -479,37 +477,35 @@ case 12:
 #line 97 "src/memcached-grammar.rl"
 	{
 			struct tbuf *b;
-			const char *value;
-			u32 value_len;
+			const char *fb, *fe;
 
 			key = tbuf_read_field(keys);
 			struct tuple *tuple = memcached_find(key);
 			if (tuple == NULL) {
 				obuf_dup(out, "NOT_STORED\r\n", 12);
 			} else {
-				value = tuple_field(tuple, 3);
-				value_len = load_varint32(&value);
+				tuple_field(tuple, 3, &fb, &fe);
 				b = tbuf_new(fiber->gc_pool);
 				if (append) {
-					tbuf_append(b, value, value_len);
+					tbuf_append(b, fb, fe - fb);
 					tbuf_append(b, data, bytes);
 				} else {
 					tbuf_append(b, data, bytes);
-					tbuf_append(b, value, value_len);
+					tbuf_append(b, fb, fe - fb);
 				}
 
-				bytes += value_len;
+				bytes += (fe - fb);
 				data = b->data;
 				STORE;
 			}
 		}
 	goto st197;
 tr71:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -532,7 +528,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -541,37 +537,35 @@ case 12:
 #line 97 "src/memcached-grammar.rl"
 	{
 			struct tbuf *b;
-			const char *value;
-			u32 value_len;
+			const char *fb, *fe;
 
 			key = tbuf_read_field(keys);
 			struct tuple *tuple = memcached_find(key);
 			if (tuple == NULL) {
 				obuf_dup(out, "NOT_STORED\r\n", 12);
 			} else {
-				value = tuple_field(tuple, 3);
-				value_len = load_varint32(&value);
+				tuple_field(tuple, 3, &fb, &fe);
 				b = tbuf_new(fiber->gc_pool);
 				if (append) {
-					tbuf_append(b, value, value_len);
+					tbuf_append(b, fb, fe - fb);
 					tbuf_append(b, data, bytes);
 				} else {
 					tbuf_append(b, data, bytes);
-					tbuf_append(b, value, value_len);
+					tbuf_append(b, fb, fe - fb);
 				}
 
-				bytes += value_len;
+				bytes += (fe - fb);
 				data = b->data;
 				STORE;
 			}
 		}
 	goto st197;
 tr91:
-#line 248 "src/memcached-grammar.rl"
+#line 243 "src/memcached-grammar.rl"
 	{cas = memcached_natoq(fstart, p);}
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -594,7 +588,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -613,9 +607,9 @@ case 12:
 		}
 	goto st197;
 tr95:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -638,7 +632,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -657,11 +651,11 @@ case 12:
 		}
 	goto st197;
 tr105:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -684,7 +678,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -703,22 +697,21 @@ case 12:
 		}
 	goto st197;
 tr118:
-#line 249 "src/memcached-grammar.rl"
+#line 244 "src/memcached-grammar.rl"
 	{incr = memcached_natoq(fstart, p);}
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 124 "src/memcached-grammar.rl"
+#line 122 "src/memcached-grammar.rl"
 	{
 			struct meta *m;
 			struct tbuf *b;
-			const char *field;
-			u32 value_len;
+			const char *fb, *fe;
 			u64 value;
 
 			key = tbuf_read_field(keys);
@@ -727,12 +720,10 @@ case 12:
 				obuf_dup(out, "NOT_FOUND\r\n", 11);
 			} else {
 				m = memcached_meta(tuple);
-				field = tuple_field(tuple, 3);
-				value_len = load_varint32(&field);
+				tuple_field(tuple, 3, &fb, &fe);
 
-				if (memcached_is_numeric(field, value_len)) {
-					value = memcached_natoq((const char *) field,
-						(const char *) field + value_len);
+				if (memcached_is_numeric(fb, fe - fb)) {
+					value = memcached_natoq(fb, fe);
 
 					if (incr_sign > 0) {
 						value += incr;
@@ -770,20 +761,19 @@ case 12:
 		}
 	goto st197;
 tr122:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 124 "src/memcached-grammar.rl"
+#line 122 "src/memcached-grammar.rl"
 	{
 			struct meta *m;
 			struct tbuf *b;
-			const char *field;
-			u32 value_len;
+			const char *fb, *fe;
 			u64 value;
 
 			key = tbuf_read_field(keys);
@@ -792,12 +782,10 @@ case 12:
 				obuf_dup(out, "NOT_FOUND\r\n", 11);
 			} else {
 				m = memcached_meta(tuple);
-				field = tuple_field(tuple, 3);
-				value_len = load_varint32(&field);
+				tuple_field(tuple, 3, &fb, &fe);
 
-				if (memcached_is_numeric(field, value_len)) {
-					value = memcached_natoq((const char *) field,
-						(const char *) field + value_len);
+				if (memcached_is_numeric(fb, fe - fb)) {
+					value = memcached_natoq(fb, fe);
 
 					if (incr_sign > 0) {
 						value += incr;
@@ -835,22 +823,21 @@ case 12:
 		}
 	goto st197;
 tr132:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 124 "src/memcached-grammar.rl"
+#line 122 "src/memcached-grammar.rl"
 	{
 			struct meta *m;
 			struct tbuf *b;
-			const char *field;
-			u32 value_len;
+			const char *fb, *fe;
 			u64 value;
 
 			key = tbuf_read_field(keys);
@@ -859,12 +846,10 @@ case 12:
 				obuf_dup(out, "NOT_FOUND\r\n", 11);
 			} else {
 				m = memcached_meta(tuple);
-				field = tuple_field(tuple, 3);
-				value_len = load_varint32(&field);
+				tuple_field(tuple, 3, &fb, &fe);
 
-				if (memcached_is_numeric(field, value_len)) {
-					value = memcached_natoq((const char *) field,
-						(const char *) field + value_len);
+				if (memcached_is_numeric(fb, fe - fb)) {
+					value = memcached_natoq(fb, fe);
 
 					if (incr_sign > 0) {
 						value += incr;
@@ -902,15 +887,15 @@ case 12:
 		}
 	goto st197;
 tr141:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 179 "src/memcached-grammar.rl"
+#line 174 "src/memcached-grammar.rl"
 	{
 			key = tbuf_read_field(keys);
 			struct tuple *tuple = memcached_find(key);
@@ -930,21 +915,21 @@ case 12:
 		}
 	goto st197;
 tr146:
-#line 239 "src/memcached-grammar.rl"
+#line 234 "src/memcached-grammar.rl"
 	{
 			exptime = memcached_natoq(fstart, p);
 			if (exptime > 0 && exptime <= 60*60*24*30)
 				exptime = exptime + ev_now();
 		}
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 179 "src/memcached-grammar.rl"
+#line 174 "src/memcached-grammar.rl"
 	{
 			key = tbuf_read_field(keys);
 			struct tuple *tuple = memcached_find(key);
@@ -964,17 +949,17 @@ case 12:
 		}
 	goto st197;
 tr157:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 179 "src/memcached-grammar.rl"
+#line 174 "src/memcached-grammar.rl"
 	{
 			key = tbuf_read_field(keys);
 			struct tuple *tuple = memcached_find(key);
@@ -994,15 +979,15 @@ case 12:
 		}
 	goto st197;
 tr169:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 208 "src/memcached-grammar.rl"
+#line 203 "src/memcached-grammar.rl"
 	{
 			struct fiber *f = fiber_new("flush_all",
 						    memcached_flush_all);
@@ -1011,17 +996,17 @@ case 12:
 		}
 	goto st197;
 tr174:
-#line 250 "src/memcached-grammar.rl"
+#line 245 "src/memcached-grammar.rl"
 	{flush_delay = memcached_natoq(fstart, p);}
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 208 "src/memcached-grammar.rl"
+#line 203 "src/memcached-grammar.rl"
 	{
 			struct fiber *f = fiber_new("flush_all",
 						    memcached_flush_all);
@@ -1030,17 +1015,17 @@ case 12:
 		}
 	goto st197;
 tr185:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 208 "src/memcached-grammar.rl"
+#line 203 "src/memcached-grammar.rl"
 	{
 			struct fiber *f = fiber_new("flush_all",
 						    memcached_flush_all);
@@ -1049,15 +1034,15 @@ case 12:
 		}
 	goto st197;
 tr195:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 197 "src/memcached-grammar.rl"
+#line 192 "src/memcached-grammar.rl"
 	{
 			try {
 				memcached_get(out, keys_count, keys, show_cas);
@@ -1070,25 +1055,25 @@ case 12:
 		}
 	goto st197;
 tr213:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 219 "src/memcached-grammar.rl"
+#line 214 "src/memcached-grammar.rl"
 	{
 			return -1;
 		}
 	goto st197;
 tr233:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -1111,7 +1096,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -1128,9 +1113,9 @@ case 12:
 		}
 	goto st197;
 tr237:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -1153,7 +1138,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -1170,11 +1155,11 @@ case 12:
 		}
 	goto st197;
 tr246:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -1197,7 +1182,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -1214,11 +1199,11 @@ case 12:
 		}
 	goto st197;
 tr263:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -1241,7 +1226,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -1254,9 +1239,9 @@ case 12:
 		}
 	goto st197;
 tr267:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -1279,7 +1264,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -1292,11 +1277,11 @@ case 12:
 		}
 	goto st197;
 tr276:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 252 "src/memcached-grammar.rl"
+#line 247 "src/memcached-grammar.rl"
 	{
 			size_t parsed = p - in->pos;
 			while (ibuf_size(in) - parsed < bytes + 2) {
@@ -1319,7 +1304,7 @@ case 12:
 				goto exit;
 			}
 		}
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
@@ -1332,15 +1317,15 @@ case 12:
 		}
 	goto st197;
 tr281:
-#line 281 "src/memcached-grammar.rl"
+#line 276 "src/memcached-grammar.rl"
 	{ p++; }
-#line 275 "src/memcached-grammar.rl"
+#line 270 "src/memcached-grammar.rl"
 	{
 			done = true;
 			stats.bytes_read += p - in->pos;
 			in->pos = p;
 		}
-#line 215 "src/memcached-grammar.rl"
+#line 210 "src/memcached-grammar.rl"
 	{
 			memcached_print_stats(out);
 		}
@@ -1349,33 +1334,33 @@ case 12:
 	if ( ++p == pe )
 		goto _test_eof197;
 case 197:
-#line 1353 "src/memcached-grammar.cc"
+#line 1338 "src/memcached-grammar.cc"
 	goto st0;
 tr27:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
 	goto st13;
 tr40:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st13;
 st13:
 	if ( ++p == pe )
 		goto _test_eof13;
 case 13:
-#line 1367 "src/memcached-grammar.cc"
+#line 1352 "src/memcached-grammar.cc"
 	if ( (*p) == 10 )
 		goto tr30;
 	goto st0;
 tr28:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
 	goto st14;
 st14:
 	if ( ++p == pe )
 		goto _test_eof14;
 case 14:
-#line 1379 "src/memcached-grammar.cc"
+#line 1364 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 32: goto st14;
 		case 78: goto st15;
@@ -1489,18 +1474,18 @@ case 26:
 		goto tr45;
 	goto st0;
 tr45:
-#line 289 "src/memcached-grammar.rl"
+#line 284 "src/memcached-grammar.rl"
 	{append = true; }
 	goto st27;
 tr209:
-#line 290 "src/memcached-grammar.rl"
+#line 285 "src/memcached-grammar.rl"
 	{append = false;}
 	goto st27;
 st27:
 	if ( ++p == pe )
 		goto _test_eof27;
 case 27:
-#line 1504 "src/memcached-grammar.cc"
+#line 1489 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 13: goto st0;
 		case 32: goto st27;
@@ -1509,7 +1494,7 @@ case 27:
 		goto st0;
 	goto tr46;
 tr46:
-#line 224 "src/memcached-grammar.rl"
+#line 219 "src/memcached-grammar.rl"
 	{
 			fstart = p;
 			for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++);
@@ -1525,7 +1510,7 @@ case 27:
 	if ( ++p == pe )
 		goto _test_eof28;
 case 28:
-#line 1529 "src/memcached-grammar.cc"
+#line 1514 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st29;
 	goto st0;
@@ -1539,49 +1524,49 @@ case 29:
 		goto tr49;
 	goto st0;
 tr49:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st30;
 st30:
 	if ( ++p == pe )
 		goto _test_eof30;
 case 30:
-#line 1550 "src/memcached-grammar.cc"
+#line 1535 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto tr50;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto st30;
 	goto st0;
 tr50:
-#line 246 "src/memcached-grammar.rl"
+#line 241 "src/memcached-grammar.rl"
 	{flags = memcached_natoq(fstart, p);}
 	goto st31;
 st31:
 	if ( ++p == pe )
 		goto _test_eof31;
 case 31:
-#line 1564 "src/memcached-grammar.cc"
+#line 1549 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st31;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto tr53;
 	goto st0;
 tr53:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st32;
 st32:
 	if ( ++p == pe )
 		goto _test_eof32;
 case 32:
-#line 1578 "src/memcached-grammar.cc"
+#line 1563 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto tr54;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto st32;
 	goto st0;
 tr54:
-#line 239 "src/memcached-grammar.rl"
+#line 234 "src/memcached-grammar.rl"
 	{
 			exptime = memcached_natoq(fstart, p);
 			if (exptime > 0 && exptime <= 60*60*24*30)
@@ -1592,21 +1577,21 @@ case 32:
 	if ( ++p == pe )
 		goto _test_eof33;
 case 33:
-#line 1596 "src/memcached-grammar.cc"
+#line 1581 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st33;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto tr57;
 	goto st0;
 tr57:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st34;
 st34:
 	if ( ++p == pe )
 		goto _test_eof34;
 case 34:
-#line 1610 "src/memcached-grammar.cc"
+#line 1595 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr58;
 		case 13: goto tr59;
@@ -1616,30 +1601,30 @@ case 34:
 		goto st34;
 	goto st0;
 tr59:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
 	goto st35;
 tr72:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st35;
 st35:
 	if ( ++p == pe )
 		goto _test_eof35;
 case 35:
-#line 1631 "src/memcached-grammar.cc"
+#line 1616 "src/memcached-grammar.cc"
 	if ( (*p) == 10 )
 		goto tr62;
 	goto st0;
 tr60:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
 	goto st36;
 st36:
 	if ( ++p == pe )
 		goto _test_eof36;
 case 36:
-#line 1643 "src/memcached-grammar.cc"
+#line 1628 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 32: goto st36;
 		case 78: goto st37;
@@ -1746,7 +1731,7 @@ case 47:
 		goto st0;
 	goto tr76;
 tr76:
-#line 224 "src/memcached-grammar.rl"
+#line 219 "src/memcached-grammar.rl"
 	{
 			fstart = p;
 			for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++);
@@ -1762,7 +1747,7 @@ case 47:
 	if ( ++p == pe )
 		goto _test_eof48;
 case 48:
-#line 1766 "src/memcached-grammar.cc"
+#line 1751 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st49;
 	goto st0;
@@ -1776,49 +1761,49 @@ case 49:
 		goto tr78;
 	goto st0;
 tr78:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st50;
 st50:
 	if ( ++p == pe )
 		goto _test_eof50;
 case 50:
-#line 1787 "src/memcached-grammar.cc"
+#line 1772 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto tr79;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto st50;
 	goto st0;
 tr79:
-#line 246 "src/memcached-grammar.rl"
+#line 241 "src/memcached-grammar.rl"
 	{flags = memcached_natoq(fstart, p);}
 	goto st51;
 st51:
 	if ( ++p == pe )
 		goto _test_eof51;
 case 51:
-#line 1801 "src/memcached-grammar.cc"
+#line 1786 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st51;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto tr82;
 	goto st0;
 tr82:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st52;
 st52:
 	if ( ++p == pe )
 		goto _test_eof52;
 case 52:
-#line 1815 "src/memcached-grammar.cc"
+#line 1800 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto tr83;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto st52;
 	goto st0;
 tr83:
-#line 239 "src/memcached-grammar.rl"
+#line 234 "src/memcached-grammar.rl"
 	{
 			exptime = memcached_natoq(fstart, p);
 			if (exptime > 0 && exptime <= 60*60*24*30)
@@ -1829,49 +1814,49 @@ case 52:
 	if ( ++p == pe )
 		goto _test_eof53;
 case 53:
-#line 1833 "src/memcached-grammar.cc"
+#line 1818 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st53;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto tr86;
 	goto st0;
 tr86:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st54;
 st54:
 	if ( ++p == pe )
 		goto _test_eof54;
 case 54:
-#line 1847 "src/memcached-grammar.cc"
+#line 1832 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto tr87;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto st54;
 	goto st0;
 tr87:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
 	goto st55;
 st55:
 	if ( ++p == pe )
 		goto _test_eof55;
 case 55:
-#line 1861 "src/memcached-grammar.cc"
+#line 1846 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st55;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto tr90;
 	goto st0;
 tr90:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st56;
 st56:
 	if ( ++p == pe )
 		goto _test_eof56;
 case 56:
-#line 1875 "src/memcached-grammar.cc"
+#line 1860 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr91;
 		case 13: goto tr92;
@@ -1881,30 +1866,30 @@ case 56:
 		goto st56;
 	goto st0;
 tr106:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st57;
 tr92:
-#line 248 "src/memcached-grammar.rl"
+#line 243 "src/memcached-grammar.rl"
 	{cas = memcached_natoq(fstart, p);}
 	goto st57;
 st57:
 	if ( ++p == pe )
 		goto _test_eof57;
 case 57:
-#line 1896 "src/memcached-grammar.cc"
+#line 1881 "src/memcached-grammar.cc"
 	if ( (*p) == 10 )
 		goto tr95;
 	goto st0;
 tr93:
-#line 248 "src/memcached-grammar.rl"
+#line 243 "src/memcached-grammar.rl"
 	{cas = memcached_natoq(fstart, p);}
 	goto st58;
 st58:
 	if ( ++p == pe )
 		goto _test_eof58;
 case 58:
-#line 1908 "src/memcached-grammar.cc"
+#line 1893 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr95;
 		case 13: goto st57;
@@ -1978,14 +1963,14 @@ case 65:
 	}
 	goto st0;
 tr107:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st66;
 st66:
 	if ( ++p == pe )
 		goto _test_eof66;
 case 66:
-#line 1989 "src/memcached-grammar.cc"
+#line 1974 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr95;
 		case 13: goto st57;
@@ -2029,18 +2014,18 @@ case 70:
 		goto tr113;
 	goto st0;
 tr113:
-#line 298 "src/memcached-grammar.rl"
+#line 293 "src/memcached-grammar.rl"
 	{incr_sign = -1;}
 	goto st71;
 tr202:
-#line 297 "src/memcached-grammar.rl"
+#line 292 "src/memcached-grammar.rl"
 	{incr_sign = 1; }
 	goto st71;
 st71:
 	if ( ++p == pe )
 		goto _test_eof71;
 case 71:
-#line 2044 "src/memcached-grammar.cc"
+#line 2029 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 13: goto st0;
 		case 32: goto st71;
@@ -2049,7 +2034,7 @@ case 71:
 		goto st0;
 	goto tr114;
 tr114:
-#line 224 "src/memcached-grammar.rl"
+#line 219 "src/memcached-grammar.rl"
 	{
 			fstart = p;
 			for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++);
@@ -2065,7 +2050,7 @@ case 71:
 	if ( ++p == pe )
 		goto _test_eof72;
 case 72:
-#line 2069 "src/memcached-grammar.cc"
+#line 2054 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st73;
 	goto st0;
@@ -2079,14 +2064,14 @@ case 73:
 		goto tr117;
 	goto st0;
 tr117:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st74;
 st74:
 	if ( ++p == pe )
 		goto _test_eof74;
 case 74:
-#line 2090 "src/memcached-grammar.cc"
+#line 2075 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr118;
 		case 13: goto tr119;
@@ -2096,30 +2081,30 @@ case 74:
 		goto st74;
 	goto st0;
 tr133:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st75;
 tr119:
-#line 249 "src/memcached-grammar.rl"
+#line 244 "src/memcached-grammar.rl"
 	{incr = memcached_natoq(fstart, p);}
 	goto st75;
 st75:
 	if ( ++p == pe )
 		goto _test_eof75;
 case 75:
-#line 2111 "src/memcached-grammar.cc"
+#line 2096 "src/memcached-grammar.cc"
 	if ( (*p) == 10 )
 		goto tr122;
 	goto st0;
 tr120:
-#line 249 "src/memcached-grammar.rl"
+#line 244 "src/memcached-grammar.rl"
 	{incr = memcached_natoq(fstart, p);}
 	goto st76;
 st76:
 	if ( ++p == pe )
 		goto _test_eof76;
 case 76:
-#line 2123 "src/memcached-grammar.cc"
+#line 2108 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr122;
 		case 13: goto st75;
@@ -2193,14 +2178,14 @@ case 83:
 	}
 	goto st0;
 tr134:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st84;
 st84:
 	if ( ++p == pe )
 		goto _test_eof84;
 case 84:
-#line 2204 "src/memcached-grammar.cc"
+#line 2189 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr122;
 		case 13: goto st75;
@@ -2253,7 +2238,7 @@ case 89:
 		goto st0;
 	goto tr140;
 tr140:
-#line 224 "src/memcached-grammar.rl"
+#line 219 "src/memcached-grammar.rl"
 	{
 			fstart = p;
 			for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++);
@@ -2269,7 +2254,7 @@ case 89:
 	if ( ++p == pe )
 		goto _test_eof90;
 case 90:
-#line 2273 "src/memcached-grammar.cc"
+#line 2258 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr141;
 		case 13: goto st91;
@@ -2277,7 +2262,7 @@ case 90:
 	}
 	goto st0;
 tr147:
-#line 239 "src/memcached-grammar.rl"
+#line 234 "src/memcached-grammar.rl"
 	{
 			exptime = memcached_natoq(fstart, p);
 			if (exptime > 0 && exptime <= 60*60*24*30)
@@ -2285,14 +2270,14 @@ case 90:
 		}
 	goto st91;
 tr158:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st91;
 st91:
 	if ( ++p == pe )
 		goto _test_eof91;
 case 91:
-#line 2296 "src/memcached-grammar.cc"
+#line 2281 "src/memcached-grammar.cc"
 	if ( (*p) == 10 )
 		goto tr141;
 	goto st0;
@@ -2311,14 +2296,14 @@ case 92:
 		goto tr144;
 	goto st0;
 tr144:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st93;
 st93:
 	if ( ++p == pe )
 		goto _test_eof93;
 case 93:
-#line 2322 "src/memcached-grammar.cc"
+#line 2307 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr146;
 		case 13: goto tr147;
@@ -2328,7 +2313,7 @@ case 93:
 		goto st93;
 	goto st0;
 tr148:
-#line 239 "src/memcached-grammar.rl"
+#line 234 "src/memcached-grammar.rl"
 	{
 			exptime = memcached_natoq(fstart, p);
 			if (exptime > 0 && exptime <= 60*60*24*30)
@@ -2339,7 +2324,7 @@ case 93:
 	if ( ++p == pe )
 		goto _test_eof94;
 case 94:
-#line 2343 "src/memcached-grammar.cc"
+#line 2328 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr141;
 		case 13: goto st91;
@@ -2413,14 +2398,14 @@ case 101:
 	}
 	goto st0;
 tr159:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st102;
 st102:
 	if ( ++p == pe )
 		goto _test_eof102;
 case 102:
-#line 2424 "src/memcached-grammar.cc"
+#line 2409 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr141;
 		case 13: goto st91;
@@ -2508,18 +2493,18 @@ case 111:
 	}
 	goto st0;
 tr186:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st112;
 tr175:
-#line 250 "src/memcached-grammar.rl"
+#line 245 "src/memcached-grammar.rl"
 	{flush_delay = memcached_natoq(fstart, p);}
 	goto st112;
 st112:
 	if ( ++p == pe )
 		goto _test_eof112;
 case 112:
-#line 2523 "src/memcached-grammar.cc"
+#line 2508 "src/memcached-grammar.cc"
 	if ( (*p) == 10 )
 		goto tr169;
 	goto st0;
@@ -2538,14 +2523,14 @@ case 113:
 		goto tr172;
 	goto st0;
 tr172:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st114;
 st114:
 	if ( ++p == pe )
 		goto _test_eof114;
 case 114:
-#line 2549 "src/memcached-grammar.cc"
+#line 2534 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr174;
 		case 13: goto tr175;
@@ -2555,14 +2540,14 @@ case 114:
 		goto st114;
 	goto st0;
 tr176:
-#line 250 "src/memcached-grammar.rl"
+#line 245 "src/memcached-grammar.rl"
 	{flush_delay = memcached_natoq(fstart, p);}
 	goto st115;
 st115:
 	if ( ++p == pe )
 		goto _test_eof115;
 case 115:
-#line 2566 "src/memcached-grammar.cc"
+#line 2551 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr169;
 		case 13: goto st112;
@@ -2636,14 +2621,14 @@ case 122:
 	}
 	goto st0;
 tr187:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st123;
 st123:
 	if ( ++p == pe )
 		goto _test_eof123;
 case 123:
-#line 2647 "src/memcached-grammar.cc"
+#line 2632 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr169;
 		case 13: goto st112;
@@ -2679,18 +2664,18 @@ case 126:
 	}
 	goto st0;
 tr191:
-#line 294 "src/memcached-grammar.rl"
+#line 289 "src/memcached-grammar.rl"
 	{show_cas = false;}
 	goto st127;
 tr198:
-#line 295 "src/memcached-grammar.rl"
+#line 290 "src/memcached-grammar.rl"
 	{show_cas = true;}
 	goto st127;
 st127:
 	if ( ++p == pe )
 		goto _test_eof127;
 case 127:
-#line 2694 "src/memcached-grammar.cc"
+#line 2679 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 13: goto st0;
 		case 32: goto st127;
@@ -2699,7 +2684,7 @@ case 127:
 		goto st0;
 	goto tr193;
 tr193:
-#line 224 "src/memcached-grammar.rl"
+#line 219 "src/memcached-grammar.rl"
 	{
 			fstart = p;
 			for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++);
@@ -2715,7 +2700,7 @@ case 127:
 	if ( ++p == pe )
 		goto _test_eof128;
 case 128:
-#line 2719 "src/memcached-grammar.cc"
+#line 2704 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr195;
 		case 13: goto st129;
@@ -2958,7 +2943,7 @@ case 155:
 		goto st0;
 	goto tr222;
 tr222:
-#line 224 "src/memcached-grammar.rl"
+#line 219 "src/memcached-grammar.rl"
 	{
 			fstart = p;
 			for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++);
@@ -2974,7 +2959,7 @@ case 155:
 	if ( ++p == pe )
 		goto _test_eof156;
 case 156:
-#line 2978 "src/memcached-grammar.cc"
+#line 2963 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st157;
 	goto st0;
@@ -2988,49 +2973,49 @@ case 157:
 		goto tr224;
 	goto st0;
 tr224:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st158;
 st158:
 	if ( ++p == pe )
 		goto _test_eof158;
 case 158:
-#line 2999 "src/memcached-grammar.cc"
+#line 2984 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto tr225;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto st158;
 	goto st0;
 tr225:
-#line 246 "src/memcached-grammar.rl"
+#line 241 "src/memcached-grammar.rl"
 	{flags = memcached_natoq(fstart, p);}
 	goto st159;
 st159:
 	if ( ++p == pe )
 		goto _test_eof159;
 case 159:
-#line 3013 "src/memcached-grammar.cc"
+#line 2998 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st159;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto tr228;
 	goto st0;
 tr228:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st160;
 st160:
 	if ( ++p == pe )
 		goto _test_eof160;
 case 160:
-#line 3027 "src/memcached-grammar.cc"
+#line 3012 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto tr229;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto st160;
 	goto st0;
 tr229:
-#line 239 "src/memcached-grammar.rl"
+#line 234 "src/memcached-grammar.rl"
 	{
 			exptime = memcached_natoq(fstart, p);
 			if (exptime > 0 && exptime <= 60*60*24*30)
@@ -3041,21 +3026,21 @@ case 160:
 	if ( ++p == pe )
 		goto _test_eof161;
 case 161:
-#line 3045 "src/memcached-grammar.cc"
+#line 3030 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st161;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto tr232;
 	goto st0;
 tr232:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st162;
 st162:
 	if ( ++p == pe )
 		goto _test_eof162;
 case 162:
-#line 3059 "src/memcached-grammar.cc"
+#line 3044 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr233;
 		case 13: goto tr234;
@@ -3065,30 +3050,30 @@ case 162:
 		goto st162;
 	goto st0;
 tr234:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
 	goto st163;
 tr247:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st163;
 st163:
 	if ( ++p == pe )
 		goto _test_eof163;
 case 163:
-#line 3080 "src/memcached-grammar.cc"
+#line 3065 "src/memcached-grammar.cc"
 	if ( (*p) == 10 )
 		goto tr237;
 	goto st0;
 tr235:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
 	goto st164;
 st164:
 	if ( ++p == pe )
 		goto _test_eof164;
 case 164:
-#line 3092 "src/memcached-grammar.cc"
+#line 3077 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 32: goto st164;
 		case 78: goto st165;
@@ -3197,7 +3182,7 @@ case 175:
 		goto st0;
 	goto tr252;
 tr252:
-#line 224 "src/memcached-grammar.rl"
+#line 219 "src/memcached-grammar.rl"
 	{
 			fstart = p;
 			for (; p < pe && *p != ' ' && *p != '\r' && *p != '\n'; p++);
@@ -3213,7 +3198,7 @@ case 175:
 	if ( ++p == pe )
 		goto _test_eof176;
 case 176:
-#line 3217 "src/memcached-grammar.cc"
+#line 3202 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st177;
 	goto st0;
@@ -3227,49 +3212,49 @@ case 177:
 		goto tr254;
 	goto st0;
 tr254:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st178;
 st178:
 	if ( ++p == pe )
 		goto _test_eof178;
 case 178:
-#line 3238 "src/memcached-grammar.cc"
+#line 3223 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto tr255;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto st178;
 	goto st0;
 tr255:
-#line 246 "src/memcached-grammar.rl"
+#line 241 "src/memcached-grammar.rl"
 	{flags = memcached_natoq(fstart, p);}
 	goto st179;
 st179:
 	if ( ++p == pe )
 		goto _test_eof179;
 case 179:
-#line 3252 "src/memcached-grammar.cc"
+#line 3237 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st179;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto tr258;
 	goto st0;
 tr258:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st180;
 st180:
 	if ( ++p == pe )
 		goto _test_eof180;
 case 180:
-#line 3266 "src/memcached-grammar.cc"
+#line 3251 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto tr259;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto st180;
 	goto st0;
 tr259:
-#line 239 "src/memcached-grammar.rl"
+#line 234 "src/memcached-grammar.rl"
 	{
 			exptime = memcached_natoq(fstart, p);
 			if (exptime > 0 && exptime <= 60*60*24*30)
@@ -3280,21 +3265,21 @@ case 180:
 	if ( ++p == pe )
 		goto _test_eof181;
 case 181:
-#line 3284 "src/memcached-grammar.cc"
+#line 3269 "src/memcached-grammar.cc"
 	if ( (*p) == 32 )
 		goto st181;
 	if ( 48 <= (*p) && (*p) <= 57 )
 		goto tr262;
 	goto st0;
 tr262:
-#line 223 "src/memcached-grammar.rl"
+#line 218 "src/memcached-grammar.rl"
 	{ fstart = p; }
 	goto st182;
 st182:
 	if ( ++p == pe )
 		goto _test_eof182;
 case 182:
-#line 3298 "src/memcached-grammar.cc"
+#line 3283 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 10: goto tr263;
 		case 13: goto tr264;
@@ -3304,30 +3289,30 @@ case 182:
 		goto st182;
 	goto st0;
 tr264:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
 	goto st183;
 tr277:
-#line 283 "src/memcached-grammar.rl"
+#line 278 "src/memcached-grammar.rl"
 	{ noreply = true; }
 	goto st183;
 st183:
 	if ( ++p == pe )
 		goto _test_eof183;
 case 183:
-#line 3319 "src/memcached-grammar.cc"
+#line 3304 "src/memcached-grammar.cc"
 	if ( (*p) == 10 )
 		goto tr267;
 	goto st0;
 tr265:
-#line 247 "src/memcached-grammar.rl"
+#line 242 "src/memcached-grammar.rl"
 	{bytes = memcached_natoq(fstart, p);}
 	goto st184;
 st184:
 	if ( ++p == pe )
 		goto _test_eof184;
 case 184:
-#line 3331 "src/memcached-grammar.cc"
+#line 3316 "src/memcached-grammar.cc"
 	switch( (*p) ) {
 		case 32: goto st184;
 		case 78: goto st185;
@@ -3642,7 +3627,7 @@ case 196:
 	_out: {}
 	}
 
-#line 308 "src/memcached-grammar.rl"
+#line 303 "src/memcached-grammar.rl"
 
 
 	if (!done) {
diff --git a/src/memcached-grammar.rl b/src/memcached-grammar.rl
index 62b738e345..f772b5f0b4 100644
--- a/src/memcached-grammar.rl
+++ b/src/memcached-grammar.rl
@@ -96,26 +96,24 @@ memcached_dispatch(struct ev_io *coio, struct iobuf *iobuf)
 
 		action append_prepend {
 			struct tbuf *b;
-			const char *value;
-			u32 value_len;
+			const char *fb, *fe;
 
 			key = tbuf_read_field(keys);
 			struct tuple *tuple = memcached_find(key);
 			if (tuple == NULL) {
 				obuf_dup(out, "NOT_STORED\r\n", 12);
 			} else {
-				value = tuple_field(tuple, 3);
-				value_len = load_varint32(&value);
+				tuple_field(tuple, 3, &fb, &fe);
 				b = tbuf_new(fiber->gc_pool);
 				if (append) {
-					tbuf_append(b, value, value_len);
+					tbuf_append(b, fb, fe - fb);
 					tbuf_append(b, data, bytes);
 				} else {
 					tbuf_append(b, data, bytes);
-					tbuf_append(b, value, value_len);
+					tbuf_append(b, fb, fe - fb);
 				}
 
-				bytes += value_len;
+				bytes += (fe - fb);
 				data = b->data;
 				STORE;
 			}
@@ -124,8 +122,7 @@ memcached_dispatch(struct ev_io *coio, struct iobuf *iobuf)
 		action incr_decr {
 			struct meta *m;
 			struct tbuf *b;
-			const char *field;
-			u32 value_len;
+			const char *fb, *fe;
 			u64 value;
 
 			key = tbuf_read_field(keys);
@@ -134,12 +131,10 @@ memcached_dispatch(struct ev_io *coio, struct iobuf *iobuf)
 				obuf_dup(out, "NOT_FOUND\r\n", 11);
 			} else {
 				m = memcached_meta(tuple);
-				field = tuple_field(tuple, 3);
-				value_len = load_varint32(&field);
+				tuple_field(tuple, 3, &fb, &fe);
 
-				if (memcached_is_numeric(field, value_len)) {
-					value = memcached_natoq((const char *) field,
-						(const char *) field + value_len);
+				if (memcached_is_numeric(fb, fe - fb)) {
+					value = memcached_natoq(fb, fe);
 
 					if (incr_sign > 0) {
 						value += incr;
diff --git a/src/memcached.cc b/src/memcached.cc
index de12a2f0b4..a8fec80070 100644
--- a/src/memcached.cc
+++ b/src/memcached.cc
@@ -180,8 +180,10 @@ memcached_find(const char *key)
 static struct meta *
 memcached_meta(struct tuple *tuple)
 {
-	const char *field = tuple_field(tuple, 1);
-	return (struct meta *) (field + 1);
+	const char *fb, *fe;
+	tuple_field(tuple, 1, &fb, &fe);
+	assert (fb + sizeof(struct meta) <= fe);
+	return (struct meta *) fb;
 }
 
 static bool
@@ -270,13 +272,11 @@ void memcached_get(struct obuf *out, size_t keys_count, struct tbuf *keys,
 	while (keys_count-- > 0) {
 		struct tuple *tuple;
 		const struct meta *m;
-		const char *field;
 		const char *value;
 		const char *suffix;
 		u32 key_len;
 		u32 value_len;
 		u32 suffix_len;
-		u32 _l;
 
 		const char *key = tbuf_read_field(keys);
 		tuple = memcached_find(key);
@@ -288,25 +288,27 @@ void memcached_get(struct obuf *out, size_t keys_count, struct tbuf *keys,
 			continue;
 		}
 
-		field = tuple->data;
-
+		const char *fb, *fe;
+		struct tuple_iterator it;
 		/* skip key */
-		_l = load_varint32(&field);
-		field = field + _l;
+		tuple_seek(&it, tuple, 1, &fb, &fe);
 
 		/* metainfo */
-		_l = load_varint32(&field);
-		m = (const struct meta *) field;
-		field = field + _l;
+		tuple_next(&it);
+		m = (const struct meta *) fb;
+		assert (fb + sizeof(struct meta *) < fe);
 
 		/* suffix */
-		suffix_len = load_varint32(&field);
-		suffix = field;
-		field = field + suffix_len;
+		tuple_next(&it);
+		suffix_len = fe - fb;
+		suffix = fb;
 
 		/* value */
-		value_len = load_varint32(&field);
-		value = field;
+		tuple_next(&it);
+		value_len = fe - fb;
+		value = fb;
+
+		assert (!tuple_next(&it));
 
 		if (m->exptime > 0 && m->exptime < ev_now()) {
 			stats.get_misses++;
diff --git a/test/box/lua.result b/test/box/lua.result
index 4c17fb494e..f5d95e967c 100644
--- a/test/box/lua.result
+++ b/test/box/lua.result
@@ -597,7 +597,7 @@ error: 'tuple.next(): bad arguments'
 ...
 lua t:next(1)
 ---
-error: 'tuple.next(): bad arguments'
+ - nil
 ...
 lua t:next(t)
 ---
-- 
GitLab