aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/lib/ringbuf.h
blob: c8abccfeb4e1c0c13171f43277572682b09e92d5 (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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/// Macros-based ring buffer implementation.
///
/// Supported functions:
///
/// - new: allocates new ring buffer.
/// - dealloc: free ring buffer itself.
/// - free: free ring buffer and all its elements.
/// - push: adds element to the end of the buffer.
/// - length: get buffer length.
/// - size: size of the ring buffer.
/// - idx: get element at given index.
/// - idx_p: get pointer to the element at given index.
/// - insert: insert element at given position.
/// - remove: remove element from given position.

#pragma once

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

#include "nvim/func_attr.h"
#include "nvim/memory.h"

#define _RINGBUF_LENGTH(rb) \
  ((rb)->first == NULL ? 0 \
   : ((rb)->next == (rb)->first) ? (size_t)((rb)->buf_end - (rb)->buf) + 1 \
   : ((rb)->next > (rb)->first) ? (size_t)((rb)->next - (rb)->first) \
   : (size_t)((rb)->next - (rb)->buf + (rb)->buf_end - (rb)->first + 1))

#define _RINGBUF_NEXT(rb, var) \
  ((var) == (rb)->buf_end ? (rb)->buf : (var) + 1)
#define _RINGBUF_PREV(rb, var) \
  ((var) == (rb)->buf ? (rb)->buf_end : (var) - 1)

/// Iterate over all ringbuf values
///
/// @param  rb       Ring buffer to iterate over.
/// @param  RBType   Type of the ring buffer element.
/// @param  varname  Variable name.
#define RINGBUF_FORALL(rb, RBType, varname) \
  size_t varname##_length_fa_ = _RINGBUF_LENGTH(rb); \
  for (RBType *varname = ((rb)->first == NULL ? (rb)->next : (rb)->first); \
       varname##_length_fa_; \
       (varname = _RINGBUF_NEXT(rb, varname)), \
       varname##_length_fa_--)

/// Iterate over all ringbuf values, from end to the beginning
///
/// Unlike previous RINGBUF_FORALL uses already defined variable, in place of
/// defining variable in the cycle body.
///
/// @param  rb       Ring buffer to iterate over.
/// @param  RBType   Type of the ring buffer element.
/// @param  varname  Variable name.
#define RINGBUF_ITER_BACK(rb, RBType, varname) \
  size_t varname##_length_ib_ = _RINGBUF_LENGTH(rb); \
  for (varname = ((rb)->next == (rb)->buf ? (rb)->buf_end : (rb)->next - 1); \
       varname##_length_ib_; \
       (varname = _RINGBUF_PREV(rb, varname)), \
       varname##_length_ib_--)

/// Define a ring buffer structure
///
/// @param TypeName    Ring buffer type name. Actual type name will be
///                    `{TypeName}RingBuffer`.
/// @param RBType      Type of the single ring buffer element.
#define RINGBUF_TYPEDEF(TypeName, RBType) \
  typedef struct { \
    RBType *buf; \
    RBType *next; \
    RBType *first; \
    RBType *buf_end; \
  } TypeName##RingBuffer;

/// Dummy item free macros, for use in RINGBUF_INIT
///
/// This macros actually does nothing.
///
/// @param[in]  item  Item to be freed.
#define RINGBUF_DUMMY_FREE(item)

/// Static ring buffer
///
/// @warning Ring buffers created with this macros must neither be freed nor
///          deallocated.
///
/// @param  scope  Ring buffer scope.
/// @param  TypeName  Ring buffer type name.
/// @param  RBType  Type of the single ring buffer element.
/// @param  varname  Variable name.
/// @param  rbsize  Ring buffer size.
#define RINGBUF_STATIC(scope, TypeName, RBType, varname, rbsize) \
  static RBType _##varname##_buf[rbsize]; \
  scope TypeName##RingBuffer varname = { \
    .buf = _##varname##_buf, \
    .next = _##varname##_buf, \
    .first = NULL, \
    .buf_end = _##varname##_buf + rbsize - 1, \
  };

/// Initialize a new ring buffer
///
/// @param TypeName    Ring buffer type name. Actual type name will be
///                    `{TypeName}RingBuffer`.
/// @param funcprefix  Prefix for all ring buffer functions. Function name will
///                    look like `{funcprefix}_rb_{function_name}`.
/// @param RBType      Type of the single ring buffer element.
/// @param rbfree      Function used to free ring buffer element. May be
///                    a macros like `#define RBFREE(item)` (to skip freeing).
///
///                    Intended function signature: `void *rbfree(RBType *)`;
#define RINGBUF_INIT(TypeName, funcprefix, RBType, rbfree) \
  static inline TypeName##RingBuffer funcprefix##_rb_new(const size_t size) \
  REAL_FATTR_WARN_UNUSED_RESULT; \
  static inline TypeName##RingBuffer funcprefix##_rb_new(const size_t size) \
  { \
    assert(size != 0); \
    RBType *buf = xmalloc(size * sizeof(RBType)); \
    return (TypeName##RingBuffer) { \
      .buf = buf, \
      .next = buf, \
      .first = NULL, \
      .buf_end = buf + size - 1, \
    }; \
  } \
  static inline void funcprefix##_rb_free(TypeName##RingBuffer *const rb) \
  REAL_FATTR_UNUSED; \
  static inline void funcprefix##_rb_free(TypeName##RingBuffer *const rb) \
  { \
    if (rb == NULL) { \
      return; \
    } \
    RINGBUF_FORALL(rb, RBType, rbitem) { \
      rbfree(rbitem); \
    } \
    XFREE_CLEAR(rb->buf); \
  } \
  static inline void funcprefix##_rb_dealloc(TypeName##RingBuffer *const rb) \
  REAL_FATTR_UNUSED; \
  static inline void funcprefix##_rb_dealloc(TypeName##RingBuffer *const rb) \
  { \
    XFREE_CLEAR(rb->buf); \
  } \
  static inline void funcprefix##_rb_push(TypeName##RingBuffer *const rb, \
                                          RBType item) \
  REAL_FATTR_NONNULL_ARG(1); \
  static inline void funcprefix##_rb_push(TypeName##RingBuffer *const rb, \
                                          RBType item) \
  { \
    if (rb->next == rb->first) { \
      rbfree(rb->first); \
      rb->first = _RINGBUF_NEXT(rb, rb->first); \
    } else if (rb->first == NULL) { \
      rb->first = rb->next; \
    } \
    *rb->next = item; \
    rb->next = _RINGBUF_NEXT(rb, rb->next); \
  } \
  static inline ptrdiff_t funcprefix##_rb_find_idx(const TypeName##RingBuffer *const rb, \
                                                   const RBType *const item_p) \
  REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_UNUSED; \
  static inline ptrdiff_t funcprefix##_rb_find_idx(const TypeName##RingBuffer *const rb, \
                                                   const RBType *const item_p) \
  { \
    assert(rb->buf <= item_p); \
    assert(rb->buf_end >= item_p); \
    if (rb->first == NULL) { \
      return -1; \
    } else if (item_p >= rb->first) { \
      return item_p - rb->first; \
    } else { \
      return item_p - rb->buf + rb->buf_end - rb->first + 1; \
    } \
  } \
  static inline size_t funcprefix##_rb_size(const TypeName##RingBuffer *const rb) \
  REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \
  static inline size_t funcprefix##_rb_size(const TypeName##RingBuffer *const rb) \
  { \
    return (size_t)(rb->buf_end - rb->buf) + 1; \
  } \
  static inline size_t funcprefix##_rb_length(const TypeName##RingBuffer *const rb) \
  REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \
  static inline size_t funcprefix##_rb_length(const TypeName##RingBuffer *const rb) \
  { \
    return _RINGBUF_LENGTH(rb); \
  } \
  static inline RBType *funcprefix##_rb_idx_p(const TypeName##RingBuffer *const rb, \
                                              const size_t idx) \
  REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE; \
  static inline RBType *funcprefix##_rb_idx_p(const TypeName##RingBuffer *const rb, \
                                              const size_t idx) \
  { \
    assert(idx <= funcprefix##_rb_size(rb)); \
    assert(idx <= funcprefix##_rb_length(rb)); \
    if (rb->first + idx > rb->buf_end) { \
      return rb->buf + ((rb->first + idx) - (rb->buf_end + 1)); \
    } else { \
      return rb->first + idx; \
    } \
  } \
  static inline RBType funcprefix##_rb_idx(const TypeName##RingBuffer *const rb, \
                                           const size_t idx) \
  REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_UNUSED; \
  static inline RBType funcprefix##_rb_idx(const TypeName##RingBuffer *const rb, \
                                           const size_t idx) \
  { \
    return *funcprefix##_rb_idx_p(rb, idx); \
  } \
  static inline void funcprefix##_rb_insert(TypeName##RingBuffer *const rb, \
                                            const size_t idx, \
                                            RBType item) \
  REAL_FATTR_NONNULL_ARG(1) REAL_FATTR_UNUSED; \
  static inline void funcprefix##_rb_insert(TypeName##RingBuffer *const rb, \
                                            const size_t idx, \
                                            RBType item) \
  { \
    assert(idx <= funcprefix##_rb_size(rb)); \
    assert(idx <= funcprefix##_rb_length(rb)); \
    const size_t length = funcprefix##_rb_length(rb); \
    if (idx == length) { \
      funcprefix##_rb_push(rb, item); \
      return; \
    } \
    RBType *const insertpos = funcprefix##_rb_idx_p(rb, idx); \
    if (insertpos == rb->next) { \
      funcprefix##_rb_push(rb, item); \
      return; \
    } \
    if (length == funcprefix##_rb_size(rb)) { \
      rbfree(rb->first); \
    } \
    if (insertpos < rb->next) { \
      memmove(insertpos + 1, insertpos, \
              (size_t)((uintptr_t)rb->next - (uintptr_t)insertpos)); \
    } else { \
      assert(insertpos > rb->first); \
      assert(rb->next <= rb->first); \
      memmove(rb->buf + 1, rb->buf, \
              (size_t)((uintptr_t)rb->next - (uintptr_t)rb->buf)); \
      *rb->buf = *rb->buf_end; \
      memmove(insertpos + 1, insertpos, \
              (size_t)((uintptr_t)(rb->buf_end + 1) - (uintptr_t)insertpos)); \
    } \
    *insertpos = item; \
    if (length == funcprefix##_rb_size(rb)) { \
      rb->first = _RINGBUF_NEXT(rb, rb->first); \
    } \
    rb->next = _RINGBUF_NEXT(rb, rb->next); \
  } \
  static inline void funcprefix##_rb_remove(TypeName##RingBuffer *const rb, \
                                            const size_t idx) \
  REAL_FATTR_NONNULL_ARG(1) REAL_FATTR_UNUSED; \
  static inline void funcprefix##_rb_remove(TypeName##RingBuffer *const rb, \
                                            const size_t idx) \
  { \
    assert(idx < funcprefix##_rb_size(rb)); \
    assert(idx < funcprefix##_rb_length(rb)); \
    RBType *const rmpos = funcprefix##_rb_idx_p(rb, idx); \
    rbfree(rmpos); \
    if (rmpos == rb->next - 1) { \
      rb->next--; \
      if (rb->first == rb->next) { \
        rb->first = NULL; \
        rb->next = rb->buf; \
      } \
    } else if (rmpos == rb->first) { \
      rb->first = _RINGBUF_NEXT(rb, rb->first); \
      if (rb->first == rb->next) { \
        rb->first = NULL; \
        rb->next = rb->buf; \
      } \
    } else if (rb->first < rb->next || rb->next == rb->buf) { \
      assert(rmpos > rb->first); \
      assert(rmpos <= _RINGBUF_PREV(rb, rb->next)); \
      memmove(rb->first + 1, rb->first, \
              (size_t)((uintptr_t)rmpos - (uintptr_t)rb->first)); \
      rb->first = _RINGBUF_NEXT(rb, rb->first); \
    } else if (rmpos < rb->next) { \
      memmove(rmpos, rmpos + 1, \
              (size_t)((uintptr_t)rb->next - (uintptr_t)rmpos)); \
      rb->next = _RINGBUF_PREV(rb, rb->next); \
    } else { \
      assert(rb->first < rb->buf_end); \
      memmove(rb->first + 1, rb->first, \
              (size_t)((uintptr_t)rmpos - (uintptr_t)rb->first)); \
      rb->first = _RINGBUF_NEXT(rb, rb->first); \
    } \
  }