From f81f79c88bbb46297dcf7640285bd0d11bff8988 Mon Sep 17 00:00:00 2001
From: Daniil Medvedev <medvdanil@gmail.com>
Date: Mon, 20 Jul 2015 17:10:57 +0300
Subject: [PATCH] csv tests reorganization

---
 csv.documentation     |  63 ---------------
 src/lib/csv/csv.c     |  16 +++-
 src/lib/csv/csv.h     |  19 +++--
 src/lua/csv.lua       |  61 +++++++-------
 test/app/csv.result   |  76 +++--------------
 test/app/csv.test.lua |  46 +++++------
 test/unit/csv.c       |  82 ++++++++++++++-----
 test/unit/csv.result  | 184 ++++++++++++------------------------------
 8 files changed, 202 insertions(+), 345 deletions(-)
 delete mode 100644 csv.documentation

diff --git a/csv.documentation b/csv.documentation
deleted file mode 100644
index 912f78b48d..0000000000
--- a/csv.documentation
+++ /dev/null
@@ -1,63 +0,0 @@
-Tarantool supports CSV file input/output.
-CSV is comma separated values, like this:
-example.txt:
-package,method,return value
-fio,pathjoin,string
-csv,load,table
-none,",comma in field", and ""quote""
-
-Commas an linebreaks in fields must be in quotes
-Quotes in fields is repeated two times quote character.
-You can set delimiter and quote character:
-    csv.delimiter = ','
-    csv.quote = '"'
-Input/output works through readable/writable objects, for example files or sockets.
-Readable object has method read(N), which returns N or less bytes as string.
-Writable object has method write(string), which sends string to output.
-
-csv.iterate = function(readable[, csv_chunk_size])
---@brief parse csv string by string
---@param readable must be string or object with method read(num) returns string
---@param csv_chunk_size (default 4096). Parser will read by csv_chunk_size symbols
---@return iter function, iterator state
-
-Example:
-    f = require("fio").open("example.txt", { "O_RDONLY"})
-    for tup in csv.iterate(f) do
-        print(tup[1], tup[2], tup[3])
-    end
-Output:
-    package method  return value
-    fio     pathjoin        string
-    csv     load    table
-    none    ,comma in field and "quote"
-
-csv.load = function(readable[, skip_lines, csv_chunk_size])
---@brief parse csv and make table
---@param skip_lines is number of lines to skip.
---@return table
-If csv file has a header, it may be skipped.
-
-csv.dump = function(t[, writable])
---@brief dumps tuple or table as csv
---@param t is table or tuple. Fields may be of any type, but it will be converted to string by method tostring(field).
---@param writable must be object with method write(string) like file or socket
---@return there is no writable it returns csv as string
-
-Example:
-    f = require("fio").open("dump.csv", { "O_WRONLY", "O_TRUNC" , "O_CREAT"}, 0x1FF)
-    multiline_header = {{'csv example'}, {'3 numbers per string:'}}
-    csv.dump(multiline_header, f)
-    for i=0,14,3 do 
-    t = {i, i + 1, i + 2} 
-    s = csv.dump(t, f) 
-    end
-dump.csv:
-    csv example
-    3 numbers per string:
-    0,1,2
-    3,4,5
-    6,7,8
-    9,10,11
-    12,13,14
-
diff --git a/src/lib/csv/csv.c b/src/lib/csv/csv.c
index 25a8050eb4..9640a822e7 100644
--- a/src/lib/csv/csv.c
+++ b/src/lib/csv/csv.c
@@ -249,21 +249,29 @@ csv_feed(struct csv_iterator *it, const char *str)
 }
 
 int
-csv_escape_field(struct csv *csv, const char *field, char *dst)
+csv_escape_field(struct csv *csv, const char *field, size_t field_len, char *dst, size_t buf_size)
 {
 	char *p = dst;
 	int inquotes = 0;
-	if(strchr(field, csv->csv_delim) || strchr(field, '\n') || strchr(field, '\r')) {
+	if(memchr(field, csv->csv_delim, field_len) || memchr(field, '\n', field_len) || memchr(field, '\r', field_len)) {
 		inquotes = 1;
 		*p++ = csv->csv_quote;
 	}
 	while(*field) {
-		if(*field == csv->csv_quote)
+		if(*field == csv->csv_quote) {
+			if(p - dst >= buf_size)
+				return -1;
 			*p++ = csv->csv_quote;
+		}
+		if(p - dst >= buf_size)
+			return -1;
 		*p++ = *field++;
 	}
-	if(inquotes)
+	if(inquotes) {
+		if(p - dst >= buf_size)
+			return -1;
 		*p++ = csv->csv_quote;
+	}
 	*p = 0;
 	return p - dst;
 }
diff --git a/src/lib/csv/csv.h b/src/lib/csv/csv.h
index 6fd48b7caa..8faaadc85e 100644
--- a/src/lib/csv/csv.h
+++ b/src/lib/csv/csv.h
@@ -120,15 +120,24 @@ void
 csv_feed(struct csv_iterator *, const char *);
 
 /**
- * @brief csv_escape_field adds pair quote and
- * if there is comma or linebreak in field, adds surrounding quotes
+ * @brief csv_escape_field prepares field to out in file.
+ * Adds pair quote and if there is comma or linebreak in field, adds surrounding quotes.
+ * At worst escaped field will 2 times more symbols than input field.
+ * @return length of escaped field or -1 if not enough space in buffer.
  */
 int
-csv_escape_field(struct csv *csv, const char *field, char *dst);
+csv_escape_field(struct csv *csv, const char *field, size_t field_len, char *dst, size_t buf_size);
 
-#define CSV_ITERATOR_GET_FIELD(it) it->field
-#define CSV_ITERATOR_GET_FLEN(it)  it->field_len
 
+static inline const char* csv_iterator_get_field(struct csv_iterator *it)
+{
+	return it->field;
+}
+
+static inline size_t csv_iterator_get_field_len(struct csv_iterator *it)
+{
+	return it->field_len;
+}
 #if defined(__cplusplus)
 }
 #endif /* extern "C" */
diff --git a/src/lua/csv.lua b/src/lua/csv.lua
index 8c0183e7f7..0fd0b93edc 100644
--- a/src/lua/csv.lua
+++ b/src/lua/csv.lua
@@ -41,7 +41,7 @@ ffi.cdef[[
     void csv_iter_create(struct csv_iterator *it, struct csv *csv);
     int csv_next(struct csv_iterator *);
     void csv_feed(struct csv_iterator *, const char *);
-    int csv_escape_field(struct csv *, const char *, char *);
+    int csv_escape_field(struct csv *csv, const char *field, size_t field_len, char *dst, size_t buf_size);
     enum {
         CSV_IT_OK,
         CSV_IT_EOL,
@@ -51,7 +51,28 @@ ffi.cdef[[
     };
 ]]
 
-local iter = function(csvstate)
+local make_readable = function(s)
+    rd = {}
+    rd.val = s
+    rd.read = function(self, cnt)
+        local res = self.val;
+        self.val = ""
+        return res
+    end
+    return rd
+end
+
+local make_writable = function()
+    wr = {}
+    wr.returnstring = ""
+    wr.write = function(self, s)
+        wr.returnstring = wr.returnstring .. s
+    end
+    return wr
+end
+
+
+local iter = function(csvstate, i)
     local readable = csvstate[1]
     local csv_chunk_size = csvstate[2]
     local csv = csvstate[3]
@@ -62,7 +83,8 @@ local iter = function(csvstate)
         if st == ffi.C.CSV_IT_NEEDMORE then
             ffi.C.csv_feed(it, readable:read(csv_chunk_size))
         elseif st == ffi.C.CSV_IT_EOL then
-            return tup
+            i = i + 1
+            return i, tup
         elseif st == ffi.C.CSV_IT_OK then
             table.insert(tup, ffi.string(it[0].field, it[0].field_len))
         elseif st == ffi.C.CSV_IT_ERROR then
@@ -76,26 +98,6 @@ local iter = function(csvstate)
     end
 end
 
-local make_readable = function(s)
-    rd = {}
-    rd.val = s
-    rd.read = function(self, cnt)
-        local res = self.val;
-        self.val = ""
-        return res
-    end
-    return rd
-end
-
-local make_writable = function()
-    wr = {}
-    wr.returnstring = ""
-    wr.write = function(self, s)
-        wr.returnstring = wr.returnstring .. s
-    end
-    return wr
-end
-
 local module = {}
 
 module.delimiter = ','
@@ -126,7 +128,7 @@ module.iterate = function(readable, csv_chunk_size)
     ffi.C.csv_iter_create(it, csv)
     ffi.C.csv_feed(it, str)
 
-    return iter, {readable, csv_chunk_size, csv, it}
+    return iter, {readable, csv_chunk_size, csv, it}, 0
 end
 
 --@brief parse csv and make table
@@ -136,12 +138,9 @@ module.load = function(readable, skip_lines, csv_chunk_size)
     skip_lines = skip_lines or 0
     csv_chunk_size = csv_chunk_size or 4096
     result = {}
-    i = 0
-    for tup in module.iterate(readable, csv_chunk_size) do
-        if i < skip_lines then 
-            i = i + 1
-        else
-            table.insert(result, tup)
+    for i, tup in module.iterate(readable, csv_chunk_size) do
+        if i > skip_lines then             
+            result[i - skip_lines] = tup
         end
     end
 
@@ -175,7 +174,7 @@ module.dump = function(t, writable)
                 bufsz = (strf:len() + 1) * 2
                 buf = csv[0].csv_realloc(buf, bufsz)
             end
-            local len = ffi.C.csv_escape_field(csv, strf, buf)
+            local len = ffi.C.csv_escape_field(csv, strf, string.len(strf), buf, bufsz)
             if first then
                 first = false
             else
diff --git a/test/app/csv.result b/test/app/csv.result
index 73f58e2b21..bb6183e414 100644
--- a/test/app/csv.result
+++ b/test/app/csv.result
@@ -1,66 +1,10 @@
-obj test1:
-|a|	|b|	
-|1|	|ha
-"ha"
-ha|	
-|3|	|4|	
-
-obj test2:
-||	||	||	
-||	||	
-||	
-
-obj test3:
-||	||	
-|kp"v|	
-
-fio test1:
-|123|	|5|	|92|	|0|	|0|	
-|1|	|12  34|	|56|	|quote , |	|66|	
-|ok|	
-
-fio test2:
-|1|	
-|23|	|456|	|abcac|	|'multiword field 4'|	
-|none|	|none|	|0|	
-||	||	||	
-|aba|	|adda|	|f3|	|0|	
-|local res = internal.pwrite(self.fh|	|data|	|len|	|offset)|	
-|iflag = bit.bor(iflag|	|fio.c.flag[ flag ])|	
-||	||	||	
-
-fio test3:
-|23|	|456|	|abcac|	|'multiword field 4'|	
-|none|	|none|	|0|	
-||	||	||	
-|aba|	|adda|	|f3|	|0|	
-|local res = internal.pwrite(self.fh|	|data|	|len|	|offset)|	
-|iflag = bit.bor(iflag|	|fio.c.flag[ flag ])|	
-||	||	||	
-
-test roundtrip: 
-true
-test iterate, only first field:
-1
-23
-none
-
-aba
-local res = internal.pwrite(self.fh
-iflag = bit.bor(iflag
-
-test str dump:
-quote"" d,",and, comma","both "" of "" t,h,e,m"
-"""""",","","""
-"mul
-ti
-li
-ne
-
-",field
-
-""
-"
-"
-
-test load(dump(t)): true
+TAP version 13
+1..8
+ok - obj test1
+ok - obj test2
+ok - obj test3
+ok - fio test1
+ok - fio test2
+ok - fio test3
+ok - test roundtrip
+ok - test load(dump(t))
diff --git a/test/app/csv.test.lua b/test/app/csv.test.lua
index 3bb6a54bf4..06ebaefec4 100755
--- a/test/app/csv.test.lua
+++ b/test/app/csv.test.lua
@@ -18,40 +18,49 @@ local function myread(self, bytes)
 end
 local csv = require('csv')
 local fio = require('fio')
+local tap = require('tap')
+local test1 = '|a|\t|b|\t\n|1|\t|ha\n"ha"\nha|\t\n|3|\t|4|\t\n'
+local test2 = '||\t||\t||\t\n||\t||\t\n||\t\n'
+local test3 = '||\t||\t\n|kp"v|\t\n'
+local test4 = '|123|\t|5|\t|92|\t|0|\t|0|\t\n|1|\t|12  34|\t|56|\t|quote , |\t|66|\t\n|ok|\t\n'
+local test5 = "|1|\t\n|23|\t|456|\t|abcac|\t|'multiword field 4'|\t\n|none|\t|none|\t|0|\t\n" .. 
+        "||\t||\t||\t\n|aba|\t|adda|\t|f3|\t|0|\t\n|local res = internal.pwrite(self.fh|\t|d" ..
+        "ata|\t|len|\t|offset)|\t\n|iflag = bit.bor(iflag|\t|fio.c.flag[ flag ])|\t\n||\t||\t||\t\n"
+local test6 = "|23|\t|456|\t|abcac|\t|'multiword field 4'|\t\n|none|\t|none|\t|0|\t\n||\t||\t||\t\n" .. 
+        "|aba|\t|adda|\t|f3|\t|0|\t\n|local res = internal.pwrite(self.fh|\t|data|\t|len|\t|offset)" ..
+        "|\t\n|iflag = bit.bor(iflag|\t|fio.c.flag[ flag ])|\t\n||\t||\t||\t\n"
+
+test = tap.test("csv")
+test:plan(8)
 
-print("obj test1:")
 readable = {}
 readable.read = myread
 readable.v = "a,b\n1,\"ha\n\"\"ha\"\"\nha\"\n3,4\n"
 readable.i = 0
-print(table2str(csv.load(readable)))
+test:is(table2str(csv.load(readable)), test1, "obj test1")
 
-print("obj test2:")
 readable.v = ", ,\n , \n\n"
 readable.i = 0
-print(table2str(csv.load(readable, 0, 1)))
+test:is(table2str(csv.load(readable, 0, 1)), test2, "obj test2")
 
-print("obj test3:")
 readable.v = ", \r\nkp\"\"v"
 readable.i = 0
-print(table2str(csv.load(readable, 0, 3)))
+test:is(table2str(csv.load(readable, 0, 3)), test3, "obj test3")
 
 tmpdir = fio.tempdir()
 file1 = fio.pathjoin(tmpdir, 'file.1')
 file2 = fio.pathjoin(tmpdir, 'file.2')
 file3 = fio.pathjoin(tmpdir, 'file.3')
 
-print("fio test1:")
 local f = fio.open(file1, { 'O_WRONLY', 'O_TRUNC', 'O_CREAT' }, 0777)
 f:write("123 , 5  ,       92    , 0, 0\n" ..
         "1, 12  34, 56, \"quote , \", 66\nok")
 f:close()
 f = fio.open(file1, {'O_RDONLY'}) 
-print(table2str(csv.load(f,0,10)))
+test:is(table2str(csv.load(f,0,10)), test4, "fio test1")
 f:close()
 
 
-print("fio test2:")
 f = fio.open(file2, { 'O_WRONLY', 'O_TRUNC', 'O_CREAT' }, 0777)
 f:write("1\n23,456,abcac,\'multiword field 4\'\n" ..
         "none,none,0\n" ..
@@ -63,12 +72,11 @@ f:write("1\n23,456,abcac,\'multiword field 4\'\n" ..
 )
 f:close()
 f = fio.open(file2, {'O_RDONLY'}) 
-print(table2str(csv.load(f, 0, 1))) --symbol by symbol reading
+test:is(table2str(csv.load(f, 0, 1)), test5, "fio test2") --symbol by symbol reading
 f:close()
 
-print("fio test3:")
 f = fio.open(file2, {'O_RDONLY'}) 
-print(table2str(csv.load(f, 1, 7))) --7 symbols per chunk
+test:is(table2str(csv.load(f, 1, 7)), test6, "fio test3") --7 symbols per chunk
 f:close()
 
 
@@ -88,19 +96,9 @@ f = fio.open(file3, {'O_RDONLY'})
 t2 = csv.load(f, 0, 5)
 f:close()
 
-print("test roundtrip: ")
-print(table2str(t) == table2str(t2))
+test:is(table2str(t), table2str(t2), "test roundtrip")
 
-print("test iterate, only first field:")
-f = fio.open(file2, {'O_RDONLY'}) 
-for tup in csv.iterate(f) do
-    print(tup[1])
-end
-f:close()
-
-print("test str dump:")
-print(csv.dump(t))
-print("test load(dump(t)): " .. tostring(table2str(t) == table2str(csv.load(csv.dump(t)))))
+test:is(table2str(t), table2str(csv.load(csv.dump(t))), "test load(dump(t))")
 
 fio.unlink(file1)
 fio.unlink(file2)
diff --git a/test/unit/csv.c b/test/unit/csv.c
index 8a36c7b6b6..6863254967 100644
--- a/test/unit/csv.c
+++ b/test/unit/csv.c
@@ -4,15 +4,20 @@
 #include <string.h>
 #include <assert.h>
 
+int isendl = 1;
 void
 print_endl(void *ctx)
 {
 	fflush(stdout);
 	puts("");
+	isendl = 1;
 }
 void
 print_field(void *ctx, const char *s, const char *end)
 {
+	if(!isendl)
+		putchar('\t');
+	isendl = 0;
 	putchar('|');
 	for(const char *p = s; p != end && *p; p++) {
 		if((*p == '\r' || *p == '\n') && (p + 1 == end || (*(p + 1) != '\r' && *(p + 1) != '\n')))
@@ -21,9 +26,26 @@ print_field(void *ctx, const char *s, const char *end)
 			putchar(*p);
 	}
 	putchar('|');
-	putchar('\t');
 	fflush(stdout);
 }
+void
+buf_endl(void *ctx)
+{
+	*(*((char**)ctx))++ = '\n';
+}
+void
+buf_field(void *ctx, const char *s, const char *end)
+{
+	*(*((char**)ctx))++ = '|';
+	for(const char *p = s; p != end && *p; p++) {
+		if((*p == '\r' || *p == '\n') && (p + 1 == end || (*(p + 1) != '\r' && *(p + 1) != '\n')))
+			*(*((char**)ctx))++ = '\n';
+		else
+			*(*((char**)ctx))++ = *p;
+	}
+	*(*((char**)ctx))++ = '|';
+	*(*((char**)ctx))++ = '\t';
+}
 
 void small_string_test(const char* const s)
 {
@@ -33,7 +55,6 @@ void small_string_test(const char* const s)
 	csv.emit_row = print_endl;
 	csv_parse_chunk(&csv, s, s + strlen(s));
 	csv_finish_parsing(&csv);
-	printf("valid: %s\n", csv.csv_invalid ? "NO" : "yes");
 	csv_destroy(&csv);
 }
 
@@ -172,18 +193,33 @@ void big_chunk_separated_test() {
 
 void random_generated_test() {
 	header();
-	small_string_test(
-				"\n\r\" ba\r a\ra, \n\"\n\"a\nb\" \raa\rb,\n"
-				"\r, \n\",\r\n\"\n,a, ,\"a\n\n\r \"\r ba\r,b"
-				"  a,\n,\"\"a\n\r \"b\"   \n,\",a\r,a ,\r\rc"
-				"\" a,b\r\n,\"b\r\"aa  \nb \n\r\r\n\n,\rb\nc"
-				",\n\n aa\n \"\n ab\rab,\r\" b\n\",   ,,\r\r"
-				"bab\rb\na\n\"a\ra,\"\",\n\"a\n\n \"\r \ra\n"
-				"a\r\raa a\" ,baab ,a \rbb   ,\r \r,\rb,,  b"
-				"\n\r\"\nb\n\nb \n,ab \raa\r\"\nb a\"ba,b, c"
-				"\"a\"a \"\r\n\"b \n,b\"\",\nba\n\" \n\na \r"
-				"\nb\rb\"bbba,\" \n\n\n,a,b,a,b,\n\n\n\nb\"\r"
-				);
+	const char *rand_test =
+			"\n\r\" ba\r a\ra, \n\"\n\"a\nb\" \raa\rb,\n"
+			"\r, \n\",\r\n\"\n,a, ,\"a\n\n\r \"\r ba\r,b"
+			"  a,\n,\"\"a\n\r \"b\"   \n,\",a\r,a ,\r\rc"
+			"\" a,b\r\n,\"b\r\"aa  \nb \n\r\r\n\n,\rb\nc"
+			",\n\n aa\n \"\n ab\rab,\r\" b\n\",   ,,\r\r"
+			"bab\rb\na\n\"a\ra,\"\",\n\"a\n\n \"\r \ra\n"
+			"a\r\raa a\" ,baab ,a \rbb   ,\r \r,\rb,,  b"
+			"\n\r\"\nb\n\nb \n,ab \raa\r\"\nb a\"ba,b, c"
+			"\"a\"a \"\r\n\"b \n,b\"\",\nba\n\" \n\na \r"
+			"\nb\rb\"bbba,\" \n\n\n,a,b,a,b,\n\n\n\nb\"\r";
+
+	struct csv csv;
+	csv_create(&csv);
+	csv_setopt(&csv, CSV_OPT_EMIT_FIELD, fieldsizes_counter);
+	csv_setopt(&csv, CSV_OPT_EMIT_ROW, line_counter);
+
+	struct counter cnt;
+	cnt.line_cnt = 0;
+	cnt.fieldsizes_cnt = 0;
+	csv_setopt(&csv, CSV_OPT_CTX, &cnt);
+
+	csv_parse_chunk(&csv, rand_test, rand_test + strlen(rand_test));
+	csv_finish_parsing(&csv);
+	printf("line_cnt=%d, fieldsizes_cnt=%d\n", (int)cnt.line_cnt, (int)cnt.fieldsizes_cnt);
+	printf("valid: %s\n", csv.csv_invalid ? "NO" : "yes");
+	csv_destroy(&csv);
 
 	footer();
 }
@@ -203,7 +239,7 @@ void iter_test1() {
 			buf += strlen(buf);
 			break;
 		case CSV_IT_EOL:
-			printf("\n");
+			print_endl(0);
 			break;
 		case CSV_IT_OK:
 			print_field(0, it.field, it.field + it.field_len);
@@ -233,7 +269,7 @@ void iter_test2() {
 			buf += 3;
 			break;
 		case CSV_IT_EOL:
-			printf("\n");
+			print_endl(0);
 			break;
 		case CSV_IT_OK:
 			print_field(0, it.field, it.field + it.field_len);
@@ -250,14 +286,18 @@ void iter_test2() {
 void csv_out() {
 	header();
 
-	const char fields[4][36] = { "abc", "with,comma", "\"in quotes\"", "1 \" quote"};
-	char buf[18];
+	const char fields[5][24] = { "abc", "with,comma", "\"in quotes\"", "1 \" quote",
+				     "long field, return \"-1\"" };
+	char buf[24];
 	int i;
 	struct csv csv;
 	csv_create(&csv);
-	for(i = 0; i < 4; i++) {
-		int len = csv_escape_field(&csv, fields[i], buf);
-		printf("%s<len=%d>%c", buf, len, i == 3 ? '\n' : ',');
+	for(i = 0; i < 5; i++) {
+		int len = csv_escape_field(&csv, fields[i], strlen(fields[i]), buf, sizeof(buf));
+		if(len != -1)
+			printf("%s<len=%d>%c", buf, len, i == 4 ? '\n' : ',');
+		else
+			printf("<len=%d>%c", len, i == 4 ? '\n' : ',');
 	}
 
 	footer();
diff --git a/test/unit/csv.result b/test/unit/csv.result
index 8d56fa9744..61a66cb1a0 100644
--- a/test/unit/csv.result
+++ b/test/unit/csv.result
@@ -1,189 +1,111 @@
 	*** test1 ***
-|1|	
-||	
-|1|	|2|	|3|	
-|123|	
+|1|
+||
+|1|	|2|	|3|
+|123|
 valid: yes
 	*** test1: done ***
  	*** test2 ***
-|123|	|456|	|abcac|	|'multiword field 4'|	
-|none|	|none|	|0|	
-||	||	||	
-||	||	||	
+|123|	|456|	|abcac|	|'multiword field 4'|
+|none|	|none|	|0|
+||	||	||
+||	||	||
 valid: yes
 	*** test2: done ***
  	*** test3 ***
-|1|	||	|2|	
+|1|	||	|2|
 valid: yes
 	*** test3: done ***
  	*** test4 ***
-|123|	|5|	|92|	|0|	|0|	
-|1|	|12  34|	|56|	|quote , |	|66|	
-|ok|	
+|123|	|5|	|92|	|0|	|0|
+|1|	|12  34|	|56|	|quote , |	|66|
+|ok|
 valid: yes
 	*** test4: done ***
  	*** test5 ***
-|abc|	|longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong|	|0|	
-|123|	|456|	||	
-|0|	||	||	
+|abc|	|longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong|	|0|
+|123|	|456|	||
+|0|	||	||
 valid: yes
 	*** test5: done ***
  	*** test6 ***
-||	
-||	
-|abc|	
-|c"|	|d|	|de|	
-||	
-|k|	
-|e|	
-||	
-||	
-| |	
+||
+||
+|abc|
+|c"|	|d|	|de|
+||
+|k|
+|e|
+||
+||
+| |
 valid: NO
 	*** test6: done ***
  	*** big_chunk_separated_test ***
 line_cnt=10000, fieldsizes_cnt=1920000, 1920000
 	*** big_chunk_separated_test: done ***
  	*** random_generated_test ***
-||	
-| ba
- a
-a, 
-|	
-|a
-b|	
-|aa|	
-|b|	||	
-||	||	
-|,
-|	
-||	|a|	||	|a
-
-
- |	
-|ba|	
-||	|b  a|	||	
-||	|"a|	
-|b|	
-||	|,a
-,a ,
-c a|	|b|	
-||	|b
-aa|	
-|b|	
-||	
-||	
-||	||	
-|b|	
-|c|	||	
-||	
-|aa|	
-|
- ab
-ab,
- b|	
-|,   ,,
-bab
-b
-a
-a|	
-|a|	|"|	||	
-|a
-
- |	
-||	
-|a|	
-|a|	
-||	
-|aa a ,baab ,a 
-bb   ,
- 
-,
-b,,  b
-
-|	
-|b|	
-||	
-|b|	
-||	|ab|	
-|aa|	
-|
-b aba|	|b|	|caa 
-b|	
-||	|b"|	||	
-|ba|	
-| 
-
-a 
-b
-bbbba|	| 
-
-
-,a,b,a,b,
-
-
-
-b|	
+line_cnt=40, fieldsizes_cnt=183
 valid: yes
 	*** random_generated_test: done ***
  	*** common_test ***
-|first|	|last|	|address|	|city|	|zip|	
-|John|	|Doe|	|120 any st.|	|Anytown, WW|	|08123|	
+|first|	|last|	|address|	|city|	|zip|
+|John|	|Doe|	|120 any st.|	|Anytown, WW|	|08123|
 valid: yes
 	*** common_test: done ***
  	*** common_test ***
-|a|	|b|	|c|	
-|1|	|"|	|"|	
-|2|	|3|	|4|	
+|a|	|b|	|c|
+|1|	|"|	|"|
+|2|	|3|	|4|
 valid: yes
 	*** common_test: done ***
  	*** common_test ***
-|a|	|b|	
-|1|	|ha "ha" ha|	
-|3|	|4|	
+|a|	|b|
+|1|	|ha "ha" ha|
+|3|	|4|
 valid: yes
 	*** common_test: done ***
  	*** common_test ***
-|key|	|val|	
-|1|	|{"type": "Point", "coordinates": [102.0, 0.5]}|	
+|key|	|val|
+|1|	|{"type": "Point", "coordinates": [102.0, 0.5]}|
 valid: yes
 	*** common_test: done ***
  	*** common_test ***
-|a|	|b|	|c|	
-|1|	|2|	|3|	
+|a|	|b|	|c|
+|1|	|2|	|3|
 |Once upon 
-a time|	|5|	|6|	
-|7|	|8|	|9|	
+a time|	|5|	|6|
+|7|	|8|	|9|
 valid: yes
 	*** common_test: done ***
  	*** common_test ***
-|a|	|b|	
+|a|	|b|
 |1|	|ha
 "ha"
-ha|	
-|3|	|4|	
+ha|
+|3|	|4|
 valid: yes
 	*** common_test: done ***
  	*** common_test ***
-|a|	|b|	|c|	
-|1|	|2|	|3|	
-|4|	|5|	|а нет ли ошибок?|	
+|a|	|b|	|c|
+|1|	|2|	|3|
+|4|	|5|	|а нет ли ошибок?|
 valid: yes
 	*** common_test: done ***
  	*** common_test ***
-|www|	|aaa|	|tt  |	
+|www|	|aaa|	|tt  |
 valid: yes
 	*** common_test: done ***
  	*** iter_test1 ***
-||	|d|	|e|	
-|12|	|42|	|3|	
-|o|	
+||	|d|	|e|
+|12|	|42|	|3|
+|o|
 	*** iter_test1: done ***
  	*** iter_test2 ***
-|1|	
-|23|	
+|1|
+|23|
 	*** iter_test2: done ***
  	*** csv_out ***
-abc<len=3>,"with,comma"<len=12>,""in quotes""<len=13>,1 "" quote<len=10>
+abc<len=3>,"with,comma"<len=12>,""in quotes""<len=13>,1 "" quote<len=10>,<len=-1>
 	*** csv_out: done ***
  
\ No newline at end of file
-- 
GitLab