aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/rbuffer.h
blob: 63d5119004845851e78b4378f2faab1ca023eed4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// Specialized ring buffer. This is basically an array that wraps read/write
// pointers around the memory region. It should be more efficient than the old
// RBuffer which required memmove() calls to relocate read/write positions.
//
// The main purpose of RBuffer is simplify memory management when reading from
// uv_stream_t instances:
//
// - The event loop writes data to a RBuffer, advancing the write pointer
// - The main loop reads data, advancing the read pointer
// - If the buffer becomes full(size == capacity) the rstream is temporarily
//   stopped(automatic backpressure handling)
//
// Reference: http://en.wikipedia.org/wiki/Circular_buffer
#ifndef NVIM_RBUFFER_H
#define NVIM_RBUFFER_H

#include <stddef.h>
#include <stdint.h>

struct rbuffer;

// Macros that simplify working with the read/write pointers directly by hiding
// ring buffer wrap logic. Some examples:
//
// - Pass the write pointer to a function(write_data) that incrementally
//   produces data, returning the number of bytes actually written to the
//   ring buffer:
//
//       RBUFFER_UNTIL_FULL(rbuf, ptr, cnt)
//         rbuffer_produced(rbuf, write_data(state, ptr, cnt));
//
// - Pass the read pointer to a function(read_data) that incrementally
//   consumes data, returning the number of bytes actually read from the
//   ring buffer:
//
//       RBUFFER_UNTIL_EMPTY(rbuf, ptr, cnt)
//         rbuffer_consumed(rbuf, read_data(state, ptr, cnt));
//
// Note that the rbuffer_{produced,consumed} calls are necessary or these macros
// create infinite loops
//
// -V:RBUFFER_UNTIL_EMPTY:1044
#define RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) \
  for (size_t rcnt = 0, _r = 1; _r; _r = 0)  /* NOLINT(readability/braces) */ \
  for (char *rptr = rbuffer_read_ptr(buf, &rcnt);  /* NOLINT(readability/braces) */ \
       buf->size; \
       rptr = rbuffer_read_ptr(buf, &rcnt))

#define RBUFFER_UNTIL_FULL(buf, wptr, wcnt) \
  for (size_t wcnt = 0, _r = 1; _r; _r = 0)  /* NOLINT(readability/braces) */ \
  for (char *wptr = rbuffer_write_ptr(buf, &wcnt);  /* NOLINT(readability/braces) */ \
       rbuffer_space(buf); \
       wptr = rbuffer_write_ptr(buf, &wcnt))

// Iteration
#define RBUFFER_EACH(buf, c, i) \
  for (size_t i = 0;  /* NOLINT(readability/braces) */ \
       i < buf->size; \
       i = buf->size) \
  for (char c = 0;  /* NOLINT(readability/braces) */ \
       i < buf->size ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \
       i++)

#define RBUFFER_EACH_REVERSE(buf, c, i) \
  for (size_t i = buf->size;  /* NOLINT(readability/braces) */ \
       i != SIZE_MAX; \
       i = SIZE_MAX) \
  for (char c = 0;  /* NOLINT(readability/braces) */ \
       i-- > 0 ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \
       )

typedef struct rbuffer RBuffer;
/// Type of function invoked during certain events:
///   - When the RBuffer switches to the full state
///   - When the RBuffer switches to the non-full state
typedef void (*rbuffer_callback)(RBuffer *buf, void *data);

struct rbuffer {
  rbuffer_callback full_cb, nonfull_cb;
  void *data;
  size_t size;
  // helper memory used to by rbuffer_reset if required
  char *temp;
  char *end_ptr, *read_ptr, *write_ptr;
  char start_ptr[];
};

#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "rbuffer.h.generated.h"
#endif

#endif  // NVIM_RBUFFER_H