Remove luaL_buffer from file_g_read() (#1541)

* remove luaL_buffer from file_g_read()
- avoid memory leak when function gets terminated by lua_error
- skip scanning for end_char when reading until EOF
* attempt to free memory in any case
This commit is contained in:
Arnim Läuger 2016-10-28 16:32:12 +02:00 committed by Marcel Stör
parent 2227383843
commit 9db07783f4
2 changed files with 49 additions and 24 deletions

View File

@ -2,12 +2,17 @@
#include "module.h" #include "module.h"
#include "lauxlib.h" #include "lauxlib.h"
#include "lmem.h"
#include "platform.h" #include "platform.h"
#include "c_types.h" #include "c_types.h"
#include "vfs.h" #include "vfs.h"
#include "c_string.h" #include "c_string.h"
#include <alloca.h>
#define FILE_READ_CHUNK 1024
static int file_fd = 0; static int file_fd = 0;
static int rtc_cb_ref = LUA_NOREF; static int rtc_cb_ref = LUA_NOREF;
@ -255,36 +260,55 @@ static int file_rename( lua_State* L )
// g_read() // g_read()
static int file_g_read( lua_State* L, int n, int16_t end_char ) static int file_g_read( lua_State* L, int n, int16_t end_char )
{ {
if(n <= 0 || n > LUAL_BUFFERSIZE) static char *heap_mem = NULL;
n = LUAL_BUFFERSIZE; // free leftover memory
if (heap_mem)
luaM_free(L, heap_mem);
if(n <= 0)
n = FILE_READ_CHUNK;
if(end_char < 0 || end_char >255) if(end_char < 0 || end_char >255)
end_char = EOF; end_char = EOF;
luaL_Buffer b;
if(!file_fd) if(!file_fd)
return luaL_error(L, "open a file first"); return luaL_error(L, "open a file first");
luaL_buffinit(L, &b); char *p;
char *p = luaL_prepbuffer(&b);
int i; int i;
if (n > LUAL_BUFFERSIZE) {
// get buffer from heap
p = heap_mem = luaM_malloc(L, n);
} else {
// small chunks go onto the stack
p = alloca(n);
}
n = vfs_read(file_fd, p, n); n = vfs_read(file_fd, p, n);
for (i = 0; i < n; ++i) // bypass search if no end character provided
for (i = end_char != EOF ? 0 : n; i < n; ++i)
if (p[i] == end_char) if (p[i] == end_char)
{ {
++i; ++i;
break; break;
} }
if(i==0){ if (i == 0) {
luaL_pushresult(&b); /* close buffer */ if (heap_mem) {
return (lua_objlen(L, -1) > 0); /* check whether read something */ luaM_free(L, heap_mem);
heap_mem = NULL;
}
return 0;
} }
vfs_lseek(file_fd, -(n - i), VFS_SEEK_CUR); vfs_lseek(file_fd, -(n - i), VFS_SEEK_CUR);
luaL_addsize(&b, i); lua_pushlstring(L, p, i);
luaL_pushresult(&b); /* close buffer */ if (heap_mem) {
return 1; /* read at least an `eol' */ luaM_free(L, heap_mem);
heap_mem = NULL;
}
return 1;
} }
// Lua: read() // Lua: read()
@ -293,15 +317,12 @@ static int file_g_read( lua_State* L, int n, int16_t end_char )
// file.read('q') will read until 'q' or EOF is reached. // file.read('q') will read until 'q' or EOF is reached.
static int file_read( lua_State* L ) static int file_read( lua_State* L )
{ {
unsigned need_len = LUAL_BUFFERSIZE; unsigned need_len = FILE_READ_CHUNK;
int16_t end_char = EOF; int16_t end_char = EOF;
size_t el; size_t el;
if( lua_type( L, 1 ) == LUA_TNUMBER ) if( lua_type( L, 1 ) == LUA_TNUMBER )
{ {
need_len = ( unsigned )luaL_checkinteger( L, 1 ); need_len = ( unsigned )luaL_checkinteger( L, 1 );
if( need_len > LUAL_BUFFERSIZE ){
need_len = LUAL_BUFFERSIZE;
}
} }
else if(lua_isstring(L, 1)) else if(lua_isstring(L, 1))
{ {
@ -318,7 +339,7 @@ static int file_read( lua_State* L )
// Lua: readline() // Lua: readline()
static int file_readline( lua_State* L ) static int file_readline( lua_State* L )
{ {
return file_g_read(L, LUAL_BUFFERSIZE, '\n'); return file_g_read(L, FILE_READ_CHUNK, '\n');
} }
// Lua: write("string") // Lua: write("string")

View File

@ -298,14 +298,18 @@ end
Read content from the open file. Read content from the open file.
!!! note
The function temporarily allocates 2 * (number of requested bytes) on the heap for buffering and processing the read data. Default chunk size (`FILE_READ_CHUNK`) is 1024 bytes and is regarded to be safe. Pushing this by 4x or more can cause heap overflows depending on the application. Consider this when selecting a value for parameter `n_or_char`.
#### Syntax #### Syntax
`file.read([n_or_str])` `file.read([n_or_char])`
#### Parameters #### Parameters
- `n_or_str`: - `n_or_char`:
- if nothing passed in, read up to `LUAL_BUFFERSIZE` bytes (default 1024) or the entire file (whichever is smaller) - if nothing passed in, then read up to `FILE_READ_CHUNK` bytes or the entire file (whichever is smaller).
- if passed a number n, then read the file until the lesser of `n` bytes, `LUAL_BUFFERSIZE` bytes, or EOF is reached. Specifying a number larger than the buffer size will read the buffer size. - if passed a number `n`, then read up to `n` bytes or the entire file (whichever is smaller).
- if passed a string `str`, then read until `str` appears next in the file, `LUAL_BUFFERSIZE` bytes have been read, or EOF is reached - if passed a string containing the single character `char`, then read until `char` appears next in the file, `FILE_READ_CHUNK` bytes have been read, or EOF is reached.
#### Returns #### Returns
File content as a string, or nil when EOF File content as a string, or nil when EOF
@ -331,7 +335,7 @@ end
## file.readline() ## file.readline()
Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than `LUAL_BUFFERSIZE`, this function only returns the first `LUAL_BUFFERSIZE` bytes (this is 1024 bytes by default). Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than 1024, this function only returns the first 1024 bytes.
#### Syntax #### Syntax
`file.readline()` `file.readline()`