[Tarantool-patches] [PATCH luajit v2 2/7] core: introduce write buffer module

Sergey Kaplun skaplun at tarantool.org
Fri Dec 25 18:26:04 MSK 2020


This patch introduces the standalone module for writing data to the
file, socket or memory (and so on) via the special buffer.
The module provides the API for buffer initial setup
and its convenient usage.

Part of tarantool/tarantool#5442
---

Changes in v2:
  - Removed custom memcpy.
  - lj_wbuf_addn() fills buffer to the end first and then flushes.
  - Changed assert in lj_wbuf_flush() to early return.

 src/Makefile     |   2 +-
 src/Makefile.dep |  23 ++++----
 src/lj_wbuf.c    | 141 +++++++++++++++++++++++++++++++++++++++++++++++
 src/lj_wbuf.h    |  87 +++++++++++++++++++++++++++++
 src/ljamalg.c    |   1 +
 5 files changed, 242 insertions(+), 12 deletions(-)
 create mode 100644 src/lj_wbuf.c
 create mode 100644 src/lj_wbuf.h

diff --git a/src/Makefile b/src/Makefile
index dc2ddb6..384b590 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -485,7 +485,7 @@ LJLIB_O= lib_base.o lib_math.o lib_bit.o lib_string.o lib_table.o \
 	 lib_misc.o
 LJLIB_C= $(LJLIB_O:.o=.c)
 
-LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \
+LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o lj_wbuf.o \
 	  lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \
 	  lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \
 	  lj_strfmt.o lj_strfmt_num.o lj_api.o lj_mapi.o lj_profile.o \
diff --git a/src/Makefile.dep b/src/Makefile.dep
index 75409bf..59ed450 100644
--- a/src/Makefile.dep
+++ b/src/Makefile.dep
@@ -211,28 +211,29 @@ lj_vmevent.o: lj_vmevent.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
  lj_vm.h lj_vmevent.h
 lj_vmmath.o: lj_vmmath.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
  lj_ir.h lj_vm.h
+lj_wbuf.o: lj_wbuf.c lj_wbuf.h lj_def.h lua.h luaconf.h lj_utils.h
 ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \
  lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h \
  lj_func.h lj_udata.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h \
  lj_cdata.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h \
  lj_vm.h lj_err.c lj_debug.h lj_ff.h lj_ffdef.h lj_strfmt.h lj_char.c \
- lj_char.h lj_bc.c lj_bcdef.h lj_obj.c lj_buf.c lj_str.c lj_tab.c \
- lj_func.c lj_udata.c lj_meta.c lj_strscan.h lj_lib.h lj_debug.c \
- lj_state.c lj_lex.h lj_alloc.h luajit.h lj_dispatch.c lj_ccallback.h \
- lj_profile.h lj_vmevent.c lj_vmevent.h lj_vmmath.c lj_strscan.c \
- lj_strfmt.c lj_strfmt_num.c lj_api.c lj_mapi.c lmisclib.h lj_profile.c \
- lj_lex.c lualib.h lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h lj_bcwrite.c \
- lj_load.c lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c lj_ccall.h \
- lj_ccallback.c lj_target.h lj_target_*.h lj_mcode.h lj_carith.c \
+ lj_char.h lj_bc.c lj_bcdef.h lj_obj.c lj_buf.c lj_wbuf.c lj_wbuf.h lj_utils.h \
+ lj_str.c lj_tab.c lj_func.c lj_udata.c lj_meta.c lj_strscan.h lj_lib.h \
+ lj_debug.c lj_state.c lj_lex.h lj_alloc.h luajit.h lj_dispatch.c \
+ lj_ccallback.h lj_profile.h lj_vmevent.c lj_vmevent.h lj_vmmath.c \
+ lj_strscan.c lj_strfmt.c lj_strfmt_num.c lj_api.c lj_mapi.c lmisclib.h \
+ lj_profile.c lj_lex.c lualib.h lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h \
+ lj_bcwrite.c lj_load.c lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c \
+ lj_ccall.h lj_ccallback.c lj_target.h lj_target_*.h lj_mcode.h lj_carith.c \
  lj_carith.h lj_clib.c lj_clib.h lj_cparse.c lj_cparse.h lj_lib.c lj_ir.c \
  lj_ircall.h lj_iropt.h lj_opt_mem.c lj_opt_fold.c lj_folddef.h \
  lj_opt_narrow.c lj_opt_dce.c lj_opt_loop.c lj_snap.h lj_opt_split.c \
  lj_opt_sink.c lj_mcode.c lj_snap.c lj_record.c lj_record.h lj_ffrecord.h \
  lj_crecord.c lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h \
  lj_emit_*.h lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c \
- lj_utils_leb128.c lj_utils.h lib_aux.c lib_base.c lj_libdef.h lib_math.c \
- lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c \
- lib_bit.c lib_jit.c lib_ffi.c lib_misc.c lib_init.c
+ lj_utils_leb128.c lib_aux.c lib_base.c lj_libdef.h lib_math.c lib_string.c \
+ lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c \
+ lib_ffi.c lib_misc.c lib_init.c
 luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h
 host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \
  lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \
diff --git a/src/lj_wbuf.c b/src/lj_wbuf.c
new file mode 100644
index 0000000..8f090eb
--- /dev/null
+++ b/src/lj_wbuf.c
@@ -0,0 +1,141 @@
+/*
+** Low-level writer for LuaJIT.
+**
+** Major portions taken verbatim or adapted from the LuaVela.
+** Copyright (C) 2015-2019 IPONWEB Ltd.
+*/
+
+#define lj_wbuf_c
+#define LUA_CORE
+
+#include <errno.h>
+
+#include "lj_wbuf.h"
+#include "lj_utils.h"
+
+static LJ_AINLINE void wbuf_set_flag(struct lj_wbuf *buf, uint8_t flag)
+{
+  buf->flags |= flag;
+}
+
+static LJ_AINLINE void wbuf_save_errno(struct lj_wbuf *buf)
+{
+  buf->saved_errno = errno;
+}
+
+static LJ_AINLINE size_t wbuf_len(const struct lj_wbuf *buf)
+{
+  return (size_t)(buf->pos - buf->buf);
+}
+
+static LJ_AINLINE size_t wbuf_left(const struct lj_wbuf *buf)
+{
+  return buf->size - wbuf_len(buf);
+}
+
+void lj_wbuf_init(struct lj_wbuf *buf, lj_wbuf_writer writer,
+		  void *ctx, uint8_t *mem, size_t size)
+{
+  buf->ctx = ctx;
+  buf->writer = writer;
+  buf->buf = mem;
+  buf->pos = mem;
+  buf->size = size;
+  buf->flags = 0;
+  buf->saved_errno = 0;
+}
+
+void LJ_FASTCALL lj_wbuf_terminate(struct lj_wbuf *buf)
+{
+  lj_wbuf_init(buf, NULL, NULL, NULL, 0);
+}
+
+static LJ_AINLINE void wbuf_reserve(struct lj_wbuf *buf, size_t n)
+{
+  lua_assert(n <= buf->size);
+  if (LJ_UNLIKELY(wbuf_left(buf) < n))
+    lj_wbuf_flush(buf);
+}
+
+/* Writes a byte to the output buffer. */
+void LJ_FASTCALL lj_wbuf_addbyte(struct lj_wbuf *buf, uint8_t b)
+{
+  if (LJ_UNLIKELY(lj_wbuf_test_flag(buf, STREAM_STOP)))
+    return;
+  wbuf_reserve(buf, sizeof(b));
+  *buf->pos++ = b;
+}
+
+/* Writes an unsigned integer which is at most 64 bits long to the output. */
+void LJ_FASTCALL lj_wbuf_addu64(struct lj_wbuf *buf, uint64_t n)
+{
+  if (LJ_UNLIKELY(lj_wbuf_test_flag(buf, STREAM_STOP)))
+    return;
+  wbuf_reserve(buf, LEB128_U64_MAXSIZE);
+  buf->pos += (ptrdiff_t)lj_utils_write_uleb128(buf->pos, n);
+}
+
+/* Writes n bytes from an arbitrary buffer src to the buffer. */
+void lj_wbuf_addn(struct lj_wbuf *buf, const void *src, size_t n)
+{
+  if (LJ_UNLIKELY(lj_wbuf_test_flag(buf, STREAM_STOP)))
+    return;
+  /*
+  ** Very unlikely: We are told to write a large buffer at once.
+  ** Buffer not belong to us so we must to pump data
+  ** through buffer.
+  */
+  while (LJ_UNLIKELY(n > buf->size)) {
+    const size_t left = wbuf_left(buf);
+    memcpy(buf->pos, src, left);
+    buf->pos += (ptrdiff_t)left;
+    lj_wbuf_flush(buf);
+    src += (ptrdiff_t)left;
+    n -= left;
+  }
+
+  wbuf_reserve(buf, n);
+  memcpy(buf->pos, src, n);
+  buf->pos += (ptrdiff_t)n;
+}
+
+/* Writes a \0-terminated C string to the output buffer. */
+void LJ_FASTCALL lj_wbuf_addstring(struct lj_wbuf *buf, const char *s)
+{
+  const size_t l = strlen(s);
+
+  /* Check that profiling is still active is made in the callee's scope. */
+  lj_wbuf_addu64(buf, (uint64_t)l);
+  lj_wbuf_addn(buf, s, l);
+}
+
+void LJ_FASTCALL lj_wbuf_flush(struct lj_wbuf *buf)
+{
+  const size_t len = wbuf_len(buf);
+  size_t written;
+
+  if (LJ_UNLIKELY(lj_wbuf_test_flag(buf, STREAM_STOP)))
+    return;
+
+  written = buf->writer((const void **)&buf->buf, len, buf->ctx);
+
+  if (LJ_UNLIKELY(written < len)) {
+    wbuf_set_flag(buf, STREAM_ERR_IO);
+    wbuf_save_errno(buf);
+  }
+  if (LJ_UNLIKELY(buf->buf == NULL)) {
+    wbuf_set_flag(buf, STREAM_STOP);
+    wbuf_save_errno(buf);
+  }
+  buf->pos = buf->buf;
+}
+
+int LJ_FASTCALL lj_wbuf_test_flag(const struct lj_wbuf *buf, uint8_t flag)
+{
+  return buf->flags & flag;
+}
+
+int LJ_FASTCALL lj_wbuf_errno(const struct lj_wbuf *buf)
+{
+  return buf->saved_errno;
+}
diff --git a/src/lj_wbuf.h b/src/lj_wbuf.h
new file mode 100644
index 0000000..58a109e
--- /dev/null
+++ b/src/lj_wbuf.h
@@ -0,0 +1,87 @@
+/*
+** Low-level event streaming for LuaJIT Profiler.
+**
+** XXX: Please note that all events may not be streamed inside a signal handler
+** due to using default memcpy from glibc as not async-signal-safe function.
+**
+** Major portions taken verbatim or adapted from the LuaVela.
+** Copyright (C) 2015-2019 IPONWEB Ltd.
+*/
+
+#ifndef _LJ_WBUF_H
+#define _LJ_WBUF_H
+
+#include "lj_def.h"
+
+/*
+** Data format for strings:
+**
+** string         := string-len string-payload
+** string-len     := <ULEB128>
+** string-payload := <BYTE> {string-len}
+**
+** Note.
+** For strings shorter than 128 bytes (most likely scenario in our case)
+** we write the same amount of data (1-byte ULEB128 + actual payload) as we
+** would have written with straightforward serialization (actual payload + \0),
+** but make parsing easier.
+*/
+
+/* Stream errors. */
+#define STREAM_ERR_IO 0x1
+#define STREAM_STOP   0x2
+
+typedef size_t (*lj_wbuf_writer)(const void **data, size_t len, void *opt);
+
+/* Write buffer. */
+struct lj_wbuf {
+  /*
+  ** Buffer writer which will called at buffer write.
+  ** Should return amount of written bytes on success or zero in case of error.
+  ** *data should contain new buffer of size greater or equal to len.
+  ** If *data == NULL stream stops.
+  */
+  lj_wbuf_writer writer;
+  /* Context for writer function. */
+  void *ctx;
+  /* Buffer size. */
+  size_t size;
+  /* Saved errno in case of error. */
+  int saved_errno;
+  /* Start of buffer. */
+  uint8_t *buf;
+  /* Current position in buffer. */
+  uint8_t *pos;
+  /* Internal flags. */
+  volatile uint8_t flags;
+};
+
+/* Init buffer. */
+void lj_wbuf_init(struct lj_wbuf *buf, lj_wbuf_writer writer, void *ctx,
+		  uint8_t *mem, size_t size);
+
+/* Set pointers to NULL and reset flags and errno. */
+void LJ_FASTCALL lj_wbuf_terminate(struct lj_wbuf *buf);
+
+/* Write single byte to the buffer. */
+void LJ_FASTCALL lj_wbuf_addbyte(struct lj_wbuf *buf, uint8_t b);
+
+/* Write uint64_t in uleb128 format to the buffer. */
+void LJ_FASTCALL lj_wbuf_addu64(struct lj_wbuf *buf, uint64_t n);
+
+/* Writes n bytes from an arbitrary buffer src to the buffer. */
+void lj_wbuf_addn(struct lj_wbuf *buf, const void *src, size_t n);
+
+/* Write string to the buffer. */
+void LJ_FASTCALL lj_wbuf_addstring(struct lj_wbuf *buf, const char *s);
+
+/* Immediatly flush the buffer. */
+void LJ_FASTCALL lj_wbuf_flush(struct lj_wbuf *buf);
+
+/* Check flags. */
+int LJ_FASTCALL lj_wbuf_test_flag(const struct lj_wbuf *buf, uint8_t flag);
+
+/* Return saved errno. */
+int LJ_FASTCALL lj_wbuf_errno(const struct lj_wbuf *buf);
+
+#endif
diff --git a/src/ljamalg.c b/src/ljamalg.c
index 268b321..705e296 100644
--- a/src/ljamalg.c
+++ b/src/ljamalg.c
@@ -34,6 +34,7 @@
 #include "lj_bc.c"
 #include "lj_obj.c"
 #include "lj_buf.c"
+#include "lj_wbuf.c"
 #include "lj_str.c"
 #include "lj_tab.c"
 #include "lj_func.c"
-- 
2.28.0



More information about the Tarantool-patches mailing list