From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp16.mail.ru (smtp16.mail.ru [94.100.176.153]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 8B5594765E0 for ; Thu, 24 Dec 2020 18:45:36 +0300 (MSK) From: Sergey Ostanevich Message-Id: <4216B30D-84B1-4A46-8DD7-C1A51587D7FF@tarantool.org> Content-Type: multipart/alternative; boundary="Apple-Mail=_22ACAD9C-272E-4D6D-8E5A-E54BCD3C47AB" Mime-Version: 1.0 (Mac OS X Mail 13.4 \(3608.120.23.2.4\)) Date: Thu, 24 Dec 2020 18:45:34 +0300 In-Reply-To: <20201224064656.GH9101@root> References: <0adaa12ef5be0efc1d9c79ab746b8149c252d661.1608142899.git.skaplun@tarantool.org> <20201221092415.GT5396@tarantool.org> <20201224064656.GH9101@root> Subject: Re: [Tarantool-patches] [PATCH luajit v1 03/11] profile: introduce profiler writing module List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Sergey Kaplun Cc: tarantool-patches@dev.tarantool.org --Apple-Mail=_22ACAD9C-272E-4D6D-8E5A-E54BCD3C47AB Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 Hi! Thanks for the changes. Although I see no new patch - I look into the=20 origin/skaplun/gh-5442-luajit-memory-profiler under third_party/luajit +void lj_wbuf_init(struct lj_wbuf *buf, lj_wbuf_writer writer, void = *ctx, + uint8_t *mem, size_t size) +{ + lua_assert(size >=3D LEB128_U64_MAXSIZE); Is it meaningful to allocate just 10bytes? +/* 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 =3D wbuf_left(buf); + memcpy(buf->pos, src, left); I=E2=80=99m afraid the src pointer is not progress through the while() = loop + buf->pos +=3D (ptrdiff_t)left; + lj_wbuf_flush(buf); + n -=3D left; + } Sergos. > On 24 Dec 2020, at 09:46, Sergey Kaplun wrote: >=20 > Hi, Igor! >=20 > Thanks for the review! >=20 > On 21.12.20, Igor Munkin wrote: >> Sergey, >>=20 >> Thanks for the patch! Please consider the comments below. I expected >> that we agreed we need an MVP for now, so the module API is fine = except >> the part: it is overcomplicated for the current = uses. >=20 > We can try this API for now. This may lead to thoughts about improving = C > API. >=20 >>=20 >> On 16.12.20, Sergey Kaplun wrote: >>> This patch introduces module for writing profile data. >>> Its usage will be added at the next patches. >>>=20 >>> It can be used for memory profiler or for signal-based >>> cpu profiler. >>=20 >> I see nothing strongly related to the profiler, so it's simply an >> internal write buffer. Then let's settle for the naming at first. I >> guess is a good prefix for this subsystem. >=20 > OK, it looks better. >=20 >>=20 >> So, I propose the following wording for the commit message: >> | core: introduce write buffer module >> | >> | This patch introduces the standalone module for writing data to the >> | file via the special buffer. The module provides the API for buffer >> | initial setup and its convenient usage. >>=20 >> Feel free to adjust the description on you own and even describe the >> introduced API in a more verbose way. >=20 > Thanks, sounds well! >=20 >>=20 >>>=20 >>> Part of tarantool/tarantool#5442 >>> --- >>>=20 >>> Custom memcpy function (see below) makes sense if this module will = be >>> used for cpu/sample profiler based on a signal-based timer. Else it = can >>> be easily redefined. >>=20 >> I suggest to not overcomplicate all this machinery added for an a = single >> profiler. Let's drop all memcpy-related hacks. By the way we can = simply >> incorporate it when it is necessary. >=20 > Sure. >=20 >>=20 >>>=20 >>> src/Makefile | 5 +- >>> src/Makefile.dep | 2 + >>> src/profile/ljp_write.c | 195 = ++++++++++++++++++++++++++++++++++++++++ >>> src/profile/ljp_write.h | 84 +++++++++++++++++ >>> 4 files changed, 284 insertions(+), 2 deletions(-) >>> create mode 100644 src/profile/ljp_write.c >>> create mode 100644 src/profile/ljp_write.h >>>=20 >>> diff --git a/src/Makefile b/src/Makefile >>> index be7ed95..4b1d937 100644 >>> --- a/src/Makefile >>> +++ b/src/Makefile >>=20 >> Please, adjust these changes considering the comments to the first >> patch. You can find the proposed naming above. >=20 > Done. >=20 >>=20 >>> @@ -469,6 +469,7 @@ DASM_FLAGS=3D $(DASM_XFLAGS) $(DASM_AFLAGS) >>=20 >> >>=20 >>> diff --git a/src/profile/ljp_write.c b/src/profile/ljp_write.c >>> new file mode 100644 >>> index 0000000..de7202d >>> --- /dev/null >>> +++ b/src/profile/ljp_write.c >>> @@ -0,0 +1,195 @@ >>> +/* >>> +** Low-level writer for LuaJIT Profiler. >>> +** >>> +** Major portions taken verbatim or adapted from the LuaVela. >>> +** Copyright (C) 2015-2019 IPONWEB Ltd. >>> +*/ >>> + >>> +#include >>> +#include >>> + >>> +#include "profile/ljp_write.h" >>> +#include "utils/leb128.h" >>> +#include "lj_def.h" >>=20 >> >>=20 >>> +/* Wraps a write syscall ensuring all data have been written. */ >>=20 >> I see no syscall wrapped here. Anyway, IIRC we discussed that we = don't >> need such complex interfaces in scope of MVP. So you can just use >> or right here for now and redesign the wbuf API = later >> if necessary (it's internal, so I see no problem). >=20 > It *may* be wrapped here (encapsulated in writer function). As I say > above, it can lead us to some thoughts about C API. It is internal > interface and we can change it whenever we want. >=20 > Thoughts? >=20 >>=20 >>> +static void write_buffer_sys(struct ljp_buffer *buffer, const void = **data, >>> + size_t len) >>=20 >> Why do you need a separate function for this? I guess this should be >> moved right to the (hell these names). >=20 > I suppose it not necessary. I'll merge it with flush. >=20 >>=20 >>> +{ >>> + void *ctx =3D buffer->ctx; >>> + size_t written; >>> + >>> + lua_assert(!ljp_write_test_flag(buffer, STREAM_STOP)); >>> + >>> + written =3D buffer->writer(data, len, ctx); >>=20 >> Well, I believe you can use buffer->ctx instead of the additional >> variable here. Trust me, you can! >=20 > Yep :) >=20 >>=20 >>> + >>> + if (LJ_UNLIKELY(written < len)) { >>> + write_set_flag(buffer, STREAM_ERR_IO); >>> + write_save_errno(buffer); >>> + } >>> + if (LJ_UNLIKELY(*data =3D=3D NULL)) { >>> + write_set_flag(buffer, STREAM_STOP); >>> + write_save_errno(buffer); >>> + } >>> +} >>> + >>> +static LJ_AINLINE size_t write_bytes_buffered(const struct = ljp_buffer *buf) >>=20 >> I propose s/write_bytes_buffered/lj_wbuf_len/ (consider sbuflen = macro). >=20 > Fixed. >=20 >>=20 >> >>=20 >>> +static LJ_AINLINE int write_buffer_has(const struct ljp_buffer = *buf, size_t n) >>=20 >> I propose s/write_buffer_has/lj_wbuf_left/ (consider sbufleft macro). >=20 > Fixed. >=20 >>=20 >=20 > >=20 >>> +/* Writes n bytes from an arbitrary buffer src to the output. */ >>> +static void write_buffer(struct ljp_buffer *buf, const void *src, = size_t n) >>> +{ >>> + if (LJ_UNLIKELY(ljp_write_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)) { >>> + ljp_write_flush_buffer(buf); >>=20 >> Why do you need to flush the buffer on start? I guess you can fill = the >> buffer till it becomes full and only then flush. >=20 > Thanks! Good point! >=20 >>=20 >>> + write_memcpy(buf->pos, src, buf->size); >>> + buf->pos +=3D (ptrdiff_t)buf->size; >>> + n -=3D buf->size; >>> + } >>> + >>> + write_reserve(buf, n); >>> + write_memcpy(buf->pos, src, n); >>> + buf->pos +=3D (ptrdiff_t)n; >>> +} >>> + >>> +/* Writes a \0-terminated C string to the output buffer. */ >>> +void ljp_write_string(struct ljp_buffer *buf, const char *s) >>> +{ >>> + const size_t l =3D strlen(s); >>> + >>> + ljp_write_u64(buf, (uint64_t)l); >>=20 >> This is unclear that the check that profiling is still active is made = in >> scope of the callee. >=20 > I'll add the comment here. >=20 >>=20 >>> + write_buffer(buf, s, l); >>> +} >>> + >>=20 >> >>=20 >>> diff --git a/src/profile/ljp_write.h b/src/profile/ljp_write.h >>> new file mode 100644 >>> index 0000000..29c1669 >>> --- /dev/null >>> +++ b/src/profile/ljp_write.h >>> @@ -0,0 +1,84 @@ >>> +/* >>> +** Low-level event streaming for LuaJIT Profiler. >>> +** NB! Please note that all events may be streamed inside a signal = handler. >>> +** This means effectively that only async-signal-safe library = functions and >>> +** syscalls MUST be used for streaming. Check with `man 7 signal` = when in >>> +** doubt. >>> +** Major portions taken verbatim or adapted from the LuaVela. >>> +** Copyright (C) 2015-2019 IPONWEB Ltd. >>> +*/ >>> + >>> +#ifndef _LJP_WRITE_H >>> +#define _LJP_WRITE_H >>> + >>> +#include >>> + >>> +/* >>> +** Data format for strings: >>> +** >>> +** string :=3D string-len string-payload >>> +** string-len :=3D >>> +** string-payload :=3D {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 (*ljp_writer)(const void **data, size_t len, void = *opt); >>> + >>> +/* Write buffer for profilers. */ >>> +struct ljp_buffer { >>> + /* >>> + ** 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 =3D=3D NULL stream stops. >>> + */ >>> + ljp_writer writer; >>> + /* Context to writer function. */ >>=20 >> Typo: s/Context to/Context for/. >=20 > Thanks. >=20 >>=20 >>> + 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; >>> +}; >>=20 >> Well, I don't get why the functions are called , but the >> first parameter (i.e. "self") is ljp_buffer. As a result such names = as >> looks confusing, since errno is actually written >> nowhere. I suggest to name everything with prefix, so the >> names and fits the resulting = value. >> Furthermore, the routines appending the data to the buffer can be >> renamed the following way: ljp_write_ -> lj_wbuf_add >> (consider Lua standart buffer API). Thoughts? >=20 > Yes, it's more consistent with LuaJIT internals too. >=20 >>=20 >>> + >>> +/* Write string. */ >>> +void ljp_write_string(struct ljp_buffer *buf, const char *s); >>> + >>> +/* Write single byte. */ >>> +void ljp_write_byte(struct ljp_buffer *buf, uint8_t b); >>> + >>> +/* Write uint64_t in uleb128 format. */ >>> +void ljp_write_u64(struct ljp_buffer *buf, uint64_t n); >>> + >>> +/* Immediatly flush buffer. */ >>> +void ljp_write_flush_buffer(struct ljp_buffer *buf); >>> + >>> +/* Init buffer. */ >>> +void ljp_write_init(struct ljp_buffer *buf, ljp_writer writer, void = *ctx, >>> + uint8_t *mem, size_t size); >>> + >>> +/* Check flags. */ >>> +int ljp_write_test_flag(const struct ljp_buffer *buf, uint8_t = flag); >>> + >>> +/* Return saved errno. */ >>> +int ljp_write_errno(const struct ljp_buffer *buf); >>> + >>> +/* Set pointers to NULL and reset flags. */ >>> +void ljp_write_terminate(struct ljp_buffer *buf); >>> + >>> +#endif >>> --=20 >>> 2.28.0 >>>=20 >>=20 >> --=20 >> Best regards, >> IM >=20 > --=20 > Best regards, > Sergey Kaplun --Apple-Mail=_22ACAD9C-272E-4D6D-8E5A-E54BCD3C47AB Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8 Hi!

Thanks = for the changes. Although I see no new patch - I look into = the 
origin/skaplun/gh-5442-luajit-memory-profiler under = third_party/luajit

+void = lj_wbuf_init(struct lj_wbuf *buf, lj_wbuf_writer writer, void = *ctx,
+       =           uint8_t *mem, size_t = size)
+{
+  = lua_assert(size >=3D LEB128_U64_MAXSIZE);

Is it meaningful to = allocate just 10bytes?

+/* = 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 =3D = wbuf_left(buf);
+  =   memcpy(buf->pos, src, left);

I=E2=80=99m afraid the src pointer is not progress through = the while() loop


+  =   buf->pos +=3D (ptrdiff_t)left;
+    = lj_wbuf_flush(buf);
+  =   n -=3D left;
+  = }


Sergos.


On 24 = Dec 2020, at 09:46, Sergey Kaplun <skaplun@tarantool.org> wrote:

Hi, = Igor!

Thanks for = the review!

On 21.12.20, = Igor Munkin wrote:
Sergey,

Thanks for the patch! Please consider the comments below. I = expected
that we agreed we need an MVP for now, so the = module API is fine except
the <write_buffer_sys> = part: it is overcomplicated for the current uses.

We can try this API for now. This may lead to thoughts about = improving C
API.


On 16.12.20, Sergey Kaplun wrote:
This patch introduces module for writing = profile data.
Its usage will be added at the next = patches.

It can be used for memory profiler = or for signal-based
cpu profiler.

I see nothing strongly related to = the profiler, so it's simply an
internal write buffer. = Then let's settle for the naming at first. I
guess = <lj_wbuf*> is a good prefix for this subsystem.

OK, it looks better.


So, I propose the = following wording for the commit message:
| core: = introduce write buffer module
|
| This patch = introduces the standalone module for writing data to the
| = file via the special buffer. The module provides the API for buffer
| initial setup and its convenient usage.

Feel free to adjust the description on you own and even = describe the
introduced API in a more verbose way.

Thanks, sounds well!



Part of = tarantool/tarantool#5442
---

Custom memcpy function (see below) makes sense if this module = will be
used for cpu/sample profiler based on a = signal-based timer. Else it can
be easily redefined.

I suggest to not overcomplicate = all this machinery added for an a single
profiler. Let's = drop all memcpy-related hacks. By the way we can simply
incorporate it when it is necessary.

Sure.



src/Makefile =            | =   5 +-
src/Makefile.dep =        |   2 +
src/profile/ljp_write.c | 195 = ++++++++++++++++++++++++++++++++++++++++
src/profile/ljp_write.h |  84 +++++++++++++++++
4 files changed, 284 insertions(+), 2 deletions(-)
create mode 100644 src/profile/ljp_write.c
create= mode 100644 src/profile/ljp_write.h

diff = --git a/src/Makefile b/src/Makefile
index be7ed95..4b1d937 = 100644
--- a/src/Makefile
+++ = b/src/Makefile

Please, adjust = these changes considering the comments to the first
patch. = You can find the proposed naming above.

Done.

@@ -469,6 +469,7 @@ = DASM_FLAGS=3D $(DASM_XFLAGS) $(DASM_AFLAGS)
<snipped>

diff --git a/src/profile/ljp_write.c = b/src/profile/ljp_write.c
new file mode 100644
index 0000000..de7202d
--- /dev/null
+++ b/src/profile/ljp_write.c
@@ -0,0 +1,195 = @@
+/*
+** Low-level writer for LuaJIT = Profiler.
+**
+** Major portions taken = verbatim or adapted from the LuaVela.
+** Copyright (C) = 2015-2019 IPONWEB Ltd.
+*/
+
+#include <unistd.h>
+#include = <errno.h>
+
+#include = "profile/ljp_write.h"
+#include "utils/leb128.h"
+#include "lj_def.h"

<snipped>

+/* Wraps a write syscall ensuring all data = have been written. */

I see no = syscall wrapped here. Anyway, IIRC we discussed that we don't
need such complex interfaces in scope of MVP. So you can just = use
<write> or <fwrite> right here for now and = redesign the wbuf API later
if necessary (it's internal, = so I see no problem).

It *may* be wrapped here (encapsulated in writer function). = As I say
above, it can = lead us to some thoughts about C API. It is internal
interface and = we can change it whenever we want.

Thoughts?


+static void write_buffer_sys(struct ljp_buffer = *buffer, const void **data,
+      size_= t len)

Why do you need a = separate function for this? I guess this should be
moved = right to the <ljp_write_flush_buffer> (hell these names).

I suppose it not necessary. I'll merge it with = flush.


+{
+ =  void *ctx =3D buffer->ctx;
+  size_t = written;
+
+ =  lua_assert(!ljp_write_test_flag(buffer, STREAM_STOP));
+
+  written =3D buffer->writer(data, = len, ctx);

Well, I believe you = can use buffer->ctx instead of the additional
variable = here. Trust me, you can!

Yep = :)


+
+ =  if (LJ_UNLIKELY(written < len)) {
+ =    write_set_flag(buffer, STREAM_ERR_IO);
+ =    write_save_errno(buffer);
+  }
+  if (LJ_UNLIKELY(*data =3D=3D NULL)) {
+ =    write_set_flag(buffer, STREAM_STOP);
+ =    write_save_errno(buffer);
+  }
+}
+
+static LJ_AINLINE size_t = write_bytes_buffered(const struct ljp_buffer *buf)

I propose = s/write_bytes_buffered/lj_wbuf_len/ (consider sbuflen macro).

Fixed.


<snipped>

+static = LJ_AINLINE int write_buffer_has(const struct ljp_buffer *buf, size_t = n)

I propose = s/write_buffer_has/lj_wbuf_left/ (consider sbufleft macro).

Fixed.



<snipped>

+/* Writes n bytes from an arbitrary buffer src to the = output. */
+static void write_buffer(struct ljp_buffer = *buf, const void *src, size_t n)
+{
+ =  if (LJ_UNLIKELY(ljp_write_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)) = {
+    ljp_write_flush_buffer(buf);

Why do you need to flush the = buffer on start? I guess you can fill the
buffer till it = becomes full and only then flush.

Thanks! Good = point!


+ =    write_memcpy(buf->pos, src, buf->size);
+    buf->pos +=3D = (ptrdiff_t)buf->size;
+    n -=3D = buf->size;
+  }
+
+ =  write_reserve(buf, n);
+ =  write_memcpy(buf->pos, src, n);
+ =  buf->pos +=3D (ptrdiff_t)n;
+}
+
+/* Writes a \0-terminated C string to the output buffer. = */
+void ljp_write_string(struct ljp_buffer *buf, const = char *s)
+{
+  const size_t l =3D = strlen(s);
+
+  ljp_write_u64(buf, = (uint64_t)l);

This is unclear = that the check that profiling is still active is made in
scope of the callee.

I'll add the = comment here.


+ =  write_buffer(buf, s, l);
+}
+

<snipped>

diff --git = a/src/profile/ljp_write.h b/src/profile/ljp_write.h
new = file mode 100644
index 0000000..29c1669
--- = /dev/null
+++ b/src/profile/ljp_write.h
@@ = -0,0 +1,84 @@
+/*
+** Low-level event = streaming for LuaJIT Profiler.
+** NB! Please note that = all events may be streamed inside a signal handler.
+** = This means effectively that only async-signal-safe library functions = and
+** syscalls MUST be used for streaming. Check with = `man 7 signal` when in
+** doubt.
+** Major = portions taken verbatim or adapted from the LuaVela.
+** = Copyright (C) 2015-2019 IPONWEB Ltd.
+*/
+
+#ifndef _LJP_WRITE_H
+#define _LJP_WRITE_H
+
+#include <stdint.h>
+
+/*
+** Data format for strings:
+**
+** string =         :=3D string-len = string-payload
+** string-len     :=3D = <ULEB128>
+** string-payload :=3D <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 (*ljp_writer)(const void **data, size_t len, void *opt);
+
+/* Write buffer for profilers. */
+struct ljp_buffer {
+  /*
+ =  ** 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 =3D=3D = NULL stream stops.
+  */
+ =  ljp_writer writer;
+  /* Context to writer = function. */

Typo: s/Context = to/Context for/.

Thanks.


+  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;
+};

Well, I don't = get why the functions are called <ljp_write_*>, but the
first parameter (i.e. "self") is ljp_buffer. As a result such = names as
<ljp_write_errno> looks confusing, since = errno is actually written
nowhere. I suggest to name = everything with <lj_wbuf_*> prefix, so the
names = <lj_wbuf_errno> and <lj_wbuf_test_flag> fits the resulting = value.
Furthermore, the routines appending the data to the = buffer can be
renamed the following way: = ljp_write_<type> -> lj_wbuf_add<type>
(consider Lua standart buffer API). Thoughts?

Yes, it's more consistent with LuaJIT internals = too.


+
+/* = Write string. */
+void ljp_write_string(struct ljp_buffer = *buf, const char *s);
+
+/* Write single = byte. */
+void ljp_write_byte(struct ljp_buffer *buf, = uint8_t b);
+
+/* Write uint64_t in uleb128 = format. */
+void ljp_write_u64(struct ljp_buffer *buf, = uint64_t n);
+
+/* Immediatly flush buffer. = */
+void ljp_write_flush_buffer(struct ljp_buffer = *buf);
+
+/* Init buffer. */
+void ljp_write_init(struct ljp_buffer *buf, ljp_writer = writer, void *ctx,
+     uint8_t = *mem, size_t size);
+
+/* Check flags. */
+int ljp_write_test_flag(const struct ljp_buffer *buf, = uint8_t flag);
+
+/* Return saved errno. = */
+int ljp_write_errno(const struct ljp_buffer *buf);
+
+/* Set pointers to NULL and reset flags. = */
+void ljp_write_terminate(struct ljp_buffer *buf);
+
+#endif
-- 
2.28.0


-- 
Best = regards,
IM

-- 
Best = regards,
Sergey = Kaplun

= --Apple-Mail=_22ACAD9C-272E-4D6D-8E5A-E54BCD3C47AB--