From 2265144de8655d2a298946a428457a334a9dee9d Mon Sep 17 00:00:00 2001
From: Daniil Medvedev <medvdanil@gmail.com>
Date: Wed, 22 Jul 2015 15:08:00 +0300
Subject: [PATCH] csv style fix

---
 src/ffisyms.cc        |   2 +-
 src/lib/csv/csv.c     |  89 +++++++++++++++------------
 src/lib/csv/csv.h     |  28 +++++----
 src/lua/csv.lua       | 137 ++++++++++++++++++++++++------------------
 test/app/csv.test.lua |  15 +++--
 test/unit/csv.c       |  26 ++++----
 test/unit/csv.result  |  14 +----
 7 files changed, 165 insertions(+), 146 deletions(-)

diff --git a/src/ffisyms.cc b/src/ffisyms.cc
index 77a764afce..095b4f4a97 100644
--- a/src/ffisyms.cc
+++ b/src/ffisyms.cc
@@ -70,7 +70,7 @@ void *ffi_symbols[] = {
 	(void *) csv_create,
 	(void *) csv_destroy,
 	(void *) csv_setopt,
-	(void *) csv_iter_create,
+	(void *) csv_iterator_create,
 	(void *) csv_next,
 	(void *) csv_feed,
 };
diff --git a/src/lib/csv/csv.c b/src/lib/csv/csv.c
index 9640a822e7..454b9d8bc8 100644
--- a/src/lib/csv/csv.c
+++ b/src/lib/csv/csv.c
@@ -27,6 +27,8 @@ csv_create(struct csv *csv)
 	csv->csv_delim= ',';
 	csv->csv_quote = '\"';
 	csv->csv_realloc = realloc;
+	csv->emit_field = csv_emit_field_empty;
+	csv->emit_row = csv_emit_row_empty;
 }
 
 void
@@ -34,19 +36,28 @@ csv_destroy(struct csv *csv)
 {
 	if(csv->buf) {
 		csv->csv_realloc(csv->buf, 0);
-		csv->buf = 0;
+		csv->buf = NULL;
 	}
 }
 
-int csv_isvalid(struct csv *csv)
+int
+csv_isvalid(struct csv *csv)
 {
 	if (csv->prevsymb == csv->csv_quote) {
 		csv->state = csv->state == CSV_BUF_IN_QUOTES ? CSV_BUF_OUT_OF_QUOTES : CSV_BUF_IN_QUOTES;
 		csv->prevsymb = ' ';
 	}
-	csv->csv_invalid = csv->state  == CSV_BUF_IN_QUOTES;
-	return !csv->csv_invalid;
+	if (csv->csv_error_status == CSV_ER_OK && csv->state  == CSV_BUF_IN_QUOTES)
+		csv->csv_error_status = CSV_ER_INVALID;
+	return !csv->csv_error_status;
+}
+
+int
+csv_get_error_status(struct csv *csv)
+{
+	return csv->csv_error_status;
 }
+
 void
 csv_setopt(struct csv *csv, int opt, ...)
 {
@@ -60,12 +71,12 @@ csv_setopt(struct csv *csv, int opt, ...)
 		csv->csv_quote = va_arg(args, int);
 		break;
 	case CSV_OPT_REALLOC:
-		csv->csv_realloc = va_arg(args, void*);
+		csv->csv_realloc = va_arg(args, void* (*)(void*, long unsigned int));
 		break;
 	case CSV_OPT_EMIT_FIELD:
-		csv->emit_field = va_arg(args, void*);
+		csv->emit_field = va_arg(args, void (*)(void*, const char *, const char *));
 	case CSV_OPT_EMIT_ROW:
-		csv->emit_row = va_arg(args, void*);
+		csv->emit_row = va_arg(args, void (*)(void*));
 	case CSV_OPT_CTX:
 		csv->emit_ctx = va_arg(args, void*);
 	}
@@ -76,12 +87,10 @@ const char *
 csv_parse_common(struct csv *csv, const char *s, const char *end, int onlyfirst)
 {
 	if (end - s == 0)
-		return 0;
+		return NULL;
 	assert(end - s > 0);
-	if (csv->emit_field == NULL)
-		csv->emit_field = csv_emit_field_empty;
-	if (csv->emit_row == NULL)
-		csv->emit_row = csv_emit_row_empty;
+	assert(csv->emit_field);
+	assert(csv->emit_row);
 	const char *p = s;
 
 	 while(p != end) {
@@ -89,7 +98,11 @@ csv_parse_common(struct csv *csv, const char *s, const char *end, int onlyfirst)
 
 		if (csv->buf == 0 || (csv->bufp && csv->buf_len < csv->bufp - csv->buf + 1)) {
 			csv->buf_len = (int)((csv->bufp - csv->buf + 1) * csv_buf_expand_factor + 1);
-			char *new_buf = csv->csv_realloc(csv->buf, csv->buf_len);
+			char *new_buf = (char *) csv->csv_realloc(csv->buf, csv->buf_len);
+			if(new_buf == NULL) {
+				csv->csv_error_status = CSV_ER_MEMORY_ERROR;
+				return NULL;
+			}
 			csv->bufp = csv->bufp - csv->buf + new_buf;
 			csv->buf = new_buf;
 		}
@@ -111,12 +124,12 @@ csv_parse_common(struct csv *csv, const char *s, const char *end, int onlyfirst)
 		}
 		csv->prevsymb = *p;
 		switch(csv->state) {
-		case CSV_LEADING_SPACES: //leading spaces
+		case CSV_LEADING_SPACES:
 			csv->bufp = csv->buf;
 			if (*p != ' ') {
 				csv->state = CSV_BUF_OUT_OF_QUOTES;
 			}
-			else break;
+			else break; //spaces passed, perform field at once
 		case CSV_BUF_OUT_OF_QUOTES:
 			if (isendl || *p == csv->csv_delim) {
 				csv->state = CSV_LEADING_SPACES;
@@ -153,7 +166,7 @@ csv_parse_common(struct csv *csv, const char *s, const char *end, int onlyfirst)
 			csv->bufp = 0;
 			if(onlyfirst) {
 				if(p + 1 == end)
-					return 0;
+					return NULL;
 				else
 					return p + 1;
 			}
@@ -183,15 +196,15 @@ csv_finish_parsing(struct csv *csv)
 		}
 		if (csv->buf)
 			csv->csv_realloc(csv->buf, 0);
-		csv->bufp = 0;
-		csv->buf = 0;
+		csv->bufp = NULL;
+		csv->buf = NULL;
 		csv->buf_len = 0;
 	}
 }
 
 
 void
-csv_iter_create(struct csv_iterator *it, struct csv *csv)
+csv_iterator_create(struct csv_iterator *it, struct csv *csv)
 {
 	memset(it, 0, sizeof(struct csv_iterator));
 	it->csv = csv;
@@ -199,20 +212,19 @@ csv_iter_create(struct csv_iterator *it, struct csv *csv)
 
 int
 csv_next(struct csv_iterator *it) {
-	it->field = 0;
+	it->field = NULL;
 	it->field_len = 0;
-	if(it->buf_begin == 0)
+	if(it->buf_begin == NULL)
 		return CSV_IT_NEEDMORE;
 	if(it->buf_begin == it->buf_end) {
-		if (!it->csv->csv_invalid && !csv_isvalid(it->csv)) {
-			it->csv->csv_invalid = 1;
+		if (!it->csv->csv_error_status && !csv_isvalid(it->csv)) {
 			it->csv->csv_realloc(it->csv->buf, 0);
-			it->csv->buf = 0;
-			it->csv->bufp = 0;
+			it->csv->buf = NULL;
+			it->csv->bufp = NULL;
 			it->csv->buf_len = 0;
 			return CSV_IT_ERROR;
 		}
-		if(it->csv->bufp == 0) {
+		if(it->csv->bufp == NULL) {
 			return CSV_IT_EOF;
 		}
 		if(it->csv->state != CSV_END_OF_INPUT) {
@@ -224,15 +236,17 @@ csv_next(struct csv_iterator *it) {
 			return CSV_IT_OK;
 		} else {
 			it->csv->csv_realloc(it->csv->buf, 0);
-			it->csv->buf = 0;
-			it->csv->bufp = 0;
+			it->csv->buf = NULL;
+			it->csv->bufp = NULL;
 			it->csv->buf_len = 0;
 			return CSV_IT_EOL;
 		}
 	}
 	const char *tail = csv_parse_common(it->csv, it->buf_begin, it->buf_end, 1);
+	if(csv_get_error_status(it->csv) == CSV_ER_MEMORY_ERROR)
+		return CSV_IT_ERROR;
 	it->buf_begin = tail;
-	if(it->csv->bufp == 0 && it->csv->prevsymb)
+	if(it->csv->bufp == NULL && it->csv->prevsymb)
 		return CSV_IT_EOL;
 	if(tail == it->buf_end)
 		return CSV_IT_NEEDMORE;
@@ -242,13 +256,13 @@ csv_next(struct csv_iterator *it) {
 }
 
 void
-csv_feed(struct csv_iterator *it, const char *str)
+csv_feed(struct csv_iterator *it, const char *buf, size_t buf_len)
 {
-	it->buf_begin = str;
-	it->buf_end = str + strlen(str);
+	it->buf_begin = buf;
+	it->buf_end = buf + buf_len;
 }
 
-int
+size_t
 csv_escape_field(struct csv *csv, const char *field, size_t field_len, char *dst, size_t buf_size)
 {
 	char *p = dst;
@@ -259,17 +273,14 @@ csv_escape_field(struct csv *csv, const char *field, size_t field_len, char *dst
 	}
 	while(*field) {
 		if(*field == csv->csv_quote) {
-			if(p - dst >= buf_size)
-				return -1;
+			assert(p - dst < buf_size);
 			*p++ = csv->csv_quote;
 		}
-		if(p - dst >= buf_size)
-			return -1;
+		assert(p - dst < buf_size);
 		*p++ = *field++;
 	}
 	if(inquotes) {
-		if(p - dst >= buf_size)
-			return -1;
+		assert(p - dst < buf_size);
 		*p++ = csv->csv_quote;
 	}
 	*p = 0;
diff --git a/src/lib/csv/csv.h b/src/lib/csv/csv.h
index 8faaadc85e..5b9854a4c2 100644
--- a/src/lib/csv/csv.h
+++ b/src/lib/csv/csv.h
@@ -19,7 +19,7 @@ struct csv
 	char csv_quote;
 
 	char prevsymb;
-	int csv_invalid;
+	int csv_error_status;
 	int csv_ending_spaces;
 
 	void *(*csv_realloc)(void*, size_t);
@@ -30,8 +30,7 @@ struct csv
 	size_t buf_len;
 };
 
-//parser options
-enum {
+enum parser_options {
 	CSV_OPT_DELIMITER,
 	CSV_OPT_QUOTE,
 	CSV_OPT_REALLOC,
@@ -40,8 +39,7 @@ enum {
 	CSV_OPT_CTX
 };
 
-//iteraion states
-enum {
+enum iteraion_states {
 	CSV_IT_OK,
 	CSV_IT_EOL,
 	CSV_IT_NEEDMORE,
@@ -49,14 +47,20 @@ enum {
 	CSV_IT_ERROR
 };
 
-//parser states
-enum {  CSV_LEADING_SPACES,
+enum parser_states {
+	CSV_LEADING_SPACES,
 	CSV_BUF_OUT_OF_QUOTES,
 	CSV_BUF_IN_QUOTES,
 	CSV_NEWLINE,
 	CSV_END_OF_INPUT
 };
 
+enum error_status {
+	CSV_ER_OK,
+	CSV_ER_INVALID,
+	CSV_ER_MEMORY_ERROR
+};
+
 void
 csv_create(struct csv *csv);
 
@@ -84,10 +88,10 @@ void
 csv_finish_parsing(struct csv *csv);
 
 /**
- * if quote not closed returns 0
+ * @return 0 is ok
  */
 int
-csv_isvalid(struct csv *csv);
+csv_get_error_status(struct csv *csv);
 
 /**
  * @brief The csv_iterator struct allows iterate field by field through csv
@@ -103,7 +107,7 @@ struct csv_iterator {
 };
 
 void
-csv_iter_create(struct csv_iterator *it, struct csv *csv);
+csv_iterator_create(struct csv_iterator *it, struct csv *csv);
 /**
  * Recieves next element from csv
  * element is field or end of line
@@ -117,7 +121,7 @@ csv_next(struct csv_iterator *);
  * empty buffer means end of iteration
  */
 void
-csv_feed(struct csv_iterator *, const char *);
+csv_feed(struct csv_iterator *it, const char *buf, size_t buf_len);
 
 /**
  * @brief csv_escape_field prepares field to out in file.
@@ -125,7 +129,7 @@ csv_feed(struct csv_iterator *, const char *);
  * 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
+size_t
 csv_escape_field(struct csv *csv, const char *field, size_t field_len, char *dst, size_t buf_size);
 
 
diff --git a/src/lua/csv.lua b/src/lua/csv.lua
index 0fd0b93edc..c06ba5ab3c 100644
--- a/src/lua/csv.lua
+++ b/src/lua/csv.lua
@@ -36,12 +36,10 @@ ffi.cdef[[
         const char *field;
         size_t field_len;
     };
-    typedef struct csv csv_t;
-    typedef struct csv_iterator csv_iterator_t;
-    void csv_iter_create(struct csv_iterator *it, struct csv *csv);
+    void csv_iterator_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 *csv, const char *field, size_t field_len, char *dst, size_t buf_size);
+    void csv_feed(struct csv_iterator *, const char *, size_t);
+    size_t 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,17 +49,6 @@ ffi.cdef[[
     };
 ]]
 
-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 = ""
@@ -81,12 +68,21 @@ local iter = function(csvstate, i)
     local st = ffi.C.csv_next(it)
     while st ~= ffi.C.CSV_IT_EOF do
         if st == ffi.C.CSV_IT_NEEDMORE then
-            ffi.C.csv_feed(it, readable:read(csv_chunk_size))
+            if readable then
+                local buf = readable:read(csv_chunk_size)
+                ffi.C.csv_feed(it, buf, string.len(buf))
+            else
+                ffi.C.csv_feed(it, "", 0)
+            end
         elseif st == ffi.C.CSV_IT_EOL then
             i = i + 1
-            return i, tup
+            if i > 0 then
+                return i, tup
+            end
         elseif st == ffi.C.CSV_IT_OK then
-            table.insert(tup, ffi.string(it[0].field, it[0].field_len))
+            if i >= 0 then
+                tup[#tup + 1] = ffi.string(it.field, it.field_len)
+            end
         elseif st == ffi.C.CSV_IT_ERROR then
             log.warn("CSV file has errors")
             break
@@ -100,69 +96,94 @@ end
 
 local module = {}
 
-module.delimiter = ','
-module.quote = '"'
-
 --@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
+--@param opts.chunk_size (default 4096). Parser will read by chunk_size symbols
+--@param opts.delimiter (default ',').
+--@param opts.quote (default '"'). 
+--@param opts.skip_head_lines (default 0). Skip header. 
 --@return iter function, iterator state
-module.iterate = function(readable, csv_chunk_size)
-    csv_chunk_size = csv_chunk_size or 4096
-    if type(readable) == "string" then
-        readable = make_readable(readable)
+module.iterate = function(readable, opts)
+    opts = opts or {}
+    if not opts.chunk_size then
+        opts.chunk_size = 4096
     end
-    if type(readable.read) ~= "function" then
-        error("Usage: load(object with method read(num) returns string)")
+    if not opts.delimiter then
+        opts.delimiter = ','
+    end
+    if not opts.quote then
+        opts.quote = '"'
+    end
+    if not opts.skip_head_lines then
+        opts.skip_head_lines = 0
+    end
+    if type(readable) ~= "string" and type(readable.read) ~= "function" then
+        error("Usage: load(string or object with method read(num) returns string)")
+    end
+    local str
+    if type(readable) == "string" then
+        str = readable
+        readable = null
+    else
+        str = readable:read(opts.chunk_size)
     end
-
-    local str = readable:read(csv_chunk_size)
     if not str then
-        error("Usage: load(object with method read(num) returns string)")
+        error("Usage: load(string or object with method read(num) returns string)")
     end
-    local it = ffi.new('csv_iterator_t[1]')
-    local csv = ffi.new('csv_t[1]')
+    local it = ffi.new('struct csv_iterator')
+    local csv = ffi.new('struct csv')
     ffi.C.csv_create(csv)
-    csv[0].csv_delim = string.byte(module.delimiter)
-    csv[0].csv_quote = string.byte(module.quote)
-    ffi.C.csv_iter_create(it, csv)
-    ffi.C.csv_feed(it, str)
-
-    return iter, {readable, csv_chunk_size, csv, it}, 0
+    ffi.gc(csv, ffi.C.csv_destroy)
+    
+    csv.csv_delim = string.byte(opts.delimiter)
+    csv.csv_quote = string.byte(opts.quote)
+    ffi.C.csv_iterator_create(it, csv)
+    ffi.C.csv_feed(it, str, string.len(str))
+
+    return iter, {readable, opts.chunk_size, csv, it}, -opts.skip_head_lines
 end
 
 --@brief parse csv and make table
---@param skip_lines is number of lines to skip.
 --@return table
-module.load = function(readable, skip_lines, csv_chunk_size)
-    skip_lines = skip_lines or 0
-    csv_chunk_size = csv_chunk_size or 4096
+module.load = function(readable, opts)
+    opts = opts or {}
     result = {}
-    for i, tup in module.iterate(readable, csv_chunk_size) do
-        if i > skip_lines then             
-            result[i - skip_lines] = tup
-        end
+    for i, tup in module.iterate(readable, opts) do
+        result[i] = tup
     end
-
     return result
 end
 
 --@brief dumps tuple or table as csv
+--@param t is tuple or table
 --@param writable must be object with method write(string) like file or socket
+--@param opts.delimiter (default ',').
+--@param opts.quote (default '"'). 
 --@return there is no writable it returns csv as string
-module.dump = function(t, writable)
+module.dump = function(t, opts, writable)
+    opts = opts or {}
+    writable = writable or null
+    if not opts.delimiter then
+        opts.delimiter = ','
+    end
+    if not opts.quote then
+        opts.quote = '"'
+    end
+    
     if type(writable) == "nil" then
         writable = make_writable()
     end
     if type(writable.write) ~= "function" or type(t) ~= "table" then
-        error("Usage: dump(writable, table)")
+        error("Usage: dump(table[, opts, writable])")
     end
-    local csv = ffi.new('csv_t[1]')
+    local csv = ffi.new('struct csv')
     ffi.C.csv_create(csv)
-    csv[0].csv_delim = string.byte(module.delimiter)
-    csv[0].csv_quote = string.byte(module.quote)
+    ffi.gc(csv, ffi.C.csv_destroy)
+    csv.csv_delim = string.byte(opts.delimiter)
+    csv.csv_quote = string.byte(opts.quote)
+    
     local bufsz = 256
-    local buf = csv[0].csv_realloc(ffi.cast(ffi.typeof('void *'), 0), bufsz)
+    local buf = csv.csv_realloc(ffi.cast(ffi.typeof('void *'), 0), bufsz)
     if type(t[1]) ~= 'table' then
         t = {t}
     end
@@ -172,20 +193,20 @@ module.dump = function(t, writable)
             strf = tostring(field)
             if (strf:len() + 1) * 2 > bufsz then
                 bufsz = (strf:len() + 1) * 2
-                buf = csv[0].csv_realloc(buf, bufsz)
+                buf = csv.csv_realloc(buf, bufsz)
             end
             local len = ffi.C.csv_escape_field(csv, strf, string.len(strf), buf, bufsz)
             if first then
                 first = false
             else
-                writable:write(module.delimiter)
+                writable:write(opts.delimiter)
             end
             writable:write(ffi.string(buf, len))
         end
         writable:write('\n')
     end
     ffi.C.csv_destroy(csv)
-    csv[0].csv_realloc(buf, 0)
+    csv.csv_realloc(buf, 0)
     if writable.returnstring then
         return writable.returnstring
     end
diff --git a/test/app/csv.test.lua b/test/app/csv.test.lua
index 06ebaefec4..720eabd3d4 100755
--- a/test/app/csv.test.lua
+++ b/test/app/csv.test.lua
@@ -41,11 +41,11 @@ test:is(table2str(csv.load(readable)), test1, "obj test1")
 
 readable.v = ", ,\n , \n\n"
 readable.i = 0
-test:is(table2str(csv.load(readable, 0, 1)), test2, "obj test2")
+test:is(table2str(csv.load(readable, {chunk_size = 1} )), test2, "obj test2")
 
 readable.v = ", \r\nkp\"\"v"
 readable.i = 0
-test:is(table2str(csv.load(readable, 0, 3)), test3, "obj test3")
+test:is(table2str(csv.load(readable, {chunk_size = 3})), test3, "obj test3")
 
 tmpdir = fio.tempdir()
 file1 = fio.pathjoin(tmpdir, 'file.1')
@@ -57,7 +57,7 @@ f:write("123 , 5  ,       92    , 0, 0\n" ..
         "1, 12  34, 56, \"quote , \", 66\nok")
 f:close()
 f = fio.open(file1, {'O_RDONLY'}) 
-test:is(table2str(csv.load(f,0,10)), test4, "fio test1")
+test:is(table2str(csv.load(f, {chunk_size = 10})), test4, "fio test1")
 f:close()
 
 
@@ -72,14 +72,13 @@ f:write("1\n23,456,abcac,\'multiword field 4\'\n" ..
 )
 f:close()
 f = fio.open(file2, {'O_RDONLY'}) 
-test:is(table2str(csv.load(f, 0, 1)), test5, "fio test2") --symbol by symbol reading
+test:is(table2str(csv.load(f, {chunk_size = 1})), test5, "fio test2") --symbol by symbol reading
 f:close()
 
 f = fio.open(file2, {'O_RDONLY'}) 
-test:is(table2str(csv.load(f, 1, 7)), test6, "fio test3") --7 symbols per chunk
+test:is(table2str(csv.load(f, {chunk_size = 7, skip_head_lines = 1})), test6, "fio test3") --7 symbols per chunk
 f:close()
 
-
 t = {
     {'quote" d', ',and, comma', 'both " of " t,h,e,m'}, 
     {'"""', ',","'}, 
@@ -90,10 +89,10 @@ t = {
 }
 
 f = require("fio").open(file3, { "O_WRONLY", "O_TRUNC" , "O_CREAT"}, 0x1FF)
-csv.dump(t, f)
+csv.dump(t, {}, f)
 f:close()
 f = fio.open(file3, {'O_RDONLY'}) 
-t2 = csv.load(f, 0, 5)
+t2 = csv.load(f, {chunk_size = 5})
 f:close()
 
 test:is(table2str(t), table2str(t2), "test roundtrip")
diff --git a/test/unit/csv.c b/test/unit/csv.c
index 6863254967..ce7561c855 100644
--- a/test/unit/csv.c
+++ b/test/unit/csv.c
@@ -106,7 +106,7 @@ void test5() {
 	csv_setopt(&csv, CSV_OPT_DELIMITER, '\t');
 	csv_parse_chunk(&csv, s, s + strlen(s));
 	csv_finish_parsing(&csv);
-	printf("valid: %s\n", csv.csv_invalid ? "NO" : "yes");
+	printf("valid: %s\n", csv.csv_error_status == CSV_ER_INVALID ? "NO" : "yes");
 	csv_destroy(&csv);
 	footer();
 }
@@ -123,7 +123,7 @@ void test6() {
 	csv_parse_chunk(&csv, s2, s2 + 2);
 	csv_parse_chunk(&csv, s2 + 2, s2 + strlen(s2));
 	csv_finish_parsing(&csv);
-	printf("valid: %s\n", csv.csv_invalid ? "NO" : "yes");
+	printf("valid: %s\n", csv_get_error_status(&csv) == CSV_ER_INVALID ? "NO" : "yes");
 	csv_destroy(&csv);
 	footer();
 }
@@ -218,7 +218,7 @@ void random_generated_test() {
 	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");
+	printf("valid: %s\n", csv_get_error_status(&csv) == CSV_ER_INVALID ? "NO" : "yes");
 	csv_destroy(&csv);
 
 	footer();
@@ -229,13 +229,13 @@ void iter_test1() {
 	struct csv_iterator it;
 	struct csv csv;
 	csv_create(&csv);
-	csv_iter_create(&it, &csv);
+	csv_iterator_create(&it, &csv);
 	int st = 0;
 	const char *buf = ",d ,e\r\n12,42,3\no\n";
 	while((st = csv_next(&it)) != CSV_IT_EOF) {
 		switch(st) {
 		case CSV_IT_NEEDMORE:
-			csv_feed(&it, buf);
+			csv_feed(&it, buf, strlen(buf));
 			buf += strlen(buf);
 			break;
 		case CSV_IT_EOL:
@@ -258,14 +258,14 @@ void iter_test2() {
 	struct csv_iterator it;
 	struct csv csv;
 	csv_create(&csv);
-	csv_iter_create(&it, &csv);
+	csv_iterator_create(&it, &csv);
 	int st = 0;
 	const char ar[] = {'1', '\n', 0, '2', '3', 0, 0};
 	const char *buf = ar;
 	while((st = csv_next(&it)) != CSV_IT_EOF) {
 		switch(st) {
 		case CSV_IT_NEEDMORE:
-			csv_feed(&it, buf);
+			csv_feed(&it, buf, strlen(buf));
 			buf += 3;
 			break;
 		case CSV_IT_EOL:
@@ -286,18 +286,14 @@ void iter_test2() {
 void csv_out() {
 	header();
 
-	const char fields[5][24] = { "abc", "with,comma", "\"in quotes\"", "1 \" quote",
-				     "long field, return \"-1\"" };
-	char buf[24];
+	const char fields[4][24] = { "abc", "with,comma", "\"in quotes\"", "1 \" quote"};
+	char buf[54];
 	int i;
 	struct csv csv;
 	csv_create(&csv);
-	for(i = 0; i < 5; i++) {
+	for(i = 0; i < 4; 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' : ',');
+		printf("%s<len=%d>%c", buf, len, i == 3 ? '\n' : ',');
 	}
 
 	footer();
diff --git a/test/unit/csv.result b/test/unit/csv.result
index 61a66cb1a0..d27ee8f7a6 100644
--- a/test/unit/csv.result
+++ b/test/unit/csv.result
@@ -3,24 +3,20 @@
 ||
 |1|	|2|	|3|
 |123|
-valid: yes
 	*** test1: done ***
  	*** test2 ***
 |123|	|456|	|abcac|	|'multiword field 4'|
 |none|	|none|	|0|
 ||	||	||
 ||	||	||
-valid: yes
 	*** test2: done ***
  	*** test3 ***
 |1|	||	|2|
-valid: yes
 	*** test3: done ***
  	*** test4 ***
 |123|	|5|	|92|	|0|	|0|
 |1|	|12  34|	|56|	|quote , |	|66|
 |ok|
-valid: yes
 	*** test4: done ***
  	*** test5 ***
 |abc|	|longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong|	|0|
@@ -51,24 +47,20 @@ valid: yes
  	*** common_test ***
 |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|
-valid: yes
 	*** common_test: done ***
  	*** common_test ***
 |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]}|
-valid: yes
 	*** common_test: done ***
  	*** common_test ***
 |a|	|b|	|c|
@@ -76,7 +68,6 @@ valid: yes
 |Once upon 
 a time|	|5|	|6|
 |7|	|8|	|9|
-valid: yes
 	*** common_test: done ***
  	*** common_test ***
 |a|	|b|
@@ -84,17 +75,14 @@ valid: yes
 "ha"
 ha|
 |3|	|4|
-valid: yes
 	*** common_test: done ***
  	*** common_test ***
 |a|	|b|	|c|
 |1|	|2|	|3|
 |4|	|5|	|а нет ли ошибок?|
-valid: yes
 	*** common_test: done ***
  	*** common_test ***
 |www|	|aaa|	|tt  |
-valid: yes
 	*** common_test: done ***
  	*** iter_test1 ***
 ||	|d|	|e|
@@ -106,6 +94,6 @@ valid: yes
 |23|
 	*** iter_test2: done ***
  	*** csv_out ***
-abc<len=3>,"with,comma"<len=12>,""in quotes""<len=13>,1 "" quote<len=10>,<len=-1>
+abc<len=3>,"with,comma"<len=12>,""in quotes""<len=13>,1 "" quote<len=10>
 	*** csv_out: done ***
  
\ No newline at end of file
-- 
GitLab