The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 51249
MANIFEST 1015
META.json 058
META.yml 2930
Makefile.PL 34
README.pod 1128
Tarantool.xs 2637
debian/changelog 031
debian/rules 03
lib/DR/Tarantool/AsyncClient.pm 11
lib/DR/Tarantool/CoroClient.pm 11
lib/DR/Tarantool/LLClient.pm 536
lib/DR/Tarantool/LLSyncClient.pm 0268
lib/DR/Tarantool/RealSyncClient.pm 0249
lib/DR/Tarantool/Spaces.pm 22
lib/DR/Tarantool/StartTest.pm 916
lib/DR/Tarantool/SyncClient.pm 22
lib/DR/Tarantool/Tuple.pm 11
lib/DR/Tarantool.pm 1128
t/010-xs.t 257
t/025-ll_synclient.t 0189
t/065-realsync-client.t 0192
t/110-netsplit-readahead.t 11
tp.h 67511
24 files changed (This is a version diff) 2322009
@@ -1,72 +1,270 @@
-Revision history for Perl extension DR::Tarantool.
+libdr-tarantool-perl (0.42-1) unstable; urgency=low
 
-0.01  Sat May 12 18:51:55 2012
-	- original version; created by h2xs 1.23 with options
-		-n DR::Tarantool
+  * Rebuild for Debian perl 5.18.
+  * Fix some cpan tests (that are run without tarantool).
 
-0.02  Sun May 20 10:38:44 MSK 2012
-        - update documentation
+ -- Dmitry E. Oboukhov <unera@debian.org>  Wed, 18 Sep 2013 01:37:52 +0400
 
-0.03  Sun May 20 17:17:38 MSK 2012
-	- fix forgotten depends
+libdr-tarantool-perl (0.41-1) unstable; urgency=low
 
-0.04  Tue May 22 17:52:21 MSK 2012
-	- upgrade tests for new upstream library (there is no changes in driver)
+  * Fix broken xs-test (by cpan testers reports).
 
-0.05  Thu May 24 11:50:11 MSK 2012
-        - call_lua always unpacks fields, even if You doesn't define
-            their types.
+ -- Dmitry E. Oboukhov <unera@debian.org>  Mon, 02 Sep 2013 15:37:11 +0400
 
-0.06  Thu May 24 13:54:30 MSK 2012
-	- sync methods return tuple or undef
+libdr-tarantool-perl (0.40-1) unstable; urgency=low
 
-0.07  Thu May 24 18:06:17 MSK 2012
-        - iterator can construct Your objects.
+  * Add RealSyncClient module.
 
-0.08  Fri May 25 11:12:40 MSK 2012
-        - add 'JSON' to filed type list.
+ -- Dmitry E. Oboukhov <unera@debian.org>  Wed, 28 Aug 2013 17:00:13 +0400
 
-0.09  Sat May 26 13:45:12 MSK 2012
-        - fix iterators if iter->count > 2
+libdr-tarantool-perl (0.39-1) unstable; urgency=low
 
-0.11 Tue May 29 21:18:58 MSK 2012
-        - add DR::Tarantool::CoroClient.
-        - you can select tuples using partial index.
+  * Ping method works even connection isn't established.
 
-0.12 Sat Jun  2 22:37:15 MSK 2012
-        - add INT & INT64 types (signed value)
-        - add MONEY & BIGMONEY types
+ -- Dmitry E. Oboukhov <unera@debian.org>  Wed, 21 Aug 2013 17:33:08 +0400
 
-0.14 Mon Jun  4 10:23:52 MSK 2012
-	- if Coro isn't installed all tests passed properly
+libdr-tarantool-perl (0.38-1) unstable; urgency=low
 
-0.15 Thu Jun  7 00:07:44 MSK 2012
-	- fix tuple destructor (it doesn't crashes in global destructor)
+  * Parser doesn't segfault if tarantool replies by broken package.
 
-0.16 Sat Jun 23 16:39:13 MSK 2012
-        - add some functions to iterator.
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sat, 01 Jun 2013 21:23:23 +0400
 
-0.17 Wed Jun 27 11:49:40 MSK 2012
-	- backported to perl 5.8.8
+libdr-tarantool-perl (0.37-1) unstable; urgency=low
 
-0.18 Mon Jul  2 10:29:05 MSK 2012
-        - add some functions to Space
+  * Update perldoc.
 
-0.19 Mon Jul  9 12:55:38 MSK 2012
-        - optimization for parallel requests.
-        - reduce stack requirements for tuple destructors (for Coro).
+ -- Dmitry E. Oboukhov <unera@debian.org>  Mon, 29 Apr 2013 14:37:25 +0400
 
-0.20 Tue Jul 10 10:46:36 MSK 2012
-	- some optimizations in iterator.
+libdr-tarantool-perl (0.36-1) unstable; urgency=low
 
-0.22 Tue Jul 31 14:39:10 MSK 2012
-        - prebuild tuple packages in space (so Tuple doesn't use AUTOLOAD)
-        - you can use connector without any spaces
-          (for example for only lua calls)
+  * Update perldoc.
+  * Extends readahead buffer to fix FTBFS, closes: #704266.
 
-0.23 Wed Sep  5 13:43:25 MSK 2012
-        - add logging request/responses.
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sun, 21 Apr 2013 12:23:43 +0400
 
-0.24 Tue Dec 11 23:29:53 MSK 2012
-  	- Fix possibly memory leak
-  	- closes Debian FTBFS (fix tests)
+libdr-tarantool-perl (0.35-1) unstable; urgency=low
+
+  * Add stress test for tarantool, some additional test cases.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Wed, 30 Jan 2013 23:54:19 +0400
+
+libdr-tarantool-perl (0.34-1) unstable; urgency=low
+
+  * Fix some tests.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Tue, 29 Jan 2013 10:12:56 +0400
+
+libdr-tarantool-perl (0.33-2) unstable; urgency=low
+
+  * All requests that are called between reconnects will wait connection
+    instead error returning.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sun, 27 Jan 2013 16:48:26 +0400
+
+libdr-tarantool-perl (0.32-1) unstable; urgency=low
+
+  * Re-enable tests.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Wed, 23 Jan 2013 14:56:43 +0400
+
+libdr-tarantool-perl (0.31-1) unstable; urgency=low
+
+  * Disable some tests (try to localize some problems with cpan testers).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Mon, 21 Jan 2013 21:56:14 +0400
+
+libdr-tarantool-perl (0.30-1) unstable; urgency=low
+
+  * Fixed empty tuple list in tp.h.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sat, 19 Jan 2013 00:30:24 +0400
+
+libdr-tarantool-perl (0.29-1) unstable; urgency=low
+
+  * Fixed some warnings in tests.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Tue, 15 Jan 2013 22:31:13 +0400
+
+libdr-tarantool-perl (0.28-1) unstable; urgency=low
+
+  * Fix disconnect async method.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Tue, 15 Jan 2013 22:17:35 +0400
+
+libdr-tarantool-perl (0.27-1) unstable; urgency=low
+
+  * Don't use libtarantool for depends/build-depends.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Thu, 10 Jan 2013 17:53:46 +0400
+
+libdr-tarantool-perl (0.26-1) unstable; urgency=low
+
+  * Update homepage/vcs information.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Mon, 07 Jan 2013 03:01:47 +0400
+
+libdr-tarantool-perl (0.25-1) unstable; urgency=low
+
+  * Uses AE::io instead AE::Handle (benchmarks, benchmarks... :)).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sun, 06 Jan 2013 19:18:46 +0400
+
+libdr-tarantool-perl (0.24-1) unstable; urgency=low
+
+  * Fix possibly memory leak, closes Debian FTBFS (fix tests),
+    closes: #695660.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Tue, 11 Dec 2012 23:28:06 +0400
+
+libdr-tarantool-perl (0.23-1) unstable; urgency=low
+
+  * LLClient can log requests/reposnses (and error responses).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Wed, 05 Sep 2012 13:35:43 +0400
+
+libdr-tarantool-perl (0.22-1) unstable; urgency=low
+
+  * Prebuild tuple packages (so it doesn't use AUTOLOAD anymore).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Mon, 20 Aug 2012 21:19:44 +0400
+
+libdr-tarantool-perl (0.21-1) unstable; urgency=low
+
+  * Add some functions to iterator (sort, grep, ...).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sun, 15 Jul 2012 20:10:23 +0400
+
+libdr-tarantool-perl (0.20-1) unstable; urgency=low
+
+  * Some optimizations in iterators.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Tue, 10 Jul 2012 10:45:35 +0400
+
+libdr-tarantool-perl (0.19-1) unstable; urgency=low
+
+  * New upstream version: it is optimized for parallel requests.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Thu, 05 Jul 2012 23:50:03 +0400
+
+libdr-tarantool-perl (0.18-1) unstable; urgency=low
+
+  * HVs are created like perlxs: trying to be compatible with libcoro-perl.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Mon, 02 Jul 2012 10:12:39 +0400
+
+libdr-tarantool-perl (0.17-2) unstable; urgency=low
+
+  * Rebuilt for perl 5.14.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Fri, 29 Jun 2012 07:20:46 +0400
+
+libdr-tarantool-perl (0.17-1) unstable; urgency=low
+
+  * New upstream version (perl 5.8.8 compatible).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Wed, 27 Jun 2012 10:38:57 +0400
+
+libdr-tarantool-perl (0.16-1) unstable; urgency=low
+
+  * New upstream version. Add some functions to iterators.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sat, 23 Jun 2012 16:27:22 +0400
+
+libdr-tarantool-perl (0.15-1) unstable; urgency=low
+
+  * New upstream version.
+    Fix tuple destructor (it doesn't crashes in global destructor).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Thu, 07 Jun 2012 00:08:53 +0400
+
+libdr-tarantool-perl (0.14-1) unstable; urgency=low
+
+  * New version: Coro can be uninstalled: some tests will be skipped.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Mon, 04 Jun 2012 10:21:49 +0400
+
+libdr-tarantool-perl (0.12-1) unstable; urgency=low
+
+  * New version (provides new field types).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sat, 02 Jun 2012 22:35:47 +0400
+
+libdr-tarantool-perl (0.11-1) unstable; urgency=low
+
+  * New version. You can use parts of indexes in 'select'.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Tue, 29 May 2012 21:21:05 +0400
+
+libdr-tarantool-perl (0.10-1) unstable; urgency=low
+
+  * New version. Add DR::Tarantool::CoroClient.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Mon, 28 May 2012 20:05:12 +0400
+
+libdr-tarantool-perl (0.09-2) unstable; urgency=low
+
+  * Fix homepage section in debian/control.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Mon, 28 May 2012 10:00:48 +0400
+
+libdr-tarantool-perl (0.09-1) unstable; urgency=low
+
+  * New version. Fix tuple iterator.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sat, 26 May 2012 13:46:38 +0400
+
+libdr-tarantool-perl (0.08-2) unstable; urgency=low
+
+  * Add libjson-xs-perl into depends.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Fri, 25 May 2012 11:20:52 +0400
+
+libdr-tarantool-perl (0.08-1) unstable; urgency=low
+
+  * New version. Add 'JSON' to fields type list.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Fri, 25 May 2012 11:13:30 +0400
+
+libdr-tarantool-perl (0.07-1) unstable; urgency=low
+
+  * Iterators can construct objects. New version.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Thu, 24 May 2012 18:06:52 +0400
+
+libdr-tarantool-perl (0.06-1) unstable; urgency=low
+
+  * New version (sync methods return tuple or undef).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Thu, 24 May 2012 13:51:52 +0400
+
+libdr-tarantool-perl (0.05-1) unstable; urgency=low
+
+  * New version (some fixes in call_lua).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Thu, 24 May 2012 11:52:34 +0400
+
+libdr-tarantool-perl (0.04-1) unstable; urgency=low
+
+  * New version (upstream upgrades library).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Tue, 22 May 2012 17:51:25 +0400
+
+libdr-tarantool-perl (0.03-1) unstable; urgency=low
+
+  * New version.
+  * Add depends on Devel::GlobalDestruction.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sun, 20 May 2012 17:16:21 +0400
+
+libdr-tarantool-perl (0.02-1) unstable; urgency=low
+
+  * New version.
+  * Fixed documentation.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sun, 20 May 2012 09:36:46 +0400
+
+libdr-tarantool-perl (0.01-1) unstable; urgency=low
+
+  * Initial release. (Closes: #673646)
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sun, 20 May 2012 01:53:06 +0400
@@ -11,7 +11,9 @@ lib/DR/Tarantool/AsyncClient.pm
 lib/DR/Tarantool/CoroClient.pm
 lib/DR/Tarantool/Iterator.pm
 lib/DR/Tarantool/LLClient.pm
+lib/DR/Tarantool/LLSyncClient.pm
 lib/DR/Tarantool.pm
+lib/DR/Tarantool/RealSyncClient.pm
 lib/DR/Tarantool/Spaces.pm
 lib/DR/Tarantool/StartTest.pm
 lib/DR/Tarantool/SyncClient.pm
@@ -19,14 +21,25 @@ lib/DR/Tarantool/Tuple.pm
 Makefile.PL
 MANIFEST
 README.pod
+st/Check/OneHash.pm
+st/Check/OneTree.pm
+st/Check/Order.pm
+st/Check/Ping.pm
+st/Check/XlogCleanup.pm
+st/init.lua
+st/stress.cfg
+st/stress.pl
+st/stress.tarantool.cfg
 t/000-use.t
 t/010-xs.t
 t/020-low_level_client.t
+t/025-ll_synclient.t
 t/030-spaces.t
 t/033-iterator.t
 t/040-tuple.t
 t/050-async-client.t
 t/060-sync-client.t
+t/065-realsync-client.t
 t/070-coro-client.t
 t/080-tarantool.t
 t/090-parallel-requests.t
@@ -49,13 +62,5 @@ t/test-data/empty_tuple.00022-000-ok.bin
 t/test-data/init.lua
 t/test-data/llc-easy2.cfg
 t/test-data/llc-easy.cfg
-st/init.lua
-st/stress.pl
-st/stress.tarantool.cfg
-st/Check/Order.pm
-st/Check/XlogCleanup.pm
-st/Check/Ping.pm
-st/Check/OneTree.pm
-st/Check/OneHash.pm
-st/stress.cfg
-META.yml                                 Module meta-data (added by MakeMaker)
+META.yml                                 Module YAML meta-data (added by MakeMaker)
+META.json                                Module JSON meta-data (added by MakeMaker)
@@ -0,0 +1,58 @@
+{
+   "abstract" : "a Perl driver for L<Tarantool|http://tarantool.org>",
+   "author" : [
+      "Dmitry E. Oboukhov <unera@debian.org>"
+   ],
+   "dynamic_config" : 1,
+   "generated_by" : "ExtUtils::MakeMaker version 6.66, CPAN::Meta::Converter version 2.120921",
+   "license" : [
+      "artistic_1"
+   ],
+   "meta-spec" : {
+      "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
+      "version" : "2"
+   },
+   "name" : "DR-Tarantool",
+   "no_index" : {
+      "directory" : [
+         "t",
+         "inc"
+      ]
+   },
+   "prereqs" : {
+      "build" : {
+         "requires" : {
+            "ExtUtils::MakeMaker" : "0"
+         }
+      },
+      "configure" : {
+         "requires" : {
+            "ExtUtils::MakeMaker" : "0"
+         }
+      },
+      "runtime" : {
+         "requires" : {
+            "AnyEvent" : "0",
+            "Carp" : "0",
+            "Cwd" : "0",
+            "Devel::GlobalDestruction" : "0",
+            "File::Path" : "0",
+            "File::Spec::Functions" : "0",
+            "File::Temp" : "0",
+            "IO::Socket::INET" : "0",
+            "JSON::XS" : "0"
+         }
+      }
+   },
+   "release_status" : "stable",
+   "resources" : {
+      "bugtracker" : {
+         "web" : "https://github.com/dr-co/dr-tarantool/issues"
+      },
+      "homepage" : "https://github.com/dr-co/dr-tarantool",
+      "repository" : {
+         "url" : "https://github.com/dr-co/dr-tarantool"
+      }
+   },
+   "version" : "0.42"
+}
@@ -1,33 +1,34 @@
---- #YAML:1.0
-name:               DR-Tarantool
-version:            0.37
-abstract:           a Perl driver for L<Tarantool/Box|http://tarantool.org>
+---
+abstract: 'a Perl driver for L<Tarantool|http://tarantool.org>'
 author:
-    - Dmitry E. Oboukhov <unera@debian.org>
-license:            artistic
-distribution_type:  module
-configure_requires:
-    ExtUtils::MakeMaker:  0
+  - 'Dmitry E. Oboukhov <unera@debian.org>'
 build_requires:
-    ExtUtils::MakeMaker:  0
+  ExtUtils::MakeMaker: 0
+configure_requires:
+  ExtUtils::MakeMaker: 0
+dynamic_config: 1
+generated_by: 'ExtUtils::MakeMaker version 6.66, CPAN::Meta::Converter version 2.120921'
+license: artistic
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: DR-Tarantool
+no_index:
+  directory:
+    - t
+    - inc
 requires:
-    AnyEvent:             0
-    Carp:                 0
-    Cwd:                  0
-    Devel::GlobalDestruction:  0
-    File::Path:           0
-    File::Spec::Functions:  0
-    File::Temp:           0
-    IO::Socket::INET:     0
-    JSON::XS:             0
+  AnyEvent: 0
+  Carp: 0
+  Cwd: 0
+  Devel::GlobalDestruction: 0
+  File::Path: 0
+  File::Spec::Functions: 0
+  File::Temp: 0
+  IO::Socket::INET: 0
+  JSON::XS: 0
 resources:
-    bugtracker:  https://github.com/dr-co/dr-tarantool/issues
-    homepage:    https://github.com/dr-co/dr-tarantool
-no_index:
-    directory:
-        - t
-        - inc
-generated_by:       ExtUtils::MakeMaker version 6.57_05
-meta-spec:
-    url:      http://module-build.sourceforge.net/META-spec-v1.4.html
-    version:  1.4
+  bugtracker: https://github.com/dr-co/dr-tarantool/issues
+  homepage: https://github.com/dr-co/dr-tarantool
+  repository: https://github.com/dr-co/dr-tarantool
+version: 0.42
@@ -19,6 +19,7 @@ WriteMakefile(
     META_MERGE => {
         resources => {
             homepage   => 'https://github.com/dr-co/dr-tarantool',
+            repository => 'https://github.com/dr-co/dr-tarantool',
             bugtracker => 'https://github.com/dr-co/dr-tarantool/issues',
         }
     },
@@ -32,6 +33,6 @@ WriteMakefile(
     LICENSE           => 'artistic',
 );
 
-if (open my $fh, '>>', 'Makefile') {
-    print $fh "\n\nTEST_VERBOSE=1\n";
-}
+# if (open my $fh, '>>', 'Makefile') {
+#     print $fh "\n\nTEST_VERBOSE=1\n";
+# }
@@ -2,7 +2,7 @@ package DR::Tarantool;
 
 =head1 NAME
 
-DR::Tarantool - a Perl driver for L<Tarantool/Box|http://tarantool.org>
+DR::Tarantool - a Perl driver for L<Tarantool|http://tarantool.org>
 
 
 =head1 SYNOPSIS
@@ -46,10 +46,10 @@ DR::Tarantool - a Perl driver for L<Tarantool/Box|http://tarantool.org>
 =head1 DESCRIPTION
 
 This module provides a synchronous and asynchronous driver for
-L<Tarantool/Box|http://tarantool.org>.
+L<Tarantool|http://tarantool.org>.
 
 The driver does not have external dependencies, but includes the
-official light-weight Tarantool/Box C client (a single C header which
+official light-weight Tarantool C client (a single C header which
 implements all protocol formatting) for packing requests and unpacking
 server responses.
 
@@ -90,7 +90,7 @@ different coroutines are served concurrently.
 
 =back
 
-L<Tarantool/Box|http://tarantool.org> binary protocol
+L<Tarantool|http://tarantool.org> binary protocol
 contains no representation of database schema or tuple field types.
 Due to this deficiency, to easily integrate with Perl and automatically
 convert tuple fields to Perl values, the driver needs to know field names
@@ -212,7 +212,7 @@ Insert a field before the given field.
 
 =item delete
 
-Delete a field by index.
+Delete a field.
 
 =item push
 
@@ -232,7 +232,7 @@ Invoke a Lua stored procedure by name.
 
 =head2 Supported data types
 
-The driver supports all Tarantool/Box types (B<NUM>, B<NUM64>, B<STR>),
+The driver supports all Tarantool types (B<NUM>, B<NUM64>, B<STR>),
 as well as some client-only types, which are converted to the
 above server types automatically on the client:
 
@@ -260,7 +260,7 @@ L<JSON::XS> on insertion, and deserialized on selection.
 
 =back
 
-The basic data transfer unit in Tarantool/Box protocol is a single
+The basic data transfer unit in Tarantool protocol is a single
 tuple. A selected tuple is automatically wrapped into an instance
 of class L<DR::Tarantool::Tuple>. An object of this class can be
 used as an associative container, in which any field can be
@@ -301,7 +301,7 @@ our %EXPORT_TAGS = (
     constant    => [
         qw(
             TNT_INSERT TNT_SELECT TNT_UPDATE TNT_DELETE TNT_CALL TNT_PING
-            TNT_FLAG_RETURN TNT_FLAG_ADD TNT_FLAG_REPLACE TNT_FLAG_BOX_QUIET
+            TNT_FLAG_RETURN TNT_FLAG_ADD TNT_FLAG_REPLACE
         )
     ],
 );
@@ -309,13 +309,13 @@ our %EXPORT_TAGS = (
 our @EXPORT_OK = ( map { @$_ } values %EXPORT_TAGS );
 $EXPORT_TAGS{all} = \@EXPORT_OK;
 our @EXPORT = @{ $EXPORT_TAGS{client} };
-our $VERSION = '0.37';
+our $VERSION = '0.42';
 
 =head1 EXPORT
 
 =head2 tarantool
 
-connects to L<Tarantool/Box|http://tarantool.org> in synchronous mode
+connects to L<Tarantool|http://tarantool.org> in synchronous mode
 using L<DR::Tarantool::SyncClient>.
 
 =cut
@@ -330,6 +330,23 @@ sub tarantool {
 }
 
 
+=head2 rsync_tarantool
+
+connects to L<Tarantool|http://tarantool.org> in synchronous mode
+using L<DR::Tarantool::RealSyncClient>.
+
+=cut
+
+sub rsync_tarantool {
+    require DR::Tarantool::RealSyncClient;
+    no warnings 'redefine';
+    *rsync_tarantool = sub {
+        DR::Tarantool::RealSyncClient->connect(@_);
+    };
+    goto \&rsync_tarantool;
+}
+
+
 =head2 async_tarantool
 
 connects to L<tarantool|http://tarantool.org> in async mode using
@@ -409,7 +426,7 @@ Exports all functions and constants.
 
 =item *
 
-Support push, pop, delete in UPDATE.
+Support push, pop in UPDATE.
 
 =item *
 
@@ -19,6 +19,10 @@ inline static void hash_ssave(HV *h, const char *k, const char *v) {
 	hv_store( h, k, strlen(k), newSVpvn( v, strlen(v) ), 0 );
 }
 
+inline static void hash_scsave(HV *h, const char *k, SV *sv) {
+	hv_store( h, k, strlen(k), sv, 0);
+}
+
 inline static void hash_isave(HV *h, const char *k, uint32_t v) {
 	hv_store( h, k, strlen(k), newSViv( v ), 0 );
 }
@@ -48,19 +52,29 @@ inline static void tp_av_tuple(struct tp *req, AV *tuple) {
 }
 
 
-inline static void fetch_tuples( HV * ret, struct tp * rep ) {
-    AV * tuples = newAV();
-    hv_store( ret, "tuples", 6, newRV_noinc( ( SV * ) tuples ), 0 );
+inline static int fetch_tuples( HV * ret, struct tp * rep ) {
+	AV * tuples = newAV();
+	hv_store( ret, "tuples", 6, newRV_noinc( ( SV * ) tuples ), 0 );
+
+	int code;
+	while ( (code = tp_next(rep)) ) {
 
-    while ( tp_next(rep) ) {
-        AV * t = newAV();
-        av_push( tuples, newRV_noinc( ( SV * ) t ) );
+		if (code < 0)
+			return code;
 
-        while(tp_nextfield(rep)) {
-            SV * f = newSVpvn( tp_getfield(rep), tp_getfieldsize(rep) );
-            av_push( t, f );
-        }
-    }
+		AV * t = newAV();
+		av_push( tuples, newRV_noinc( ( SV * ) t ) );
+
+		while((code = tp_nextfield(rep))) {
+			if (code < 0)
+				return code;
+			SV * f = newSVpvn(
+				tp_getfield(rep), tp_getfieldsize(rep)
+			);
+			av_push( t, f );
+		}
+	}
+	return 0;
 }
 
 
@@ -338,9 +352,6 @@ HV * _pkt_parse_response( response )
 		if (code == -1) {
  			hash_ssave(RETVAL, "status", "buffer");
  			hash_ssave(RETVAL, "errstr", "Input data too short");
-//			hash_ssave(RETVAL, "status", "fatal");
-//			hash_ssave(RETVAL, "errstr",
-//					"Can't parse server response");
 		} else if (code >= 0) {
 			uint32_t type = tp_replyop(&rep);
 			hash_isave(RETVAL, "code", tp_replycode(&rep) );
@@ -349,8 +360,14 @@ HV * _pkt_parse_response( response )
 			hash_isave(RETVAL, "count", tp_replycount(&rep) );
 			if (code == 0) {
 			    if (type != TP_PING)
-			        fetch_tuples(RETVAL, &rep);
-                            hash_ssave(RETVAL, "status", "ok");
+			        code = fetch_tuples(RETVAL, &rep);
+			        if (code != 0) {
+ 					hash_ssave(RETVAL, "status", "buffer");
+ 					hash_ssave(RETVAL, "errstr",
+ 						"Broken response");
+				} else {
+					hash_ssave(RETVAL, "status", "ok");
+				}
 			} else {
 				hash_ssave(RETVAL, "status", "error");
 				size_t el = tp_replyerrorlen(&rep);
@@ -364,7 +381,7 @@ HV * _pkt_parse_response( response )
 					err = newSVpvn("", 0);
 				}
 
-				hv_stores(RETVAL, "errstr", err);
+				hash_scsave(RETVAL, "errstr", err);
 			}
 		}
 	OUTPUT:
@@ -412,25 +429,19 @@ unsigned TNT_SELECT()
 
 unsigned TNT_FLAG_RETURN()
 	CODE:
-		RETVAL = TP_FRET;
+		RETVAL = TP_BOX_RETURN_TUPLE;
 	OUTPUT:
 		RETVAL
 
 unsigned TNT_FLAG_ADD()
 	CODE:
-		RETVAL = TP_FADD;
+		RETVAL = TP_BOX_ADD;
 	OUTPUT:
 		RETVAL
 
 unsigned TNT_FLAG_REPLACE()
 	CODE:
-		RETVAL = TP_FREP;
-	OUTPUT:
-		RETVAL
-
-unsigned TNT_FLAG_BOX_QUIET()
-	CODE:
-		RETVAL = TP_FQUIET;
+		RETVAL = TP_BOX_REPLACE;
 	OUTPUT:
 		RETVAL
 
@@ -1,3 +1,34 @@
+libdr-tarantool-perl (0.42-1) unstable; urgency=low
+
+  * Rebuild for Debian perl 5.18.
+  * Fix some cpan tests (that are run without tarantool).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Wed, 18 Sep 2013 01:37:52 +0400
+
+libdr-tarantool-perl (0.41-1) unstable; urgency=low
+
+  * Fix broken xs-test (by cpan testers reports).
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Mon, 02 Sep 2013 15:37:11 +0400
+
+libdr-tarantool-perl (0.40-1) unstable; urgency=low
+
+  * Add RealSyncClient module.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Wed, 28 Aug 2013 17:00:13 +0400
+
+libdr-tarantool-perl (0.39-1) unstable; urgency=low
+
+  * Ping method works even connection isn't established.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Wed, 21 Aug 2013 17:33:08 +0400
+
+libdr-tarantool-perl (0.38-1) unstable; urgency=low
+
+  * Parser doesn't segfault if tarantool replies by broken package.
+
+ -- Dmitry E. Oboukhov <unera@debian.org>  Sat, 01 Jun 2013 21:23:23 +0400
+
 libdr-tarantool-perl (0.37-1) unstable; urgency=low
 
   * Update perldoc.
@@ -27,3 +27,6 @@ tarball:
 		-czf libdr-tarantool-perl_$(DEBVERSION).orig.tar.gz \
 		libdr-tarantool-perl-$(DEBVERSION)
 
+
+DEB_INSTALL_CHANGELOGS_ALL :=
+DEB_DH_INSTALLCHANGELOGS_ARGS := -XChanges
@@ -654,7 +654,7 @@ sub last_error_string { $_[0]->_llc->last_error_string }
 =head1 VCS
 
 The project is placed git repo on github:
-L<https://github.com/unera/dr-tarantool/>.
+L<https://github.com/dr-co/dr-tarantool/>.
 
 =cut
 
@@ -35,7 +35,7 @@ L<Tarantool|http://tarantool.org>
 
 =head2 connect
 
-Connects to Tarantool/Box.
+Connects to Tarantool.
 
 =head3 Arguments
 
@@ -118,7 +118,7 @@ my $LE = $] > 5.01 ? '<' : '';
 
 =head2 connect
 
-Creates a connection to L<Tarantool/Box| http://tarantool.org>
+Creates a connection to L<Tarantool| http://tarantool.org>
 
     DR::Tarantool::LLClient->connect(
         host => '127.0.0.1',
@@ -284,11 +284,42 @@ Ping the server.
 
 sub ping :method {
     my ($self, $cb) = @_;
-    $self->_check_cb( $cb );
     my $id = $self->_req_id;
+    $self->_check_cb( $cb );
     my $pkt = DR::Tarantool::_pkt_ping( $id );
-    $self->_request( $id, $pkt, $cb );
-    return;
+
+    if ($self->is_connected) {
+        $self->_request( $id, $pkt, $cb );
+        return;
+    }
+    
+    unless($self->{reconnect_period}) {
+        $cb->({
+                status  => 'fatal',
+                req_id  => $id,
+                errstr  => "Connection isn't established (yet)"
+            }
+        );
+        return;
+    }
+
+    my $this = $self;
+    weaken $this;
+
+    my $tmr;
+    $tmr = AE::timer $self->{reconnect_period}, 0, sub {
+        undef $tmr;
+        if ($this and $this->is_connected) {
+            $this->_request( $id, $pkt, $cb );
+            return;
+        }
+        $cb->({
+                status  => 'fatal',
+                req_id  => $id,
+                errstr  => "Connection isn't established (yet)"
+            }
+        );
+    };
 }
 
 
@@ -860,7 +891,7 @@ sub _check_operations {
 =head1 VCS
 
 The project is placed git repo on github:
-L<https://github.com/unera/dr-tarantool/>.
+L<https://github.com/dr-co/dr-tarantool/>.
 
 =cut
 
@@ -0,0 +1,268 @@
+use utf8;
+use strict;
+use warnings;
+
+package DR::Tarantool::LLSyncClient;
+use Carp;
+use IO::Socket::UNIX;
+use IO::Socket::INET;
+require DR::Tarantool;
+
+my $LE = $] > 5.01 ? '<' : '';
+
+$Carp::Internal{ (__PACKAGE__) }++;
+
+sub connect {
+    my ($class, %opts) = @_;
+
+    my $host = $opts{host} || 'localhost';
+    my $port = $opts{port} or croak 'port is undefined';
+
+    my $reconnect_period = $opts{reconnect_period} || 0;
+    my $reconnect_always = $opts{reconnect_always} || 0;
+
+
+    my $raise_error = 1;
+    if (exists $opts{raise_error}) {
+        $raise_error = $opts{raise_error} ? 1 : 0;
+    }
+
+    my $self = bless {
+        host                => $host,
+        port                => $port,
+        raise_error         => $raise_error,
+        reconnect_period    => $reconnect_period,
+        id                  => 0,
+    } => ref ($class) || $class;
+
+    unless ($self->_connect()) {
+        unless ($reconnect_always) {
+            return undef unless $self->{raise_error};
+            croak "Can't connect to $self->{host}:$self->{port}: $@";
+        }
+        unless ($reconnect_period) {
+            return undef unless $self->{raise_error};
+            croak "Can't connect to $self->{host}:$self->{port}: $@";
+        }
+    }
+    return $self;
+}
+
+
+sub _connect {
+    my ($self) = @_;
+
+    if ($self->{host} eq 'unix/' or $self->{port} =~ /\D/) {
+        return $self->{fh} = IO::Socket::UNIX->new(Peer => $self->{port});
+    } else {
+        return $self->{fh} = IO::Socket::INET->new(
+            PeerHost    => $self->{host},
+            PeerPort    => $self->{port},
+            Proto       => 'tcp',
+        );
+    }
+}
+
+sub _req_id {
+    my ($self) = @_;
+    return $self->{id}++ if $self->{id} < 0x7FFF_FFFE;
+    return $self->{id} = 0;
+}
+
+sub _request {
+    my ($self, $id, $pkt ) = @_;
+    until($self->{fh}) {
+        unless ($self->{reconnect_period}) {
+            $self->{last_error_string} = "Connection isn't established";
+            croak $self->{last_error_string} if $self->{raise_error};
+            return undef;
+        }
+        next if $self->_connect;
+        sleep $self->{reconnect_period};
+    }
+
+    my $len = length $pkt;
+
+    # send request
+    while($len > 0) {
+        no warnings; # closed socket
+        my $slen = syswrite $self->{fh}, $pkt;
+        goto SOCKET_ERROR unless defined $slen;
+        $len -= $slen;
+        substr $pkt, 0, $slen, '';
+    }
+
+    $pkt = '';
+    while(12 > length $pkt) {
+        no warnings; # closed socket
+        my $rl = sysread $self->{fh}, $pkt, 12 - length $pkt, length $pkt;
+        goto SOCKET_ERROR unless defined $rl;
+    }
+
+    my (undef, $blen) = unpack "L$LE L$LE", $pkt;
+
+    while(12 + $blen > length $pkt) {
+        no warnings; # closed socket
+        my $rl = sysread $self->{fh},
+            $pkt, 12 + $blen - length $pkt, length $pkt;
+        goto SOCKET_ERROR unless defined $rl;
+    }
+
+    my $res = DR::Tarantool::_pkt_parse_response( $pkt );
+    if ($res->{status} ne 'ok') {
+        $self->{last_error_string} = $res->{errstr};
+        $self->{last_code} = $res->{code};
+        # disconnect
+        delete $self->{fh} if $res->{status} =~ /^(fatal|buffer)$/;
+        croak $self->{last_error_string} if $self->{raise_error};
+        return undef;
+    }
+
+    $self->{last_error_string} = $res->{errstr} || '';
+    $self->{last_code} = $res->{code};
+    return $res;
+
+
+    SOCKET_ERROR:
+        delete $self->{fh};
+        $self->{last_error_string} = $!;
+        $self->{last_code} = undef;
+        croak $self->{last_error_string} if $self->{raise_error};
+        return undef;
+}
+
+sub ping :method {
+    my ($self) = @_;
+    unless ($self->{fh}) {
+        $self->_connect;
+        $self->{last_code} = -1;
+        $self->{last_error_string} = "Connection isn't established";
+        return 0 unless $self->{fh};
+    }
+    my $id = $self->_req_id;
+    my $pkt = DR::Tarantool::_pkt_ping( $id );
+    my $res = eval { $self->_request( $id, $pkt ); };
+    return 0 unless $res and $res->{status} eq 'ok';
+    return 1;
+}
+
+
+sub call_lua :method {
+
+    my $self = shift;
+    my $proc = shift;
+    my $tuple = shift;
+    $self->_check_tuple( $tuple );
+    my $flags = pop || 0;
+
+    my $id = $self->_req_id;
+    my $pkt = DR::Tarantool::_pkt_call_lua($id, $flags, $proc, $tuple);
+    return $self->_request( $id, $pkt );
+}
+
+sub select :method {
+    my $self = shift;
+    $self->_check_number(       my $ns = shift                  );
+    $self->_check_number(       my $idx = shift                 );
+    $self->_check_tuple_list(   my $keys = shift                );
+    $self->_check_number(       my $limit = shift || 0x7FFFFFFF );
+    $self->_check_number(       my $offset = shift || 0         );
+
+    my $id = $self->_req_id;
+    my $pkt =
+        DR::Tarantool::_pkt_select($id, $ns, $idx, $offset, $limit, $keys);
+    return $self->_request( $id, $pkt );
+}
+
+sub insert :method {
+
+    my $self = shift;
+    $self->_check_number(   my $space = shift       );
+    $self->_check_tuple(    my $tuple = shift       );
+    $self->_check_number(   my $flags = pop || 0    );
+    croak "insert: tuple must be ARRAYREF" unless ref $tuple eq 'ARRAY';
+    $flags ||= 0;
+    
+    my $id = $self->_req_id;
+    my $pkt = DR::Tarantool::_pkt_insert( $id, $space, $flags, $tuple );
+    return $self->_request( $id, $pkt );
+}
+
+sub update :method {
+
+    my $self = shift;
+    $self->_check_number(           my $ns = shift          );
+    $self->_check_tuple(            my $key = shift         );
+    $self->_check_operations(       my $operations = shift  );
+    $self->_check_number(           my $flags = pop || 0    );
+
+    my $id = $self->_req_id;
+    my $pkt = DR::Tarantool::_pkt_update($id, $ns, $flags, $key, $operations);
+    return $self->_request( $id, $pkt );
+}
+
+
+sub delete :method {
+    my $self = shift;
+    my $ns = shift;
+    my $key = shift;
+    $self->_check_tuple( $key );
+    my $flags = pop || 0;
+
+    my $id = $self->_req_id;
+    my $pkt = DR::Tarantool::_pkt_delete($id, $ns, $flags, $key);
+    return $self->_request( $id, $pkt );
+}
+
+
+
+sub _check_tuple {
+    my ($self, $tuple) = @_;
+    croak 'Tuple must be ARRAYREF' unless 'ARRAY' eq ref $tuple;
+}
+
+sub _check_tuple_list {
+    my ($self, $list) = @_;
+    croak 'Tuplelist must be ARRAYREF of ARRAYREF' unless 'ARRAY' eq ref $list;
+    croak 'Tuplelist is empty' unless @$list;
+    $self->_check_tuple($_) for @$list;
+}
+
+sub _check_number {
+    my ($self, $number) = @_;
+    croak "argument must be number"
+        unless defined $number and $number =~ /^\d+$/;
+}
+
+sub _check_operation {
+    my ($self, $op) = @_;
+    croak 'Operation must be ARRAYREF' unless 'ARRAY' eq ref $op;
+    croak 'Wrong update operation: too short arglist' unless @$op >= 2;
+    croak "Wrong operation: $op->[1]"
+        unless $op->[1] and
+            $op->[1] =~ /^(delete|set|insert|add|and|or|xor|substr)$/;
+    $self->_check_number($op->[0]);
+}       
+
+sub _check_operations {
+    my ($self, $list) = @_;
+    croak 'Operations list must be ARRAYREF of ARRAYREF'
+        unless 'ARRAY' eq ref $list;
+    croak 'Operations list is empty' unless @$list;
+    $self->_check_operation( $_ ) for @$list;
+}
+
+
+sub last_error_string {
+    return $_[0]->{last_error_string};
+}
+
+sub last_code {
+    return $_[0]->{last_code};
+}
+
+sub raise_error {
+    return $_[0]->{raise_error};
+}
+
+1;
@@ -0,0 +1,249 @@
+use utf8;
+use strict;
+use warnings;
+
+package DR::Tarantool::RealSyncClient;
+
+
+=head1 NAME
+
+DR::Tarantool::RealSyncClient - a synchronous driver for L<Tarantool/Box|http://tarantool.org>
+
+=head1 SYNOPSIS
+
+    my $client = DR::Tarantool::RealSyncClient->connect(
+        port    => $tnt->primary_port,
+        spaces  => $spaces
+    );
+
+    if ($client->ping) { .. };
+
+    my $t = $client->insert(
+        first_space => [ 1, 'val', 2, 'test' ], TNT_FLAG_RETURN
+    );
+
+    $t = $client->call_lua('luafunc' =>  [ 0, 0, 1 ], 'space_name');
+
+    $t = $client->select(space_name => $key);
+
+    $t = $client->update(space_name => 2 => [ name => set => 'new' ]);
+
+    $client->delete(space_name => $key);
+
+
+=head1 DESCRIPTION
+
+The module is a clone of L<DR::Tarantool::SyncClient> but it doesn't
+use L<AnyEvent> or L<Coro>.
+
+The module uses L<IO::Socket> sockets.
+
+=head1 COPYRIGHT AND LICENSE
+
+ Copyright (C) 2011 Dmitry E. Oboukhov <unera@debian.org>
+ Copyright (C) 2011 Roman V. Nikolaev <rshadow@rambler.ru>
+
+ This program is free software, you can redistribute it and/or
+ modify it under the terms of the Artistic License.
+
+=head1 VCS
+
+The project is placed git repo on github:
+L<|https://github.com/dr-co/dr-tarantool/>.
+
+=cut
+
+use DR::Tarantool::LLSyncClient;
+use DR::Tarantool::Spaces;
+use DR::Tarantool::Tuple;
+use Carp;
+$Carp::Internal{ (__PACKAGE__) }++;
+use Data::Dumper;
+use Scalar::Util 'blessed';
+
+my $unpack = sub {
+    my ($self, $res, $s) = @_;
+    return undef unless $res and $res->{status} eq 'ok';
+    return $s->tuple_class->unpack( $res->{tuples}, $s ) if $s;
+    return $res->{tuples};
+};
+
+sub connect {
+    my ($class, %opts) = @_;
+
+    my $host = $opts{host} || 'localhost';
+    my $port = $opts{port} or croak "port isn't defined";
+
+    my $spaces = blessed($opts{spaces}) ?
+        $opts{spaces} : DR::Tarantool::Spaces->new($opts{spaces});
+    my $reconnect_period    = $opts{reconnect_period} || 0;
+    my $reconnect_always    = $opts{reconnect_always} || 0;
+
+    my $client = DR::Tarantool::LLSyncClient->connect(
+        host                => $host,
+        port                => $port,
+        reconnect_period    => $reconnect_period,
+        reconnect_always    => $reconnect_always,
+        exists($opts{raise_error}) ?
+            (   raise_error => $opts{raise_error} ?  1: 0 )
+            : (),
+    );
+
+
+    return undef unless $client;
+    return bless { llc => $client, spaces => $spaces } => ref($class) || $class;
+}
+
+sub space {
+    my ($self, $name) = @_;
+    return $self->{spaces}->space($name);
+}
+
+
+sub ping {
+    my ($self) = @_;
+    $self->{llc}->ping;
+}
+
+sub insert {
+    my $self = shift;
+    my $space = shift;
+    $self->_llc->_check_tuple( my $tuple = shift );
+    my $flags = pop || 0;
+
+    my $s = $self->{spaces}->space($space);
+
+    my $res =
+        $self->_llc->insert( $s->number, $s->pack_tuple( $tuple ), $flags );
+    return $unpack->($self, $res, $s);
+}
+
+sub call_lua {
+    my $self = shift;
+    my $lua_name = shift;
+    my $args = shift;
+
+    unshift @_ => 'space' if @_ == 1;
+    my %opts = @_;
+
+    my $flags = $opts{flags} || 0;
+    my $space_name = $opts{space};
+    my $fields = $opts{fields};
+
+    my $s;
+    croak "You can't use 'fields' and 'space' at the same time"
+        if $fields and $space_name;
+
+    if ($space_name) {
+        $s = $self->space( $space_name );
+    } elsif ( $fields ) {
+        $s = DR::Tarantool::Space->new(
+            0 =>
+            {
+                name    => 'temp_space',
+                fields  => $fields,
+                indexes => {}
+            },
+        );
+    } else {
+        $s = DR::Tarantool::Space->new(
+            0 =>
+            {
+                name            => 'temp_space',
+                fields          => [],
+                indexes         => {}
+            },
+        );
+    }
+
+    if ($opts{args}) {
+        my $sa = DR::Tarantool::Space->new(
+            0 =>
+            {
+                name    => 'temp_space_args',
+                fields  => $opts{args},
+                indexes => {}
+            },
+        );
+        $args = $sa->pack_tuple( $args );
+    }
+
+    my $res = $self->_llc->call_lua( $lua_name, $args, $flags );
+
+    return $unpack->($self, $res, $s);
+}
+
+
+sub select {
+    my $self = shift;
+    my $space = shift;
+    my $keys = shift;
+
+    my ($index, $limit, $offset);
+
+    if (@_ == 1) {
+        $index = shift;
+    } elsif (@_ == 3) {
+        ($index, $limit, $offset) = @_;
+    } elsif (@_) {
+        my %opts = @_;
+        $index = $opts{index};
+        $limit = $opts{limit};
+        $offset = $opts{offset};
+    }
+
+    $index ||= 0;
+
+    my $s = $self->space($space);
+
+    my $res = $self->_llc->select(
+        $s->number,
+        $s->_index( $index )->{no},
+        $s->pack_keys( $keys, $index ),
+        $limit,
+        $offset
+    );
+
+    return $unpack->($self, $res, $s);
+}
+
+sub update {
+    my $self = shift;
+    my $space = shift;
+    my $key = shift;
+    my $op = shift;
+    my $flags = shift || 0;
+
+    my $s = $self->space($space);
+
+    my $res = $self->_llc->update(
+        $s->number,
+        $s->pack_primary_key( $key ),
+        $s->pack_operations( $op ),
+        $flags,
+    );
+    return $unpack->($self, $res, $s);
+}
+
+sub delete :method {
+    my $self = shift;
+    my $space = shift;
+    my $key = shift;
+    my $flags = shift || 0;
+
+    my $s = $self->space($space);
+
+    my $res = $self->_llc->delete(
+        $s->number,
+        $s->pack_primary_key( $key ),
+        $flags,
+    );
+    return $unpack->($self, $res, $s);
+}
+
+sub last_code { $_[0]->{llc}->last_code }
+sub last_error_string { $_[0]->{llc}->last_error_string }
+sub raise_error { $_[0]->raise_error };
+sub _llc { $_[0]{llc} }
+
+1;
@@ -10,7 +10,7 @@ my $LE = $] > 5.01 ? '<' : '';
 
 =head1 NAME
 
-DR::Tarantool::Spaces - Tarantool/Box schema description 
+DR::Tarantool::Spaces - Tarantool schema description 
 
 =head1 SYNOPSIS
 
@@ -687,7 +687,7 @@ sub pack_operations {
 =head1 VCS
 
 The project is placed git repo on github:
-L<https://github.com/unera/dr-tarantool/>.
+L<https://github.com/dr-co/dr-tarantool/>.
 
 =cut
 
@@ -13,7 +13,7 @@ use POSIX ();
 
 =head1 NAME
 
-DR::Tarantool::StartTest - finds and starts Tarantool/Box on free port.
+DR::Tarantool::StartTest - finds and starts Tarantool on free port.
 
 =head1 SYNOPSIS
 
@@ -72,7 +72,7 @@ sub run {
 
 =head2 started
 
-Return true if Tarantool/Box is found and started
+Return true if Tarantool is found and started
 
 =cut
 
@@ -84,7 +84,7 @@ sub started {
 
 =head2 log
 
-Return Tarantool/Box logs
+Return Tarantool logs
 
 =cut
 
@@ -168,7 +168,7 @@ sub _restart {
     $self->{started} = 1;
 
 
-    # wait for starting Tarantool/Box
+    # wait for starting Tarantool
     for (my $i = 0; $i < 100; $i++) {
         last if IO::Socket::INET->new(
             PeerAddr => '127.0.0.1', PeerPort => $self->primary_port
@@ -176,6 +176,13 @@ sub _restart {
 
         sleep 0.01;
     }
+
+    for (my $i = 0; $i < 100; $i++) {
+        last if $self->log =~ /entering event loop/;
+        sleep 0.01;
+    }
+
+    sleep 1 unless $self->log =~ /entering event loop/;
 }
 
 sub restart {
@@ -186,7 +193,7 @@ sub restart {
 
 =head2 primary_port
 
-Return Tarantool/Box primary port
+Return Tarantool primary port
 
 =cut
 
@@ -195,7 +202,7 @@ sub primary_port { return $_[0]->{primary_port} }
 
 =head2 admin_port
 
-Return Tarantool/Box admin port
+Return Tarantool admin port
 
 =cut
 
@@ -213,7 +220,7 @@ sub tarantool_pid { return $_[0]->{child} }
 
 =head2 kill
 
-Kills Tarantool/Box
+Kills Tarantool
 
 =cut
 
@@ -232,7 +239,7 @@ sub kill :method {
 
 =head2 is_dead
 
-Return true if child Tarantool/Box process is dead.
+Return true if child Tarantool process is dead.
 
 =cut
 
@@ -309,7 +316,7 @@ sub clean_xlogs {
 =head1 VCS
 
 The project is placed git repo on github:
-L<https://github.com/unera/dr-tarantool/>.
+L<https://github.com/dr-co/dr-tarantool/>.
 
 =cut
 
@@ -12,7 +12,7 @@ $Carp::Internal{ (__PACKAGE__) }++;
 =head1 NAME
 
 DR::Tarantool::SyncClient - a synchronous driver for
-L<Tarantool/Box|http://tarantool.org>.
+L<Tarantool|http://tarantool.org>.
 
 =head1 SYNOPSIS
 
@@ -175,7 +175,7 @@ sub DESTROY {
 =head1 VCS
 
 The project is placed git repo on github:
-L<https://github.com/unera/dr-tarantool/>.
+L<https://github.com/dr-co/dr-tarantool/>.
 
 =cut
 
@@ -265,7 +265,7 @@ sub DESTROY {  }
 =head1 VCS
 
 The project is placed git repo on github:
-L<https://github.com/unera/dr-tarantool/>.
+L<https://github.com/dr-co/dr-tarantool/>.
 
 =cut
 
@@ -2,7 +2,7 @@ package DR::Tarantool;
 
 =head1 NAME
 
-DR::Tarantool - a Perl driver for L<Tarantool/Box|http://tarantool.org>
+DR::Tarantool - a Perl driver for L<Tarantool|http://tarantool.org>
 
 
 =head1 SYNOPSIS
@@ -46,10 +46,10 @@ DR::Tarantool - a Perl driver for L<Tarantool/Box|http://tarantool.org>
 =head1 DESCRIPTION
 
 This module provides a synchronous and asynchronous driver for
-L<Tarantool/Box|http://tarantool.org>.
+L<Tarantool|http://tarantool.org>.
 
 The driver does not have external dependencies, but includes the
-official light-weight Tarantool/Box C client (a single C header which
+official light-weight Tarantool C client (a single C header which
 implements all protocol formatting) for packing requests and unpacking
 server responses.
 
@@ -90,7 +90,7 @@ different coroutines are served concurrently.
 
 =back
 
-L<Tarantool/Box|http://tarantool.org> binary protocol
+L<Tarantool|http://tarantool.org> binary protocol
 contains no representation of database schema or tuple field types.
 Due to this deficiency, to easily integrate with Perl and automatically
 convert tuple fields to Perl values, the driver needs to know field names
@@ -212,7 +212,7 @@ Insert a field before the given field.
 
 =item delete
 
-Delete a field by index.
+Delete a field.
 
 =item push
 
@@ -232,7 +232,7 @@ Invoke a Lua stored procedure by name.
 
 =head2 Supported data types
 
-The driver supports all Tarantool/Box types (B<NUM>, B<NUM64>, B<STR>),
+The driver supports all Tarantool types (B<NUM>, B<NUM64>, B<STR>),
 as well as some client-only types, which are converted to the
 above server types automatically on the client:
 
@@ -260,7 +260,7 @@ L<JSON::XS> on insertion, and deserialized on selection.
 
 =back
 
-The basic data transfer unit in Tarantool/Box protocol is a single
+The basic data transfer unit in Tarantool protocol is a single
 tuple. A selected tuple is automatically wrapped into an instance
 of class L<DR::Tarantool::Tuple>. An object of this class can be
 used as an associative container, in which any field can be
@@ -301,7 +301,7 @@ our %EXPORT_TAGS = (
     constant    => [
         qw(
             TNT_INSERT TNT_SELECT TNT_UPDATE TNT_DELETE TNT_CALL TNT_PING
-            TNT_FLAG_RETURN TNT_FLAG_ADD TNT_FLAG_REPLACE TNT_FLAG_BOX_QUIET
+            TNT_FLAG_RETURN TNT_FLAG_ADD TNT_FLAG_REPLACE
         )
     ],
 );
@@ -309,13 +309,13 @@ our %EXPORT_TAGS = (
 our @EXPORT_OK = ( map { @$_ } values %EXPORT_TAGS );
 $EXPORT_TAGS{all} = \@EXPORT_OK;
 our @EXPORT = @{ $EXPORT_TAGS{client} };
-our $VERSION = '0.37';
+our $VERSION = '0.42';
 
 =head1 EXPORT
 
 =head2 tarantool
 
-connects to L<Tarantool/Box|http://tarantool.org> in synchronous mode
+connects to L<Tarantool|http://tarantool.org> in synchronous mode
 using L<DR::Tarantool::SyncClient>.
 
 =cut
@@ -330,6 +330,23 @@ sub tarantool {
 }
 
 
+=head2 rsync_tarantool
+
+connects to L<Tarantool|http://tarantool.org> in synchronous mode
+using L<DR::Tarantool::RealSyncClient>.
+
+=cut
+
+sub rsync_tarantool {
+    require DR::Tarantool::RealSyncClient;
+    no warnings 'redefine';
+    *rsync_tarantool = sub {
+        DR::Tarantool::RealSyncClient->connect(@_);
+    };
+    goto \&rsync_tarantool;
+}
+
+
 =head2 async_tarantool
 
 connects to L<tarantool|http://tarantool.org> in async mode using
@@ -409,7 +426,7 @@ Exports all functions and constants.
 
 =item *
 
-Support push, pop, delete in UPDATE.
+Support push, pop in UPDATE.
 
 =item *
 
@@ -7,7 +7,7 @@ use open qw(:std :utf8);
 use lib qw(lib ../lib);
 use lib qw(blib/lib blib/arch ../blib/lib ../blib/arch);
 
-use Test::More tests    => 216;
+use Test::More tests    => 335;
 use Encode qw(decode encode);
 
 
@@ -32,7 +32,7 @@ like TNT_PING,              qr{^\d+$}, 'TNT_PING';
 like TNT_FLAG_RETURN,       qr{^\d+$}, 'TNT_FLAG_RETURN';
 like TNT_FLAG_ADD,          qr{^\d+$}, 'TNT_FLAG_ADD';
 like TNT_FLAG_REPLACE,      qr{^\d+$}, 'TNT_FLAG_REPLACE';
-like TNT_FLAG_BOX_QUIET,    qr{^\d+$}, 'TNT_FLAG_BOX_QUIET';
+# like TNT_FLAG_BOX_QUIET,    qr{^\d+$}, 'TNT_FLAG_BOX_QUIET';
 # like TNT_FLAG_NOT_STORE,    qr{^\d+$}, 'TNT_FLAG_NOT_STORE';
 
 my $LE = $] > 5.01 ? '<' : '';
@@ -200,3 +200,58 @@ for my $bin (sort @bins) {
     }
 }
 
+SKIP: {
+#     skip 'Devel tests $ENV{DEVEL_TEST}=0', 120 unless $ENV{DEVEL_TEST};
+for (1 .. 30) {
+    my $body = join '', map { chr int rand 256 } 1 .. (300 + int rand 300);
+    my $pkt =
+        pack 'LLLLa*',
+            TNT_SELECT,
+            length $body,
+            int rand 500,
+            0,
+            $body
+        ;
+    $res = DR::Tarantool::_pkt_parse_response( $pkt );
+    diag explain $res unless
+    is $res->{status}, 'buffer', "Broken package $_";
+    $pkt =
+        pack 'LLLLa*',
+            TNT_SELECT,
+            10 + length $body,
+            int rand 500,
+            0,
+            $body
+        ;
+    $res = DR::Tarantool::_pkt_parse_response( $pkt );
+    diag explain $res unless
+    is $res->{status}, 'buffer', "Broken package $_, too long body";
+    
+    $pkt =
+        pack 'LLLLa*',
+            TNT_SELECT,
+            -10 + length $body,
+            int rand 500,
+            0,
+            $body
+        ;
+    $res = DR::Tarantool::_pkt_parse_response( $pkt );
+    diag explain $res unless
+    is $res->{status}, 'buffer', "Broken package $_, too short body";
+    
+    $pkt =
+        pack 'LLLLa*',
+            TNT_SELECT,
+            5 + int rand 500,
+            5 + int rand 500,
+            0,
+            ''
+        ;
+
+    my $pkth = join '', map { sprintf '.%02x', ord $_ } split //, $pkt;
+    
+    $res = DR::Tarantool::_pkt_parse_response( $pkt );
+    diag explain [ $res, $pkth, TNT_SELECT ] unless
+    is $res->{status}, 'buffer', "Broken package $_, zero length body";
+}
+}
@@ -0,0 +1,189 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+use utf8;
+use open qw(:std :utf8);
+use lib qw(lib ../lib);
+use lib qw(blib/lib blib/arch ../blib/lib ../blib/arch);
+
+use constant PLAN   => 64;
+use Test::More tests    => PLAN;
+use Encode qw(decode encode);
+
+
+BEGIN {
+    # Подготовка объекта тестирования для работы с utf8
+    my $builder = Test::More->builder;
+    binmode $builder->output,         ":utf8";
+    binmode $builder->failure_output, ":utf8";
+    binmode $builder->todo_output,    ":utf8";
+
+    use_ok 'DR::Tarantool::LLSyncClient';
+    use_ok 'DR::Tarantool::StartTest';
+    use_ok 'File::Spec::Functions', 'catfile';
+    use_ok 'File::Basename', 'dirname';
+    use_ok 'DR::Tarantool', ':constant';
+}
+my $LE = $] > 5.01 ? '<' : '';
+
+
+my $cfg_dir = catfile dirname(__FILE__), 'test-data';
+ok -d $cfg_dir, 'directory with test data';
+my $tcfg = catfile $cfg_dir, 'llc-easy.cfg';
+ok -r $tcfg, $tcfg;
+
+my $tnt = run DR::Tarantool::StartTest( cfg => $tcfg );
+
+SKIP: {
+    unless ($tnt->started and !$ENV{SKIP_TNT}) {
+        diag $tnt->log unless $ENV{SKIP_TNT};
+        skip "tarantool isn't started", PLAN - 7;
+    }
+
+    my $client = DR::Tarantool::LLSyncClient->connect(
+        port                => $tnt->primary_port,
+        reconnect_period    => 0.1
+    );
+
+
+    note 'ping';
+    ok $client->ping, 'ping';
+    close $client->{fh};
+    ok !$client->ping, 'ping disconnected';
+
+    note 'call_lua';
+    {
+        my $res = $client->call_lua('box.dostring', [ 'return "123", "abc"' ]);
+        isa_ok $res => 'HASH';
+        is $res->{status}, 'ok', 'status';
+        is_deeply $res->{tuples}, [[123],['abc']], 'tuples';
+        is $res->{code}, 0, 'code';
+        is $client->last_code, $res->{code}, 'code';
+        is $client->last_error_string, '', 'error';
+        is $res->{count}, 2, '2 tuples';
+        is $res->{type}, TNT_CALL, 'type';
+    }
+    {
+        my $res = eval {
+            $client->call_lua('box.dostring', [ 'error("abc")' ]); ## LINE1
+        };
+        like $@, qr{Lua error}, 'Error';
+    }
+
+
+    note 'insert';
+    {
+        for (1 .. 2) {
+            my $res = $client->insert(0,
+                [ pack("L$LE", 1), 'abc', pack "L$LE", $_ ],
+                TNT_FLAG_RETURN
+            );
+            isa_ok $res => 'HASH';
+            is $res->{status}, 'ok', 'status';
+            is $res->{type}, TNT_INSERT, 'type';
+            is $res->{count}, 1, 'count';
+            is_deeply $res->{tuples},
+                [[pack("L$LE", 1), 'abc', pack "L$LE", $_]], 'tuples';
+        }
+        my $res = eval {
+            $client->insert(0,
+                [ pack("L$LE", 1), 'abc', pack "L$LE", 1234 ],
+                TNT_FLAG_RETURN | TNT_FLAG_ADD
+            );
+        };
+        is $res, undef, 'no results';
+        like $@, qr{Duplicate key exists}, 'Error message';
+        ok $client->last_code, 'last_code';
+        like $client->last_error_string, qr{Duplicate key exists},
+            'Error message';
+    }
+
+    note 'select';
+    {
+        my $res = $client->select(0, 0, [[ pack("L$LE", 1) ]], 2, 0);
+        isa_ok $res => 'HASH';
+        is $res->{status}, 'ok', 'status';
+        is $res->{type}, TNT_SELECT, 'type';
+        is $res->{count}, 1, 'count';
+        is_deeply $res->{tuples}, [[pack("L$LE", 1), 'abc', pack "L$LE", 2]],
+            'tuples';
+    }
+    {
+        my $res = $client->select(0, 0, [[ pack("L$LE", 2) ]], 2, 0);
+        isa_ok $res => 'HASH';
+        is $res->{status}, 'ok', 'status';
+        is $res->{type}, TNT_SELECT, 'type';
+        is $res->{count}, 0, 'count';
+        is_deeply $res->{tuples}, [], 'tuples';
+    }
+
+    note 'update';
+    {
+        my $res = $client->update(
+            0, # ns
+            [ pack "L$LE", 1 ], # keys
+            [
+                [ 1 => set      => 'abcdef' ],
+                [ 1 => substr   => 2, 2, ],
+                [ 1 => substr   => 100, 1, 'tail' ],
+                [ 2 => 'delete' ],
+                [ 2 => insert   => pack "L$LE" => 123 ],
+                [ 3 => insert   => 'third' ],
+                [ 4 => insert   => 'fourth' ],
+            ],
+            TNT_FLAG_RETURN, # flags
+        );
+
+        is $res->{code}, 0, '* update reply code';
+        is $res->{status}, 'ok', 'status';
+        is $res->{type}, TNT_UPDATE, 'type';
+        is $client->last_code, $res->{code}, 'operation code';
+        is $client->last_error_string, $res->{errstr} // '',
+            'operation errstr';
+
+        is $res->{tuples}[0][1], 'abeftail',
+            'updated tuple 1';
+        is $res->{tuples}[0][2], (pack "L$LE", 123),
+            'updated tuple 2';
+        is $res->{tuples}[0][3], 'third', 'updated tuple 3';
+        is $res->{tuples}[0][4], 'fourth', 'updated tuple 4';
+
+        $res = $client->update(
+            0,
+            [ pack "L$LE", 1 ],
+            [
+                [ 1 => set => '123' ]
+            ]
+        );
+        is $res->{code}, 0, 'update reply code';
+        is $res->{status}, 'ok', 'status';
+        is $res->{type}, TNT_UPDATE, 'type';
+        is $client->last_code, $res->{code}, 'operation code';
+        is $client->last_error_string, $res->{errstr} // '',
+            'operation errstr';
+        is $res->{count}, 1, 'count';
+        is_deeply $res->{tuples}, [], 'no tuples';
+    }
+
+    note 'delete';
+
+    {
+        my $res = $client->delete(
+            0, # ns
+            [ pack "L$LE", 1 ], # keys
+            TNT_FLAG_RETURN, # flags
+        );
+        
+        is $res->{code}, 0, '* delete reply code';
+        is $res->{status}, 'ok', 'status';
+        is $res->{type}, TNT_DELETE, 'type';
+        is $client->last_code, $res->{code}, 'operation code';
+        is $client->last_error_string, $res->{errstr} // '',
+            'operation errstr';
+
+        is $res->{tuples}[0][1], '123',
+            'deleted tuple[1]';
+    }
+
+}
@@ -0,0 +1,192 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+use utf8;
+use open qw(:std :utf8);
+use lib qw(lib ../lib);
+use lib qw(blib/lib blib/arch ../blib/lib ../blib/arch);
+
+use constant PLAN       => 59;
+use Test::More tests    => PLAN;
+use Encode qw(decode encode);
+
+my $LE = $] > 5.01 ? '<' : '';
+
+BEGIN {
+    # Подготовка объекта тестирования для работы с utf8
+    my $builder = Test::More->builder;
+    binmode $builder->output,         ":utf8";
+    binmode $builder->failure_output, ":utf8";
+    binmode $builder->todo_output,    ":utf8";
+
+    use_ok 'DR::Tarantool::LLClient', 'tnt_connect';
+    use_ok 'DR::Tarantool::StartTest';
+    use_ok 'DR::Tarantool', ':constant';
+    use_ok 'File::Spec::Functions', 'catfile';
+    use_ok 'File::Basename', 'dirname', 'basename';
+    use_ok 'AnyEvent';
+    use_ok 'DR::Tarantool::RealSyncClient';
+}
+
+my $cfg_dir = catfile dirname(__FILE__), 'test-data';
+ok -d $cfg_dir, 'directory with test data';
+my $tcfg = catfile $cfg_dir, 'llc-easy2.cfg';
+ok -r $tcfg, $tcfg;
+
+my $tnt = run DR::Tarantool::StartTest( cfg => $tcfg );
+
+my $spaces = {
+    0   => {
+        name            => 'first_space',
+        fields  => [
+            {
+                name    => 'id',
+                type    => 'NUM',
+            },
+            {
+                name    => 'name',
+                type    => 'UTF8STR',
+            },
+            {
+                name    => 'key',
+                type    => 'NUM',
+            },
+            {
+                name    => 'password',
+                type    => 'STR',
+            },
+            {
+                name    => 'json',
+                type    => 'JSON',
+            }
+        ],
+        indexes => {
+            0   => 'id',
+            1   => 'name',
+            2   => [ 'key', 'password' ],
+        },
+    }
+};
+
+SKIP: {
+    unless ($tnt->started and !$ENV{SKIP_TNT}) {
+        diag $tnt->log unless $ENV{SKIP_TNT};
+        skip "tarantool isn't started", PLAN - 9;
+    }
+
+    my $client = DR::Tarantool::RealSyncClient->connect(
+        port    => $tnt->primary_port,
+        spaces  => $spaces
+    );
+
+    isa_ok $client => 'DR::Tarantool::RealSyncClient';
+    is $client->last_code, undef, 'last_code';
+    is $client->last_error_string, undef, 'last_error_string';
+
+    ok $client->ping, '* ping';
+
+    my $t = $client->insert(
+        first_space => [ 1, 'привет', 2, 'test' ], TNT_FLAG_RETURN
+    );
+
+    isa_ok $t => 'DR::Tarantool::Tuple', '* insert tuple packed';
+    is $t->id, 1, 'id';
+    is $t->name, 'привет', 'name';
+    is $t->key, 2, 'key';
+    is $t->password, 'test', 'password';
+
+    $t = $client->insert(
+        first_space => [ 2, 'медвед', 3, 'test2' ], TNT_FLAG_RETURN
+    );
+
+    isa_ok $t => 'DR::Tarantool::Tuple', 'insert tuple packed';
+    is $t->id, 2, 'id';
+    is $t->name, 'медвед', 'name';
+    is $t->key,  3, 'key';
+    is $t->password, 'test2', 'password';
+
+
+    $t = $client->call_lua('box.select' =>
+        [ 0, 0, pack "L$LE" => 1 ], 'first_space');
+    isa_ok $t => 'DR::Tarantool::Tuple', '* call tuple packed';
+    is $t->id, 1, 'id';
+    is $t->name, 'привет', 'name';
+    is $t->key, 2, 'key';
+    is $t->password, 'test', 'password';
+
+
+    $t = $client->select(first_space => 1);
+    isa_ok $t => 'DR::Tarantool::Tuple', '* select tuple packed';
+    is $t->id, 1, 'id';
+    is $t->name, 'привет', 'name';
+    is $t->key, 2, 'key';
+    is $t->password, 'test', 'password';
+
+    $t = $client->select(first_space => 'привет', 'i1');
+    isa_ok $t => 'DR::Tarantool::Tuple', 'select tuple packed (i1)';
+    is $t->id, 1, 'id';
+    is $t->name, 'привет', 'name';
+    is $t->key, 2, 'key';
+    is $t->password, 'test', 'password';
+
+    $t = $client->select(first_space => [[2, 'test']], 'i2');
+    isa_ok $t => 'DR::Tarantool::Tuple', 'select tuple packed (i2)';
+    is $t->id, 1, 'id';
+    is $t->name, 'привет', 'name';
+    is $t->key, 2, 'key';
+    is $t->password, 'test', 'password';
+
+    $t = $client->update(first_space => 2 => [ name => set => 'привет1' ]);
+    is $t, undef, '* update without flags';
+    $t = $client->update(
+        first_space => 2 => [ name => set => 'привет медвед' ], TNT_FLAG_RETURN
+    );
+    isa_ok $t => 'DR::Tarantool::Tuple', 'update with flags';
+    is $t->name, 'привет медвед', '$t->name';
+
+
+    $t = $client->insert(first_space => [1, 2, 3, 4, undef], TNT_FLAG_RETURN);
+    is $t->json, undef, 'JSON insert: undef';
+
+    $t = $client->insert(first_space => [1, 2, 3, 4, 22], TNT_FLAG_RETURN);
+    is $t->json, 22, 'JSON insert: scalar';
+
+    $t = $client->insert(first_space => [1, 2, 3, 4, 'тест'], TNT_FLAG_RETURN);
+    is $t->json, 'тест', 'JSON insert: utf8 scalar';
+
+    $t = $client->insert(
+        first_space => [ 1, 2, 3, 4, { a => 'b' } ], TNT_FLAG_RETURN
+    );
+    isa_ok $t->json => 'HASH', 'JSON insert: hash';
+    is $t->json->{a}, 'b', 'JSON insert: hash value';
+
+    ok !eval {
+        $client->insert(
+            first_space => [ 1 .. 10 ], TNT_FLAG_RETURN | TNT_FLAG_ADD
+        );
+        1
+    }, 'raise error';
+    like $@, qr{Duplicate key exists|Tuple already exists}, 'error message';
+
+    {
+        local $client->{llc}{raise_error};
+        ok eval {
+            $client->insert(
+                first_space => [ 1 .. 10 ], TNT_FLAG_RETURN | TNT_FLAG_ADD
+            );
+            1
+        }, 'no raise error';
+        like $client->last_error_string,
+            qr{Duplicate key exists|Tuple already exists}, 'error message';
+    }
+
+    $t = $client->insert(
+        first_space => [ 1, 2, 3, 4, { привет => 'медвед' } ], TNT_FLAG_RETURN
+    );
+    isa_ok $t->json => 'HASH', 'JSON insert: hash';
+    is $t->json->{привет}, 'медвед', 'JSON insert: hash utf8 value';
+
+    ok $t = $client->delete(first_space => [ 1 ], TNT_FLAG_RETURN), 'delete';
+    is $t->json->{привет}, 'медвед', 'JSON delete: hash utf8 value';
+}
@@ -141,7 +141,7 @@ SKIP: {
                 cmp_ok $done_time, '>=', $period, 'delay minimum';
                 cmp_ok $done_time, '<', $period + .2, 'delay maximum';
                 $max = $done_time if $max < $done_time;
-                cmp_ok $tuple->raw(1), '~~', $tlen, 'Length of tuple';
+                is $tuple->raw(1), $tlen, 'Length of tuple';
                 $cv->end;
             });
 
@@ -2,14 +2,176 @@
 #define TP_H_INCLUDED
 
 /*
- * TP - Tarantool Protocol request constructor
+ * TP - Tarantool Protocol library.
  * (http://tarantool.org)
  *
  * protocol description:
- * (https://github.com/mailru/tarantool/blob/master/doc/box-protocol.txt)
+ * https://github.com/tarantool/tarantool/blob/master/doc/box-protocol.txt
+ * -------------------
  *
- * Copyright (c) 2012-2013 Tarantool/Box AUTHORS
- * (https://github.com/mailru/tarantool/blob/master/AUTHORS)
+ * TP - a C library designed to create requests and process
+ * replies to or from a Tarantool server.
+ *
+ * The library is designed to be used by a C/C++ application which
+ * requires sophisticated memory control and/or performance.
+ *
+ * The library does not support network operations. All operations
+ * are done in a user supplied buffer and with help of
+ * a user-supplied allocator.
+ *
+ * The primary purpose of the library was to spare implementors
+ * of Tarantool drivers in other languages, such as Perl,
+ * Ruby, Python, etc, from the details of the binary protocol, and
+ * also to make it possible to avoid double-buffering by writing
+ * directly to/from language objects from/to a serialized binary
+ * packet stream. This paradigm makes data transfer from domain
+ * language types (such as strings, scalars, numbers, etc) to
+ * the network format direct, and, therefore, most efficient.
+ *
+ * As a side effect, the library is usable in any kind of
+ * networking environment: synchronous with buffered sockets, or
+ * asynchronous event-based, as well as with cooperative
+ * multitasking.
+ *
+ * Before using the library, please get acquainted with
+ * Tarnatool binary protocol, documented at
+ * https://github.com/tarantool/tarantool/blob/master/doc/box-protocol.txt
+ *
+ * BASIC REQUEST STRUCTURE
+ * -----------------------
+ *
+ * Any request in Tarantool consists of a 12-byte header,
+ * containing request type, id and length, and an optional tuple
+ * or tuples. Similarly, a response carries back the same request
+ * type and id, and then a tuple or tuples.
+ *
+ * Below is a step-by-step tutorial for creating requests
+ * and unpacking responses.
+ *
+ * TO ASSEMBLE A REQUEST
+ * ---------------------
+ *
+ * (1) initialize an instance of struct tp with tp_init().
+ * Provide tp_init() with a buffer and an (optional) allocator
+ * function.
+ *
+ * (2) construct requests by sequentially calling necessary
+ * operations, such as tp_insert(), tp_delete(), tp_update(),
+ * tp_call(). Note: these operations only append to the buffer
+ * a request header, a body of the request, which is usually
+ * a tuple, must be appended to the buffer with a separate call.
+ * Each next call of tp_*() API appends request data to
+ * the tail of the buffer. If the buffer becomes too small to
+ * contain the binary stream, the reallocation function is
+ * invoked to enlarge the buffer.
+ * A buffer can contain multiple requests: Tarantool
+ * handles them all asynchronously, sending responses
+ * back as soon as they are ready. The request id can be then
+ * used to associate responses with requests.
+ *
+ * For example:
+ *
+ * char buf[256];
+ * struct tp req;
+ * // initialize request buffer
+ * tp_init(&req, buf, sizeof(buf), NULL, NULL);
+ * // append INSERT packet header to the buffer
+ * // request flags are empty, request id is 0
+ * tp_insert(&req, 0, 0);
+ * // begin appending a tuple to the request
+ * tp_tuple(&req);
+ * // append one tuple field
+ * tp_sz(&req, "key");
+ * // one more tuple field
+ * tp_sz(&req, "value");
+ *
+ * (3) the buffer can be used right after all requests are
+ * appended to it. tp_used() can be used to get the current
+ * buffer size:
+ *
+ * write(1, buf, tp_used(&req)); // write the buffer to stdout
+ *
+ * (4) When no longer needed, the buffer must be freed manually.
+ *
+ * For additional examples, please read the documentation for
+ * buffer operations.
+ *
+ * PROCESSING A REPLY
+ * ------------------
+ *
+ * (1) tp_init() must be called with a pointer to a buffer which
+ * already stores or will eventually receive the server response.
+ * Functions tp_reqbuf() and tp_req() can be then used to examine
+ * if a network buffer contains a full reply or not.
+ *
+ * Following is an example of tp_req() usage (reading from stdin
+ * and parsing it until a response is completely read):
+ *
+ * struct tp rep;
+ * tp_init(&rep, NULL, 0, tp_realloc, NULL);
+ *
+ * while (1) {
+ *  ssize_t to_read = tp_req(&rep);
+ *  printf("to_read: %zu\n", to_read);
+ *  if (to_read <= 0)
+ *    break;
+ *  ssize_t new_size = tp_ensure(&rep, to_read);
+ *  printf("new_size: %zu\n", new_size);
+ *  if (new_size == -1)
+ *    return -1;
+ *  int rc = fread(rep.p, to_read, 1, stdin);
+ *  if (rc != 1)
+ *    return 1;
+ *  // discard processed data and make space available
+ *  // for new input:
+ *  tp_use(&rep, to_read);
+ * }
+ *
+ * (2) tp_reply() function can be used to find out if the request
+ * is executed successfully or not:
+ * server_code = tp_reply(&reply);
+ *
+ * if (server_code != 0) {
+ *   printf("error: %-.*s\n", tp_replyerrorlen(&rep),
+ *          tp_replyerror(&rep));
+ * }
+ *
+ * Note: the library itself doesn't contain #defines for server
+ * error codes. They are defined in
+ * https://github.com/tarantool/tarantool/blob/master/include/errcode.h
+ *
+ * A server failure can be either transient or persistent. For
+ * example, a failure to allocate memory is transient: as soon as
+ * some data is deleted, the request can be executed again, this
+ * time successfully. A constraint violation is a non-transient
+ * error: it persists regardless of how many times a request
+ * is re-executed. Server error codes can be analyzed to better
+ * handle an error.
+ *
+ * (3) The server usually responds to any kind of request with a
+ * tuple. Tuple data can be accessed via tp_next(), tp_nextfield(),
+ * tp_gettuple(), tp_getfield().
+ *
+ * See the docs for tp_reply() and tp_next()/tp_nextfield() for an
+ * example.
+ *
+ * API RETURN VALUE CONVENTION
+ * ---------------------------
+ *
+ * API functions return 0 on success, -1 on error.
+ * If a function appends data to struct tp, it returns the
+ * size appended on success, or -1 on error.
+ *
+ * SEE ALSO
+ * --------
+ *
+ * TP is used by Tarantool Perl driver:
+ * https://github.com/dr-co/dr-tarantool/blob/master/Tarantool.xs
+*/
+
+/*
+ * Copyright (c) 2012-2013 Tarantool AUTHORS
+ * (https://github.com/tarantool/tarantool/blob/master/AUTHORS)
  *
  * Redistribution and use in source and binary forms, with or
  * without modification, are permitted provided that the following
@@ -37,7 +199,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- */
+*/
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -51,15 +213,24 @@ extern "C" {
 #define tp_packed __attribute__((packed))
 #define tp_inline __attribute__((forceinline))
 #define tp_noinline __attribute__((noinline))
+#if defined(__GNUC__)
+#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)
 #define tp_hot __attribute__((hot))
+#endif
+#endif
+#if !defined(tp_hot)
+#define tp_hot
+#endif
 
 #define tp_likely(expr)   __builtin_expect(!! (expr), 1)
 #define tp_unlikely(expr) __builtin_expect(!! (expr), 0)
 
 struct tp;
 
-typedef char *(*tp_resizer)(struct tp *p, size_t req, size_t *size);
+/* Reallocation function, can be customized for own use */
+typedef char *(*tp_reserve)(struct tp *p, size_t req, size_t *size);
 
+/* request types. */
 #define TP_PING   65280
 #define TP_INSERT 13
 #define TP_SELECT 17
@@ -67,11 +238,12 @@ typedef char *(*tp_resizer)(struct tp *p, size_t req, size_t *size);
 #define TP_DELETE 21
 #define TP_CALL   22
 
-#define TP_FRET   1
-#define TP_FADD   2
-#define TP_FREP   4
-#define TP_FQUIET 8
+/* requests flags */
+#define TP_BOX_RETURN_TUPLE   1
+#define TP_BOX_ADD            2
+#define TP_BOX_REPLACE        4
 
+/* update operation codes */
 #define TP_OPSET    0
 #define TP_OPADD    1
 #define TP_OPAND    2
@@ -81,20 +253,21 @@ typedef char *(*tp_resizer)(struct tp *p, size_t req, size_t *size);
 #define TP_OPDELETE 6
 #define TP_OPINSERT 7
 
+/* internal protocol headers */
 struct tp_h {
 	uint32_t type, len, reqid;
 } tp_packed;
 
 struct tp_hinsert {
-	uint32_t s, flags;
+	uint32_t space, flags;
 } tp_packed;
 
 struct tp_hdelete {
-	uint32_t s, flags;
+	uint32_t space, flags;
 } tp_packed;
 
 struct tp_hupdate {
-	uint32_t s, flags;
+	uint32_t space, flags;
 } tp_packed;
 
 struct tp_hcall {
@@ -102,55 +275,103 @@ struct tp_hcall {
 } tp_packed;
 
 struct tp_hselect {
-	uint32_t s, i, o, l;
+	uint32_t space, index;
+	uint32_t offset;
+	uint32_t limit;
 	uint32_t keyc;
 } tp_packed;
 
+/*
+ * Main tp object - points either to a request buffer, or to
+ * a response.
+ *
+ * All fields except tp->p should not be accessed directly.
+ * Appropriate accessors should be used instead.
+*/
 struct tp {
-	struct tp_h *h;
-	char *s, *p, *e;
-	char *t, *f, *u;
-	char *c;
-	uint32_t tsz, fsz, tc;
-	uint32_t code;
-	uint32_t cnt;
-	tp_resizer resizer;
-	void *obj;
+	struct tp_h *h;        /* current request header */
+	char *s, *p, *e;       /* start, pos, end */
+	char *t, *f, *u;       /* tuple, tuple field, update operation */
+	char *c;               /* reply parsing position */
+	uint32_t tsz, fsz, tc; /* tuple size, field size, tuple count */
+	uint32_t code;         /* reply server code */
+	uint32_t cnt;          /* reply tuple count */
+	tp_reserve reserve;    /* realloc function pointer */
+	void *obj;             /* reallocation context pointer */
 };
 
+/* Get the size of the allocated buffer */
 static inline size_t
 tp_size(struct tp *p) {
 	return p->e - p->s;
 }
 
+/* Get the size of data in the buffer */
 static inline size_t
 tp_used(struct tp *p) {
 	return p->p - p->s;
 }
 
+/* Get the size available for write */
 static inline size_t
 tp_unused(struct tp *p) {
 	return p->e - p->p;
 }
 
+/* A common reallocation function, can be used
+ * for 'reserve' param in tp_init().
+ * Resizes the buffer twice the previous size using realloc().
+ *
+ * struct tp req;
+ * tp_init(&req, NULL, tp_realloc, NULL);
+ * tp_ping(&req); // will call the reallocator
+ *
+ * data must be manually freed when the buffer is no longer
+ * needed.
+ * (eg. free(p->s));
+ * if realloc will return NULL, then you must destroy previous memory.
+ * (eg.
+ * if (tp_realloc(p, ..) == NULL) {
+ * 	free(p->s)
+ * 	return NULL;
+ * }
+*/
 tp_function_unused static char*
-tp_reallocator(struct tp *p, size_t req, size_t *size) {
+tp_realloc(struct tp *p, size_t required, size_t *size) {
 	size_t toalloc = tp_size(p) * 2;
-	if (tp_unlikely(toalloc < req))
-		toalloc = req;
+	if (tp_unlikely(toalloc < required))
+		toalloc = tp_size(p) + required;
 	*size = toalloc;
 	return realloc(p->s, toalloc);
 }
 
-tp_function_unused static char*
-tp_reallocator_noloss(struct tp *p, size_t req, size_t *size) {
-	*size = tp_size(p) + (req - tp_unused(p));
-	return realloc(p->s, *size);
+
+/* Free function for use in a pair with tp_realloc */
+static inline void
+tp_free(struct tp *p) {
+	free(p->s);
 }
 
+/* Get currently allocated buffer pointer */
+static inline char*
+tp_buf(struct tp *p) {
+	return p->s;
+}
+
+/* Main initialization function.
+ *
+ * reserve - reallocation function, may be NULL
+ * obj     - pointer to be passed to the reallocation function as
+ *           context
+ * buf     - current buffer, may be NULL
+ * size    - current buffer size
+ *
+ * Either a buffer pointer or a reserve function must be
+ * provided.
+*/
 static inline void
 tp_init(struct tp *p, char *buf, size_t size,
-        tp_resizer resizer, void *obj) {
+        tp_reserve reserve, void *obj) {
 	p->s = buf;
 	p->p = p->s;
 	p->e = p->s + size;
@@ -163,18 +384,20 @@ tp_init(struct tp *p, char *buf, size_t size,
 	p->fsz = 0;
 	p->cnt = 0;
 	p->code = 0;
-	p->resizer = resizer;
+	p->reserve = reserve;
 	p->obj = obj;
 }
 
+/* Ensure that buffer has enough space to fill size bytes, resize
+ * buffer if needed. */
 static tp_noinline ssize_t
 tp_ensure(struct tp *p, size_t size) {
 	if (tp_likely(tp_unused(p) >= size))
 		return 0;
-	if (tp_unlikely(p->resizer == NULL))
+	if (tp_unlikely(p->reserve == NULL))
 		return -1;
 	size_t sz;
-	register char *np = p->resizer(p, size, &sz);
+	register char *np = p->reserve(p, size, &sz);
 	if (tp_unlikely(np == NULL))
 		return -1;
 	p->p = np + (p->p - p->s);
@@ -187,16 +410,26 @@ tp_ensure(struct tp *p, size_t size) {
 	if (tp_unlikely(p->u))
 		p->u = (np + (p->u - p->s));
 	p->s = np;
-	p->e = np + sz; 
+	p->e = np + sz;
 	return sz;
 }
 
+/* Mark size bytes as used.
+ * Can be used to tell the buffer that a chunk has been read
+ * from the network into it.
+ */
 static inline ssize_t
 tp_use(struct tp *p, size_t size) {
 	p->p += size;
 	return tp_used(p);
 }
 
+/* Append data to the buffer.
+ * Mostly unnecessary, but can be used to add any raw
+ * iproto-format data to the buffer.
+ * Normally tp_tuple(), tp_field() and tp_sz() should be used
+ * instead.
+ */
 static inline ssize_t
 tp_append(struct tp *p, const void *data, size_t size) {
 	if (tp_unlikely(tp_ensure(p, size) == -1))
@@ -205,24 +438,32 @@ tp_append(struct tp *p, const void *data, size_t size) {
 	return tp_use(p, size);
 }
 
+/* Set the current request id.
+ *
+ * tp_ping(&req);
+ * tp_reqid(&req, 777);
+ */
 static inline void
 tp_reqid(struct tp *p, uint32_t reqid) {
 	assert(p->h != NULL);
 	p->h->reqid = reqid;
 }
 
+/* Return the current request id */
 static inline uint32_t
 tp_getreqid(struct tp *p) {
 	assert(p->h != NULL);
 	return p->h->reqid;
 }
 
+/* Get tuple count */
 static inline uint32_t
 tp_tuplecount(struct tp *p) {
 	assert(p->t != NULL);
 	return *(uint32_t*)(p->t);
 }
 
+/* Write a tuple header */
 static inline ssize_t
 tp_tuple(struct tp *p) {
 	assert(p->h != NULL);
@@ -238,6 +479,7 @@ tp_tuple(struct tp *p) {
 	return tp_used(p);
 }
 
+/* Leb128 calculation functions, internally used by the library */
 static inline size_t
 tp_leb128sizeof(uint32_t value) {
 	return (  tp_likely(value < (1 <<  7))) ? 1 :
@@ -310,6 +552,10 @@ tp_leb128load(struct tp *p, uint32_t *value) {
 	return 0;
 }
 
+/* Write a tuple field
+ * Note: the tuple must be started prior to calling
+ * this function with tp_tuple() call.
+ */
 static inline ssize_t
 tp_field(struct tp *p, const char *data, size_t size) {
 	assert(p->h != NULL);
@@ -325,6 +571,10 @@ tp_field(struct tp *p, const char *data, size_t size) {
 	return tp_used(p);
 }
 
+/* Set the current request.
+ * Note: this is an internal helper function, not part of the
+ * tp.h API.
+ */
 static inline void
 tp_setreq(struct tp *p) {
 	p->h = (struct tp_h*)p->p;
@@ -332,18 +582,47 @@ tp_setreq(struct tp *p) {
 	p->u = NULL;
 }
 
+/* Set current request and append data to the buffer.
+ * Note: this is an internal helper function, not part of the
+ * tp.h API. tp_ping(), tp_update() and other functions
+ * which directly create a request header should be used
+ * instead.
+ */
 static inline ssize_t
 tp_appendreq(struct tp *p, void *h, size_t size) {
+	int isallocated = p->p != NULL;
 	tp_setreq(p);
-	return tp_append(p, h, size);
+	ssize_t rc = tp_append(p, h, size);
+	if (tp_unlikely(rc == -1))
+		return -1;
+	if (!isallocated)
+		p->h = (struct tp_h*)p->s;
+	return rc;
 }
 
+/* Create a ping request.
+ *
+ * char buf[64];
+ * struct tp req;
+ * tp_init(&req, buf, sizeof(buf), NULL, NULL);
+ * tp_ping(&req);
+ */
 static inline ssize_t
 tp_ping(struct tp *p) {
 	struct tp_h h = { TP_PING, 0, 0 };
 	return tp_appendreq(p, &h, sizeof(h));
 }
 
+/* Create an insert request.
+ *
+ * char buf[64];
+ * struct tp req;
+ * tp_init(&req, buf, sizeof(buf), NULL, NULL);
+ * tp_insert(&req, 0, TP_FRET);
+ * tp_tuple(&req);
+ * tp_sz(&req, "key");
+ * tp_sz(&req, "value");
+ */
 static inline ssize_t
 tp_insert(struct tp *p, uint32_t space, uint32_t flags) {
 	struct {
@@ -353,11 +632,20 @@ tp_insert(struct tp *p, uint32_t space, uint32_t flags) {
 	h.h.type = TP_INSERT;
 	h.h.len = sizeof(struct tp_hinsert);
 	h.h.reqid = 0;
-	h.i.s = space;
+	h.i.space = space;
 	h.i.flags = flags;
 	return tp_appendreq(p, &h, sizeof(h));
 }
 
+/* Create a delete request.
+ *
+ * char buf[64];
+ * struct tp req;
+ * tp_init(&req, buf, sizeof(buf), NULL, NULL);
+ * tp_delete(&req, 0, 0);
+ * tp_tuple(&req);
+ * tp_sz(&req, "key");
+ */
 static inline ssize_t
 tp_delete(struct tp *p, uint32_t space, uint32_t flags) {
 	struct {
@@ -367,33 +655,54 @@ tp_delete(struct tp *p, uint32_t space, uint32_t flags) {
 	h.h.type = TP_DELETE;
 	h.h.len = sizeof(struct tp_hdelete);
 	h.h.reqid = 0;
-	h.d.s = space;
+	h.d.space = space;
 	h.d.flags = flags;
 	return tp_appendreq(p, &h, sizeof(h));
 }
 
+/* Create a call request.
+ *
+ * char buf[64];
+ * struct tp req;
+ * tp_init(&req, buf, sizeof(buf), NULL, NULL);
+ *
+ * char proc[] = "hello_proc";
+ * tp_call(&req, 0, proc, sizeof(proc) - 1);
+ * tp_tuple(&req);
+ * tp_sz(&req, "arg1");
+ * tp_sz(&req, "arg2");
+ */
 static inline ssize_t
-tp_call(struct tp *p, uint32_t flags, const char *name, size_t size) {
+tp_call(struct tp *p, uint32_t flags, const char *name, size_t name_len) {
 	struct {
 		struct tp_h h;
 		struct tp_hcall c;
 	} h;
-	size_t sz = tp_leb128sizeof(size);
+	size_t sz = tp_leb128sizeof(name_len);
 	h.h.type = TP_CALL;
-	h.h.len = sizeof(struct tp_hcall) + sz + size;
+	h.h.len = sizeof(struct tp_hcall) + sz + name_len;
 	h.h.reqid = 0;
 	h.c.flags = flags;
-	if (tp_unlikely(tp_ensure(p, sizeof(h) + sz + size) == -1))
+	if (tp_unlikely(tp_ensure(p, sizeof(h) + sz + name_len) == -1))
 		return -1;
 	tp_setreq(p);
 	memcpy(p->p, &h, sizeof(h));
 	p->p += sizeof(h);
-	tp_leb128save(p, size);
-	memcpy(p->p, name, size);
-	p->p += size;
+	tp_leb128save(p, name_len);
+	memcpy(p->p, name, name_len);
+	p->p += name_len;
 	return tp_used(p);
 }
 
+/* Append a select request.
+ *
+ * char buf[64];
+ * struct tp req;
+ * tp_init(&req, buf, sizeof(buf), NULL, NULL);
+ * tp_select(&req, 0, 0, 0, 100);
+ * tp_tuple(&req);
+ * tp_sz(&req, "key");
+ */
 static inline ssize_t
 tp_select(struct tp *p, uint32_t space, uint32_t index,
           uint32_t offset, uint32_t limit) {
@@ -404,14 +713,25 @@ tp_select(struct tp *p, uint32_t space, uint32_t index,
 	h.h.type = TP_SELECT;
 	h.h.len = sizeof(struct tp_hselect);
 	h.h.reqid = 0;
-	h.s.s = space;
-	h.s.i = index;
-	h.s.o = offset;
-	h.s.l = limit;
+	h.s.space = space;
+	h.s.index = index;
+	h.s.offset = offset;
+	h.s.limit = limit;
 	h.s.keyc = 0;
 	return tp_appendreq(p, &h, sizeof(h));
 }
 
+/* Create an update request.
+ *
+ * char buf[64];
+ * struct tp req;
+ * tp_init(&req, buf, sizeof(buf), NULL, NULL);
+ * tp_update(&req, 0, 0);
+ * tp_tuple(&req);
+ * tp_sz(&req, "key");
+ * tp_updatebegin(&req);
+ * tp_op(&req, 1, TP_OPSET, "VALUE", 5);
+ */
 static inline ssize_t
 tp_update(struct tp *p, uint32_t space, uint32_t flags) {
 	struct {
@@ -421,11 +741,27 @@ tp_update(struct tp *p, uint32_t space, uint32_t flags) {
 	h.h.type = TP_UPDATE;
 	h.h.len = sizeof(struct tp_hupdate);
 	h.h.reqid = 0;
-	h.u.s = space;
+	h.u.space = space;
 	h.u.flags = flags;
 	return tp_appendreq(p, &h, sizeof(h));
 }
 
+/* Append the number of operations the update request
+ * is going to contain.
+ * Must be called right after appending the key which
+ * identifies the tuple which must be updated. Since
+ * the key can be multipart, tp_tuple() must be used to
+ * append it.
+ *
+ * In other words, this call sequence creates a proper
+ * UPDATE request:
+ * tp_init(...)
+ * tp_update()
+ * tp_tuple()
+ * tp_sz(), tp_sz(), ...
+ * tp_updatebegin()
+ * tp_op(), tp_op(), ...
+ */
 static inline ssize_t
 tp_updatebegin(struct tp *p) {
 	assert(p->h != NULL);
@@ -438,6 +774,14 @@ tp_updatebegin(struct tp *p) {
 	return tp_used(p);
 }
 
+/* Append a single UPDATE operation.
+ *
+ * May be called after tp_updatebegin().
+ * Can be used to create TP_OPSET, TP_OPADD, TP_OPAND,
+ * TP_OPXOR, TP_OPOR operations.
+ *
+ * See: tp_update() for example.
+ */
 static inline ssize_t
 tp_op(struct tp *p, uint32_t field, uint8_t op, const char *data,
       size_t size) {
@@ -464,35 +808,46 @@ tp_op(struct tp *p, uint32_t field, uint8_t op, const char *data,
 	return tp_used(p);
 }
 
+/* Append a SPLICE operation. This operation is unlike any other,
+ * since it takes three arguments instead of one.
+ */
 static inline ssize_t
-tp_opsplice(struct tp *p, uint32_t field, uint32_t off, uint32_t len,
-            const char *data, size_t size) {
-	uint32_t olen = tp_leb128sizeof(sizeof(off)),
-	         llen = tp_leb128sizeof(sizeof(len)),
-	         dlen = tp_leb128sizeof(size);
-	uint32_t sz = olen + sizeof(off) + llen + sizeof(len) +
-	              dlen + size;
+tp_opsplice(struct tp *p, uint32_t field, uint32_t offset,
+	    uint32_t cut, const char *paste, size_t paste_len) {
+	uint32_t olen = tp_leb128sizeof(sizeof(offset)),
+	         clen = tp_leb128sizeof(sizeof(cut)),
+	         plen = tp_leb128sizeof(paste_len);
+	uint32_t sz = olen + sizeof(offset) + clen + sizeof(cut) +
+	              plen + paste_len;
 	ssize_t rc = tp_op(p, field, TP_OPSPLICE, NULL, sz);
 	if (tp_unlikely(rc == -1))
 		return -1;
 	p->p -= sz;
-	tp_leb128save(p, sizeof(off));
-	memcpy(p->p, &off, sizeof(off));
-	p->p += sizeof(off);
-	tp_leb128save(p, sizeof(len));
-	memcpy(p->p, &len, sizeof(len));
-	p->p += sizeof(len);
-	tp_leb128save(p, size);
-	memcpy(p->p, data, size);
-	p->p += size;
+	tp_leb128save(p, sizeof(offset));
+	memcpy(p->p, &offset, sizeof(offset));
+	p->p += sizeof(offset);
+	tp_leb128save(p, sizeof(cut));
+	memcpy(p->p, &cut, sizeof(cut));
+	p->p += sizeof(cut);
+	tp_leb128save(p, paste_len);
+	memcpy(p->p, paste, paste_len);
+	p->p += paste_len;
 	return rc;
 }
 
+/* Append a '\0' terminated string as a tuple field. */
 static inline ssize_t
 tp_sz(struct tp *p, const char *sz) {
 	return tp_field(p, sz, strlen(sz));
 }
 
+/*
+ * Returns the number of bytes which are required to fully
+ * store a reply in the buffer.
+ * The return value can be negative, which indicates that
+ * there is a complete reply in the buffer which is not parsed
+ * and discarded yet.
+ */
 static inline ssize_t
 tp_reqbuf(const char *buf, size_t size) {
 	if (tp_unlikely(size < sizeof(struct tp_h)))
@@ -503,16 +858,27 @@ tp_reqbuf(const char *buf, size_t size) {
 	                  sz - size : -(size - sz);
 }
 
+/* Same as tp_reqbuf(), but works on the buffer in struct tp.
+ */
 static inline ssize_t
 tp_req(struct tp *p) {
 	return tp_reqbuf(p->s, tp_size(p));
 }
 
+/* Get the size of a yet unprocessed reply data.
+ *
+ * This is not part of the API.
+ */
 static inline size_t
 tp_unfetched(struct tp *p) {
 	return p->p - p->c;
 }
 
+/* Advance the reply processed pointer.
+ *
+ * This is not part of the API, tp_use() is a higher level
+ * function.
+ */
 static inline void*
 tp_fetch(struct tp *p, int inc) {
 	assert(tp_unfetched(p) >= inc);
@@ -521,31 +887,68 @@ tp_fetch(struct tp *p, int inc) {
 	return po;
 }
 
+static inline int
+tp_can_fetch(struct tp *p, int inc) {
+    if (tp_unfetched(p) >= inc)
+        return 1;
+    return 0;
+}
+
+
+/* Get the last server error.
+*/
 static inline char*
 tp_replyerror(struct tp *p) {
 	return p->c;
 }
 
+/* Get the length of the last error message.
+ */
 static inline int
 tp_replyerrorlen(struct tp *p) {
 	return tp_unfetched(p);
 }
 
+/* Get the tuple count in the response (there must be
+ * no error).
+ */
 static inline uint32_t
 tp_replycount(struct tp *p) {
 	return p->cnt;
 }
 
+/* Get the current response return code.
+ */
 static inline uint32_t
 tp_replycode(struct tp *p) {
 	return p->code;
 }
 
+/* Get the current response operation code. */
 static inline uint32_t
 tp_replyop(struct tp *p) {
 	return p->h->type;
 }
 
+/*
+ * Initialize the buffer with a fully read server response.
+ * The response is parsed.
+ *
+ * struct tp rep;
+ * tp_init(&rep, reply_buf, reply_size, NULL, NULL);
+ *
+ * ssize_t server_code = tp_reply(&rep);
+ *
+ * printf("op:    %d\n", tp_replyop(&rep));
+ * printf("count: %d\n", tp_replycount(&rep));
+ * printf("code:  %zu\n", server_code);
+ *
+ * if (server_code != 0) {
+ *   printf("error: %-.*s\n", tp_replyerrorlen(&rep),
+ *          tp_replyerror(&rep));
+ * }
+ *
+ */
 tp_function_unused static ssize_t
 tp_reply(struct tp *p) {
 	ssize_t used = tp_req(p);
@@ -554,59 +957,87 @@ tp_reply(struct tp *p) {
 	/* this is end of packet in continious buffer */
 	p->p = p->e + used; /* end - used */
 	p->c = p->s;
+	if (!tp_can_fetch(p, sizeof(struct tp_h)))
+	    return -1;
 	p->h = tp_fetch(p, sizeof(struct tp_h));
 	p->t = p->f = p->u = NULL;
 	p->cnt = 0;
 	p->code = 0;
 	if (tp_unlikely(p->h->type == TP_PING))
-		return 0; 
+		return 0;
 	if (tp_unlikely(p->h->type != TP_UPDATE &&
 	                p->h->type != TP_INSERT &&
 	                p->h->type != TP_DELETE &&
 	                p->h->type != TP_SELECT &&
 	                p->h->type != TP_CALL))
 		return -1;
+	if (!tp_can_fetch(p, sizeof(uint32_t)))
+	    return -1;
 	p->code = *(uint32_t*)tp_fetch(p, sizeof(uint32_t));
 	if (p->code != 0)
 		return p->code;
 	/* BOX_QUIET */
 	if (tp_unlikely(tp_unfetched(p) == 0))
 		return p->code;
+	if (!tp_can_fetch(p, sizeof(uint32_t)))
+	    return -1;
 	p->cnt = *(uint32_t*)tp_fetch(p, sizeof(uint32_t));
 	return p->code;
 }
 
+/* Example: iteration over returned tuples.
+ *
+ * while (tp_next(&rep)) {
+ *   printf("tuple fields: %d\n", tp_tuplecount(&rep));
+ *   printf("tuple size: %d\n", tp_tuplesize(&rep));
+ *   printf("[");
+ *   while (tp_nextfield(&rep)) {
+ *     printf("%-.*s", tp_getfieldsize(rep), tp_getfield(&rep));
+ *     if (tp_hasnextfield(&rep))
+ *       printf(", ");
+ *   }
+ *   printf("]\n");
+ * }
+*/
+
+/* Rewind iteration to the first tuple. */
 static inline void
 tp_rewind(struct tp *p) {
 	p->t = NULL;
 	p->f = NULL;
 }
 
+/* Rewind iteration to the first tuple field of the current tuple. */
 static inline void
 tp_rewindfield(struct tp *p) {
 	p->f = NULL;
 }
 
+/* Get the current tuple data, all fields. */
 static inline char*
 tp_gettuple(struct tp *p) {
 	return p->t;
 }
 
+/* Get the current tuple size. */
 static inline uint32_t
 tp_tuplesize(struct tp *p) {
 	return p->tsz;
 }
 
+/* Get the current field. */
 static inline char*
 tp_getfield(struct tp *p) {
 	return p->f;
 }
 
+/* Get the current field size. */
 static inline uint32_t
 tp_getfieldsize(struct tp *p) {
 	return p->fsz;
 }
 
+/* Get a pointer to the end of the current tuple. */
 static inline char*
 tp_tupleend(struct tp *p) {
 	/* tuple_size + p->t + cardinaltiy_size +
@@ -614,17 +1045,21 @@ tp_tupleend(struct tp *p) {
 	return p->t + 4 + p->tsz;
 }
 
+/* Check if the response has a tuple.
+ * Automatically checked during tp_next() iteration. */
 static inline int
 tp_hasdata(struct tp *p) {
 	return tp_replyop(p) != TP_PING && tp_unfetched(p) > 0;
 }
 
+/* Check if there is a one more tuple. */
 static inline int
 tp_hasnext(struct tp *p) {
 	assert(p->t != NULL);
 	return (p->p - tp_tupleend(p)) >= 4;
 }
 
+/* Check if the current tuple has a one more field. */
 static inline int
 tp_hasnextfield(struct tp *p) {
 	assert(p->t != NULL);
@@ -634,6 +1069,9 @@ tp_hasnextfield(struct tp *p) {
 	return (tp_tupleend(p) - f) >= 1;
 }
 
+/* Skip to the next tuple.
+ * Tuple can be accessed using:
+ * tp_tuplecount(), tp_tuplesize(), tp_gettuple(). */
 static inline int
 tp_next(struct tp *p) {
 	if (tp_unlikely(p->t == NULL)) {
@@ -647,10 +1085,14 @@ tp_next(struct tp *p) {
 	p->t = tp_tupleend(p) + 4;
 fetch:
 	p->tsz = *(uint32_t*)(p->t - 4);
+	if (tp_unlikely((p->t + p->tsz) > p->e))
+		return -1;
 	p->f = NULL;
 	return 1;
 }
 
+/* Skip to the next field.
+ * Data can be accessed using: tp_getfieldsize(), tp_getfield(). */
 static inline int
 tp_nextfield(struct tp *p) {
 	assert(p->t != NULL);
@@ -667,6 +1109,8 @@ fetch:;
 	register int rc = tp_leb128load(p, &p->fsz);
 	if (tp_unlikely(rc == -1))
 		return -1;
+	if (tp_unlikely((p->f + p->fsz) > p->e))
+		return -1;
 	return 1;
 }