]>
git.ipfire.org Git - thirdparty/openssl.git/blob - include/internal/ring_buf.h
2 * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
10 #ifndef OSSL_INTERNAL_RING_BUF_H
11 # define OSSL_INTERNAL_RING_BUF_H
14 # include <openssl/e_os2.h> /* For 'ossl_inline' */
17 * ==================================================================
18 * Byte-wise ring buffer which supports pushing and popping blocks of multiple
19 * bytes at a time. The logical offset of each byte for the purposes of a QUIC
20 * stream is tracked. Bytes can be popped from the ring buffer in two stages;
21 * first they are popped, and then they are culled. Bytes which have been popped
22 * but not yet culled will not be overwritten, and can be restored.
26 size_t alloc
; /* size of buffer allocation in bytes */
29 * Logical offset of the head (where we append to). This is the current size
30 * of the QUIC stream. This increases monotonically.
35 * Logical offset of the cull tail. Data is no longer needed and is
36 * deallocated as the cull tail advances, which occurs as data is
37 * acknowledged. This increases monotonically.
39 uint64_t ctail_offset
;
42 static ossl_inline
int ring_buf_init(struct ring_buf
*r
)
46 r
->head_offset
= r
->ctail_offset
= 0;
50 static ossl_inline
void ring_buf_destroy(struct ring_buf
*r
, int cleanse
)
53 OPENSSL_clear_free(r
->start
, r
->alloc
);
55 OPENSSL_free(r
->start
);
60 static ossl_inline
size_t ring_buf_used(struct ring_buf
*r
)
62 return (size_t)(r
->head_offset
- r
->ctail_offset
);
65 static ossl_inline
size_t ring_buf_avail(struct ring_buf
*r
)
67 return r
->alloc
- ring_buf_used(r
);
70 static ossl_inline
int ring_buf_write_at(struct ring_buf
*r
,
71 uint64_t logical_offset
,
72 const unsigned char *buf
,
76 unsigned char *start
= r
->start
;
79 avail
= ring_buf_avail(r
);
80 if (logical_offset
< r
->ctail_offset
81 || logical_offset
+ buf_len
> r
->head_offset
+ avail
)
84 for (i
= 0; buf_len
> 0 && i
< 2; ++i
) {
85 idx
= logical_offset
% r
->alloc
;
90 memcpy(start
+ idx
, buf
, l
);
91 if (r
->head_offset
< logical_offset
+ l
)
92 r
->head_offset
= logical_offset
+ l
;
104 static ossl_inline
size_t ring_buf_push(struct ring_buf
*r
,
105 const unsigned char *buf
,
108 size_t pushed
= 0, avail
, idx
, l
;
109 unsigned char *start
= r
->start
;
112 avail
= ring_buf_avail(r
);
119 idx
= r
->head_offset
% r
->alloc
;
124 memcpy(start
+ idx
, buf
, l
);
134 static ossl_inline
const unsigned char *ring_buf_get_ptr(const struct ring_buf
*r
,
135 uint64_t logical_offset
,
138 unsigned char *start
= r
->start
;
141 if (logical_offset
>= r
->head_offset
|| logical_offset
< r
->ctail_offset
)
143 idx
= logical_offset
% r
->alloc
;
144 *max_len
= r
->alloc
- idx
;
149 * Retrieves data out of the read side of the ring buffer starting at the given
150 * logical offset. *buf is set to point to a contiguous span of bytes and
151 * *buf_len is set to the number of contiguous bytes. After this function
152 * returns, there may or may not be more bytes available at the logical offset
153 * of (logical_offset + *buf_len) by calling this function again. If the logical
154 * offset is out of the range retained by the ring buffer, returns 0, else
155 * returns 1. A logical offset at the end of the range retained by the ring
156 * buffer is not considered an error and is returned with a *buf_len of 0.
158 * The ring buffer state is not changed.
160 static ossl_inline
int ring_buf_get_buf_at(const struct ring_buf
*r
,
161 uint64_t logical_offset
,
162 const unsigned char **buf
,
165 const unsigned char *start
= r
->start
;
168 if (logical_offset
> r
->head_offset
|| logical_offset
< r
->ctail_offset
)
177 idx
= logical_offset
% r
->alloc
;
178 l
= (size_t)(r
->head_offset
- logical_offset
);
179 if (l
> r
->alloc
- idx
)
187 static ossl_inline
void ring_buf_cpop_range(struct ring_buf
*r
,
188 uint64_t start
, uint64_t end
,
191 assert(end
>= start
);
193 if (start
> r
->ctail_offset
)
196 if (cleanse
&& r
->alloc
> 0 && end
> r
->ctail_offset
) {
197 size_t idx
= r
->ctail_offset
% r
->alloc
;
198 uint64_t cleanse_end
= end
+ 1;
201 if (cleanse_end
> r
->head_offset
)
202 cleanse_end
= r
->head_offset
;
203 l
= (size_t)(cleanse_end
- r
->ctail_offset
);
204 if (l
> r
->alloc
- idx
) {
205 OPENSSL_cleanse((unsigned char *)r
->start
+ idx
, r
->alloc
- idx
);
210 OPENSSL_cleanse((unsigned char *)r
->start
+ idx
, l
);
213 r
->ctail_offset
= end
+ 1;
214 /* Allow culling unpushed data */
215 if (r
->head_offset
< r
->ctail_offset
)
216 r
->head_offset
= r
->ctail_offset
;
219 static ossl_inline
int ring_buf_resize(struct ring_buf
*r
, size_t num_bytes
,
222 struct ring_buf rnew
= {0};
223 const unsigned char *src
= NULL
;
224 size_t src_len
= 0, copied
= 0;
226 if (num_bytes
== r
->alloc
)
229 if (num_bytes
< ring_buf_used(r
))
232 rnew
.start
= OPENSSL_malloc(num_bytes
);
233 if (rnew
.start
== NULL
)
236 rnew
.alloc
= num_bytes
;
237 rnew
.head_offset
= r
->head_offset
- ring_buf_used(r
);
238 rnew
.ctail_offset
= rnew
.head_offset
;
241 if (!ring_buf_get_buf_at(r
, r
->ctail_offset
+ copied
, &src
, &src_len
)) {
242 OPENSSL_free(rnew
.start
);
249 if (ring_buf_push(&rnew
, src
, src_len
) != src_len
) {
250 OPENSSL_free(rnew
.start
);
257 assert(rnew
.head_offset
== r
->head_offset
);
258 rnew
.ctail_offset
= r
->ctail_offset
;
260 ring_buf_destroy(r
, cleanse
);
261 memcpy(r
, &rnew
, sizeof(*r
));
265 #endif /* OSSL_INTERNAL_RING_BUF_H */