diff mbox series

[08/11] Lua: introduce image:read() method

Message ID 20171103123009.18705-8-christian.storm@siemens.com
State Accepted
Headers show
Series [01/11] Lua: expose get_tmpdir() to Lua | expand

Commit Message

Storm, Christian Nov. 3, 2017, 12:30 p.m. UTC
For C handlers, there's core/cpio_util.c's copyfile() as
a general method to extract data from the input stream and
copy it to somewhere. image:read(<callback()>) is the
analogous function exposed to Lua that reads from the input
stream and calls the callback Lua function <callback()> for
every chunk read, passing this chunk as parameter.

Signed-off-by: Christian Storm <christian.storm@siemens.com>
---
 corelib/lua_compat.c    | 39 ++++++++++++++++++++++++++
 corelib/lua_interface.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/lua_util.h      | 21 ++++++++++++++
 3 files changed, 133 insertions(+)

Comments

Stefano Babic Nov. 7, 2017, 4:19 p.m. UTC | #1
On 03/11/2017 13:30, Christian Storm wrote:
> For C handlers, there's core/cpio_util.c's copyfile() as
> a general method to extract data from the input stream and
> copy it to somewhere. image:read(<callback()>) is the
> analogous function exposed to Lua that reads from the input
> stream and calls the callback Lua function <callback()> for
> every chunk read, passing this chunk as parameter.
> 
> Signed-off-by: Christian Storm <christian.storm@siemens.com>
> ---
>  corelib/lua_compat.c    | 39 ++++++++++++++++++++++++++
>  corelib/lua_interface.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++
>  include/lua_util.h      | 21 ++++++++++++++
>  3 files changed, 133 insertions(+)
> 
> diff --git a/corelib/lua_compat.c b/corelib/lua_compat.c
> index 6204e8d..317f353 100644
> --- a/corelib/lua_compat.c
> +++ b/corelib/lua_compat.c
> @@ -25,6 +25,45 @@
>   * https://github.com/keplerproject/lua-compat-5.2
>   */
>  #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
> +char *luaL_prepbuffsize(luaL_Buffer_52 *B, size_t s)
> +{
> +	if (B->capacity - B->nelems < s) { /* needs to grow */
> +		char* newptr = NULL;
> +		size_t newcap = B->capacity * 2;
> +		if (newcap - B->nelems < s)
> +			newcap = B->nelems + s;
> +		if (newcap < B->capacity) /* overflow */
> +			luaL_error(B->L2, "buffer too large");
> +		newptr = lua_newuserdata(B->L2, newcap);
> +		memcpy(newptr, B->ptr, B->nelems);
> +		if (B->ptr != B->b.buffer)
> +			lua_replace(B->L2, -2); /* remove old buffer */
> +		B->ptr = newptr;
> +		B->capacity = newcap;
> +	}
> +	return B->ptr+B->nelems;
> +}
> +
> +void luaL_buffinit(lua_State *L, luaL_Buffer_52 *B)
> +{
> +	/* make it crash if used via pointer to a 5.1-style luaL_Buffer */
> +	B->b.p = NULL;
> +	B->b.L = NULL;
> +	B->b.lvl = 0;
> +	/* reuse the buffer from the 5.1-style luaL_Buffer though! */
> +	B->ptr = B->b.buffer;
> +	B->capacity = LUAL_BUFFERSIZE;
> +	B->nelems = 0;
> +	B->L2 = L;
> +}
> +
> +void luaL_pushresult(luaL_Buffer_52 *B)
> +{
> +	lua_pushlstring(B->L2, B->ptr, B->nelems);
> +	if (B->ptr != B->b.buffer)
> +		lua_replace(B->L2, -2); /* remove userdata buffer */
> +}
> +
>  void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup)
>  {
>  	luaL_checkstack(L, nup+1, "too many upvalues");
> diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
> index a551cde..7bb58ec 100644
> --- a/corelib/lua_interface.c
> +++ b/corelib/lua_interface.c
> @@ -321,6 +321,75 @@ copyfile_exit:
>  	close(fdout);
>  	return 2;
>  }
> +
> +static int istream_read_callback(void *out, const void *buf, unsigned int len)
> +{
> +	lua_State* L = (lua_State*)out;
> +	if (len > LUAL_BUFFERSIZE) {
> +		ERROR("I/O buffer size is larger than Lua's buffer size %d", LUAL_BUFFERSIZE);
> +		return -1;
> +	}
> +
> +	luaL_checktype(L, 2, LUA_TFUNCTION);
> +	lua_pushvalue(L, 2);
> +
> +	luaL_Buffer lbuffer;
> +	luaL_buffinit(L, &lbuffer);
> +	char *buffer = luaL_prepbuffsize(&lbuffer, len);
> +	memcpy(buffer, buf, len);
> +	luaL_addsize(&lbuffer, len);
> +	luaL_pushresult(&lbuffer);
> +	if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
> +		ERROR("Lua error in callback: %s", lua_tostring(L, -1));
> +		lua_pop(L, 1);
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static int l_istream_read(lua_State* L)
> +{
> +	luaL_checktype(L, 1, LUA_TTABLE);
> +	luaL_checktype(L, 2, LUA_TFUNCTION);
> +
> +	struct img_type img = {};
> +	uint32_t checksum = 0;
> +
> +	lua_pushvalue(L, 1);
> +	table2image(L, &img);
> +	lua_pop(L, 1);
> +
> +	int ret = copyfile(img.fdin,
> +				 L,
> +				 img.size,
> +				 (unsigned long *)&img.offset,
> +				 img.seek,
> +				 0, /* no skip */
> +				 img.compressed,
> +				 &checksum,
> +				 img.sha256,
> +				 img.is_encrypted,
> +				 istream_read_callback);
> +
> +	lua_pop(L, 1);
> +	update_table(L, &img);
> +	lua_pop(L, 1);
> +
> +	if (ret < 0) {
> +		lua_pushinteger(L, -1);
> +		lua_pushstring(L, strerror(errno));
> +		return 2;
> +	}
> +	if ((img.checksum != 0) && (checksum != img.checksum)) {
> +		lua_pushinteger(L, -1);
> +		lua_pushfstring(L, "Checksums WRONG! Computed 0x%d, should be 0x%d\n",
> +						checksum, img.checksum);
> +		return 2;
> +	}
> +	lua_pushinteger(L, 0);
> +	lua_pushnil(L);
> +	return 2;
> +}
>  #endif
>  
>  static void update_table(lua_State* L, struct img_type *img)
> @@ -354,6 +423,10 @@ static void update_table(lua_State* L, struct img_type *img)
>  		lua_pushstring(L, "copy2file");
>  		lua_pushcfunction(L, &l_copy2file);
>  		lua_settable(L, -3);
> +
> +		lua_pushstring(L, "read");
> +		lua_pushcfunction(L, &l_istream_read);
> +		lua_settable(L, -3);
>  #endif
>  
>  		lua_getfield(L, -1, "_private");
> diff --git a/include/lua_util.h b/include/lua_util.h
> index 03ef8d9..5be8c8d 100644
> --- a/include/lua_util.h
> +++ b/include/lua_util.h
> @@ -50,6 +50,27 @@ void luaL_requiref(lua_State *L, char const* modname, lua_CFunction openf, int g
>  typedef struct luaL_Stream {
>    FILE *f;
>  } luaL_Stream;
> +
> +typedef struct luaL_Buffer_52 {
> +	luaL_Buffer b; /* make incorrect code crash! */
> +	char *ptr;
> +	size_t nelems;
> +	size_t capacity;
> +	lua_State *L2;
> +} luaL_Buffer_52;
> +#define luaL_Buffer luaL_Buffer_52
> +
> +#define luaL_prepbuffsize luaL_prepbuffsize_52
> +char *luaL_prepbuffsize(luaL_Buffer_52 *B, size_t s);
> +
> +#define luaL_buffinit luaL_buffinit_52
> +void luaL_buffinit(lua_State *L, luaL_Buffer_52 *B);
> +
> +#undef luaL_addsize
> +#define luaL_addsize(B, s) ((B)->nelems += (s))
> +
> +#define luaL_pushresult luaL_pushresult_52
> +void luaL_pushresult(luaL_Buffer_52 *B);
>  #endif
>  
>  #else
> 
Applied to -master, thanks !

Best regards,
Stefano Babic
diff mbox series

Patch

diff --git a/corelib/lua_compat.c b/corelib/lua_compat.c
index 6204e8d..317f353 100644
--- a/corelib/lua_compat.c
+++ b/corelib/lua_compat.c
@@ -25,6 +25,45 @@ 
  * https://github.com/keplerproject/lua-compat-5.2
  */
 #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
+char *luaL_prepbuffsize(luaL_Buffer_52 *B, size_t s)
+{
+	if (B->capacity - B->nelems < s) { /* needs to grow */
+		char* newptr = NULL;
+		size_t newcap = B->capacity * 2;
+		if (newcap - B->nelems < s)
+			newcap = B->nelems + s;
+		if (newcap < B->capacity) /* overflow */
+			luaL_error(B->L2, "buffer too large");
+		newptr = lua_newuserdata(B->L2, newcap);
+		memcpy(newptr, B->ptr, B->nelems);
+		if (B->ptr != B->b.buffer)
+			lua_replace(B->L2, -2); /* remove old buffer */
+		B->ptr = newptr;
+		B->capacity = newcap;
+	}
+	return B->ptr+B->nelems;
+}
+
+void luaL_buffinit(lua_State *L, luaL_Buffer_52 *B)
+{
+	/* make it crash if used via pointer to a 5.1-style luaL_Buffer */
+	B->b.p = NULL;
+	B->b.L = NULL;
+	B->b.lvl = 0;
+	/* reuse the buffer from the 5.1-style luaL_Buffer though! */
+	B->ptr = B->b.buffer;
+	B->capacity = LUAL_BUFFERSIZE;
+	B->nelems = 0;
+	B->L2 = L;
+}
+
+void luaL_pushresult(luaL_Buffer_52 *B)
+{
+	lua_pushlstring(B->L2, B->ptr, B->nelems);
+	if (B->ptr != B->b.buffer)
+		lua_replace(B->L2, -2); /* remove userdata buffer */
+}
+
 void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup)
 {
 	luaL_checkstack(L, nup+1, "too many upvalues");
diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
index a551cde..7bb58ec 100644
--- a/corelib/lua_interface.c
+++ b/corelib/lua_interface.c
@@ -321,6 +321,75 @@  copyfile_exit:
 	close(fdout);
 	return 2;
 }
+
+static int istream_read_callback(void *out, const void *buf, unsigned int len)
+{
+	lua_State* L = (lua_State*)out;
+	if (len > LUAL_BUFFERSIZE) {
+		ERROR("I/O buffer size is larger than Lua's buffer size %d", LUAL_BUFFERSIZE);
+		return -1;
+	}
+
+	luaL_checktype(L, 2, LUA_TFUNCTION);
+	lua_pushvalue(L, 2);
+
+	luaL_Buffer lbuffer;
+	luaL_buffinit(L, &lbuffer);
+	char *buffer = luaL_prepbuffsize(&lbuffer, len);
+	memcpy(buffer, buf, len);
+	luaL_addsize(&lbuffer, len);
+	luaL_pushresult(&lbuffer);
+	if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
+		ERROR("Lua error in callback: %s", lua_tostring(L, -1));
+		lua_pop(L, 1);
+		return -1;
+	}
+	return 0;
+}
+
+static int l_istream_read(lua_State* L)
+{
+	luaL_checktype(L, 1, LUA_TTABLE);
+	luaL_checktype(L, 2, LUA_TFUNCTION);
+
+	struct img_type img = {};
+	uint32_t checksum = 0;
+
+	lua_pushvalue(L, 1);
+	table2image(L, &img);
+	lua_pop(L, 1);
+
+	int ret = copyfile(img.fdin,
+				 L,
+				 img.size,
+				 (unsigned long *)&img.offset,
+				 img.seek,
+				 0, /* no skip */
+				 img.compressed,
+				 &checksum,
+				 img.sha256,
+				 img.is_encrypted,
+				 istream_read_callback);
+
+	lua_pop(L, 1);
+	update_table(L, &img);
+	lua_pop(L, 1);
+
+	if (ret < 0) {
+		lua_pushinteger(L, -1);
+		lua_pushstring(L, strerror(errno));
+		return 2;
+	}
+	if ((img.checksum != 0) && (checksum != img.checksum)) {
+		lua_pushinteger(L, -1);
+		lua_pushfstring(L, "Checksums WRONG! Computed 0x%d, should be 0x%d\n",
+						checksum, img.checksum);
+		return 2;
+	}
+	lua_pushinteger(L, 0);
+	lua_pushnil(L);
+	return 2;
+}
 #endif
 
 static void update_table(lua_State* L, struct img_type *img)
@@ -354,6 +423,10 @@  static void update_table(lua_State* L, struct img_type *img)
 		lua_pushstring(L, "copy2file");
 		lua_pushcfunction(L, &l_copy2file);
 		lua_settable(L, -3);
+
+		lua_pushstring(L, "read");
+		lua_pushcfunction(L, &l_istream_read);
+		lua_settable(L, -3);
 #endif
 
 		lua_getfield(L, -1, "_private");
diff --git a/include/lua_util.h b/include/lua_util.h
index 03ef8d9..5be8c8d 100644
--- a/include/lua_util.h
+++ b/include/lua_util.h
@@ -50,6 +50,27 @@  void luaL_requiref(lua_State *L, char const* modname, lua_CFunction openf, int g
 typedef struct luaL_Stream {
   FILE *f;
 } luaL_Stream;
+
+typedef struct luaL_Buffer_52 {
+	luaL_Buffer b; /* make incorrect code crash! */
+	char *ptr;
+	size_t nelems;
+	size_t capacity;
+	lua_State *L2;
+} luaL_Buffer_52;
+#define luaL_Buffer luaL_Buffer_52
+
+#define luaL_prepbuffsize luaL_prepbuffsize_52
+char *luaL_prepbuffsize(luaL_Buffer_52 *B, size_t s);
+
+#define luaL_buffinit luaL_buffinit_52
+void luaL_buffinit(lua_State *L, luaL_Buffer_52 *B);
+
+#undef luaL_addsize
+#define luaL_addsize(B, s) ((B)->nelems += (s))
+
+#define luaL_pushresult luaL_pushresult_52
+void luaL_pushresult(luaL_Buffer_52 *B);
 #endif
 
 #else