VirtualBox

source: vbox/trunk/include/iprt/sg.h@ 101656

Last change on this file since 101656 was 99961, checked in by vboxsync, 18 months ago

IPRT/sg: Corrected RTSgBufIsAtEnd and made the code deal with (skip) empty segments. Added a testcase (incomplete). [scm]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.4 KB
Line 
1/** @file
2 * IPRT - S/G buffer handling.
3 */
4
5/*
6 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
7 *
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.virtualbox.org.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
14 * License.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
23 *
24 * The contents of this file may alternatively be used under the terms
25 * of the Common Development and Distribution License Version 1.0
26 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
27 * in the VirtualBox distribution, in which case the provisions of the
28 * CDDL are applicable instead of those of the GPL.
29 *
30 * You may elect to license modified versions of this file under the
31 * terms and conditions of either the GPL or the CDDL or both.
32 *
33 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
34 */
35
36#ifndef IPRT_INCLUDED_sg_h
37#define IPRT_INCLUDED_sg_h
38#ifndef RT_WITHOUT_PRAGMA_ONCE
39# pragma once
40#endif
41
42#include <iprt/types.h>
43
44RT_C_DECLS_BEGIN
45
46/** @defgroup grp_rt_sgbuf RTSgBuf - Scatter / Gather Buffers
47 * @ingroup grp_rt
48 * @{
49 */
50
51/** Pointer to a const S/G entry. */
52typedef const struct RTSGBUF *PCRTSGBUF;
53
54/**
55 * Callback for RTSgBufCopyToFn() called on every segment of the given S/G buffer.
56 *
57 * @returns Number of bytes copied for this segment, a value smaller than cbSrc will stop the copy operation.
58 * @param pSgBuf The S/G buffer for reference.
59 * @param pvSrc Where to copy from.
60 * @param cbSrc The number of bytes in the source buffer.
61 * @param pvUser Opaque user data passed in RTSgBufCopyToFn().
62 */
63typedef DECLCALLBACKTYPE(size_t, FNRTSGBUFCOPYTO, (PCRTSGBUF pSgBuf, const void *pvSrc, size_t cbSrc, void *pvUser));
64/** Pointer to a FNRTSGBUFCOPYTO. */
65typedef FNRTSGBUFCOPYTO *PFNRTSGBUFCOPYTO;
66
67/**
68 * Callback for RTSgBufCopyFromFn() called on every segment of the given S/G buffer.
69 *
70 * @returns Number of bytes copied for this segment, a value smaller than cbDst will stop the copy operation.
71 * @param pSgBuf The S/G buffer for reference.
72 * @param pvDst Where to copy to.
73 * @param cbDst The number of bytes in the destination buffer.
74 * @param pvUser Opaque user data passed in RTSgBufCopyFromFn().
75 */
76typedef DECLCALLBACKTYPE(size_t, FNRTSGBUFCOPYFROM, (PCRTSGBUF pSgBuf, void *pvDst, size_t cbDst, void *pvUser));
77/** Pointer to a FNRTSGBUFCOPYFROM. */
78typedef FNRTSGBUFCOPYFROM *PFNRTSGBUFCOPYFROM;
79
80/**
81 * A S/G entry.
82 */
83typedef struct RTSGSEG
84{
85 /** Pointer to the segment buffer. */
86 void *pvSeg;
87 /** Size of the segment buffer. */
88 size_t cbSeg;
89} RTSGSEG;
90/** Pointer to a S/G entry. */
91typedef RTSGSEG *PRTSGSEG;
92/** Pointer to a const S/G entry. */
93typedef const RTSGSEG *PCRTSGSEG;
94/** Pointer to a S/G entry pointer. */
95typedef PRTSGSEG *PPRTSGSEG;
96
97/**
98 * A S/G buffer.
99 *
100 * The members should be treated as private.
101 *
102 * @warning There is a lot of code, especially in the VFS area of IPRT, that
103 * totally ignores the idxSeg, pvSegCur and cbSegLeft members! So,
104 * it is not recommended to pass buffers that aren't fully reset or
105 * where cbSegLeft is shorter than what paSegs describes.
106 */
107typedef struct RTSGBUF
108{
109 /** Pointer to the scatter/gather array. */
110 PCRTSGSEG paSegs;
111 /** Number of segments. */
112 unsigned cSegs;
113
114 /** Current segment we are in. */
115 unsigned idxSeg;
116 /** Pointer to current byte within the current segment. */
117 void *pvSegCur;
118 /** Number of bytes left in the current segment. */
119 size_t cbSegLeft;
120} RTSGBUF;
121/** Pointer to a S/G entry. */
122typedef RTSGBUF *PRTSGBUF;
123/** Pointer to a S/G entry pointer. */
124typedef PRTSGBUF *PPRTSGBUF;
125
126
127/**
128 * Sums up the length of all the segments.
129 *
130 * @returns The complete segment length.
131 * @param pSgBuf The S/G buffer to check out.
132 */
133DECLINLINE(size_t) RTSgBufCalcTotalLength(PCRTSGBUF pSgBuf)
134{
135 size_t cb = 0;
136 unsigned i = pSgBuf->cSegs;
137 while (i-- > 0)
138 cb += pSgBuf->paSegs[i].cbSeg;
139 return cb;
140}
141
142/**
143 * Sums up the number of bytes left from the current position.
144 *
145 * @returns Number of bytes left.
146 * @param pSgBuf The S/G buffer to check out.
147 */
148DECLINLINE(size_t) RTSgBufCalcLengthLeft(PCRTSGBUF pSgBuf)
149{
150 size_t cb = pSgBuf->cbSegLeft;
151 unsigned i = pSgBuf->cSegs;
152 while (i-- > pSgBuf->idxSeg + 1)
153 cb += pSgBuf->paSegs[i].cbSeg;
154 return cb;
155}
156
157/**
158 * Checks if the current buffer position is at the start of the first segment.
159 *
160 * @returns true / false.
161 * @param pSgBuf The S/G buffer to check out.
162 */
163DECLINLINE(bool) RTSgBufIsAtStart(PCRTSGBUF pSgBuf)
164{
165 return pSgBuf->idxSeg == 0
166 && ( pSgBuf->cSegs == 0
167 || pSgBuf->pvSegCur == pSgBuf->paSegs[0].pvSeg);
168}
169
170/**
171 * Checks if the current buffer position is at the end of all the segments.
172 *
173 * @returns true / false.
174 * @param pSgBuf The S/G buffer to check out.
175 */
176DECLINLINE(bool) RTSgBufIsAtEnd(PCRTSGBUF pSgBuf)
177{
178 return pSgBuf->idxSeg >= pSgBuf->cSegs
179 || ( pSgBuf->cbSegLeft == 0 /* sg.cpp doesn't create this situation, but just in case someone does. */
180 && pSgBuf->idxSeg + 1 == pSgBuf->cSegs);
181}
182
183/**
184 * Checks if the current buffer position is at the start of the current segment.
185 *
186 * @returns true / false.
187 * @param pSgBuf The S/G buffer to check out.
188 */
189DECLINLINE(bool) RTSgBufIsAtStartOfSegment(PCRTSGBUF pSgBuf)
190{
191 return pSgBuf->idxSeg < pSgBuf->cSegs
192 && pSgBuf->paSegs[pSgBuf->idxSeg].pvSeg == pSgBuf->pvSegCur;
193}
194
195/**
196 * Initialize a S/G buffer structure.
197 *
198 * @param pSgBuf Pointer to the S/G buffer to initialize.
199 * @param paSegs Pointer to the start of the segment array.
200 * @param cSegs Number of segments in the array.
201 *
202 * @note paSegs and cSegs can be NULL and 0 respectively to indicate an empty
203 * S/G buffer. Operations on the S/G buffer will not do anything in
204 * this case.
205 */
206RTDECL(void) RTSgBufInit(PRTSGBUF pSgBuf, PCRTSGSEG paSegs, size_t cSegs);
207
208/**
209 * Resets the internal buffer position of the S/G buffer to the beginning.
210 *
211 * @param pSgBuf The S/G buffer to reset.
212 */
213RTDECL(void) RTSgBufReset(PRTSGBUF pSgBuf);
214
215/**
216 * Clones a given S/G buffer.
217 *
218 * This is only a shallow copy. Both S/G buffers will point to the same segment
219 * array.
220 *
221 * The buffer position will be preserved.
222 *
223 * @param pSgBufNew The new S/G buffer to clone to.
224 * @param pSgBufOld The source S/G buffer to clone from.
225 */
226RTDECL(void) RTSgBufClone(PRTSGBUF pSgBufNew, PCRTSGBUF pSgBufOld);
227
228/**
229 * Returns the next segment in the S/G buffer or NULL if no segments left.
230 *
231 * @returns Pointer to the next segment in the S/G buffer.
232 * @param pSgBuf The S/G buffer.
233 * @param cbDesired The max number of bytes to get.
234 * @param pcbSeg Where to store the size of the returned segment, this is
235 * equal or smaller than @a cbDesired.
236 *
237 * @note Use RTSgBufAdvance() to advance after read/writing into the buffer.
238 */
239DECLINLINE(void *) RTSgBufGetCurrentSegment(PRTSGBUF pSgBuf, size_t cbDesired, size_t *pcbSeg)
240{
241 if (!RTSgBufIsAtEnd(pSgBuf))
242 {
243 *pcbSeg = RT_MIN(cbDesired, pSgBuf->cbSegLeft);
244 return pSgBuf->pvSegCur;
245 }
246 *pcbSeg = 0;
247 return NULL;
248}
249
250/**
251 * Returns the next segment in the S/G buffer or NULL if no segment is left.
252 *
253 * @returns Pointer to the next segment in the S/G buffer.
254 * @param pSgBuf The S/G buffer.
255 * @param pcbSeg Where to store the size of the returned segment.
256 * Holds the number of bytes requested initially or 0 to
257 * indicate that the size doesn't matter.
258 * This may contain fewer bytes on success if the current segment
259 * is smaller than the amount of bytes requested.
260 *
261 * @note This operation advances the internal buffer pointer of both S/G buffers.
262 */
263RTDECL(void *) RTSgBufGetNextSegment(PRTSGBUF pSgBuf, size_t *pcbSeg);
264
265/**
266 * Copy data between two S/G buffers.
267 *
268 * @returns The number of bytes copied.
269 * @param pSgBufDst The destination S/G buffer.
270 * @param pSgBufSrc The source S/G buffer.
271 * @param cbCopy Number of bytes to copy.
272 *
273 * @note This operation advances the internal buffer pointer of both S/G buffers.
274 */
275RTDECL(size_t) RTSgBufCopy(PRTSGBUF pSgBufDst, PRTSGBUF pSgBufSrc, size_t cbCopy);
276
277/**
278 * Compares the content of two S/G buffers.
279 *
280 * @returns Whatever memcmp returns.
281 * @param pSgBuf1 First S/G buffer.
282 * @param pSgBuf2 Second S/G buffer.
283 * @param cbCmp How many bytes to compare.
284 *
285 * @note This operation doesn't change the internal position of the S/G buffers.
286 */
287RTDECL(int) RTSgBufCmp(PCRTSGBUF pSgBuf1, PCRTSGBUF pSgBuf2, size_t cbCmp);
288
289/**
290 * Compares the content of two S/G buffers - advanced version.
291 *
292 * @returns Whatever memcmp returns.
293 * @param pSgBuf1 First S/G buffer.
294 * @param pSgBuf2 Second S/G buffer.
295 * @param cbCmp How many bytes to compare.
296 * @param poffDiff Where to store the offset of the first different byte
297 * in the buffer starting from the position of the S/G
298 * buffer before this call.
299 * @param fAdvance Flag whether the internal buffer position should be advanced.
300 *
301 */
302RTDECL(int) RTSgBufCmpEx(PRTSGBUF pSgBuf1, PRTSGBUF pSgBuf2, size_t cbCmp, size_t *poffDiff, bool fAdvance);
303
304/**
305 * Fills an S/G buf with a constant byte.
306 *
307 * @returns The number of actually filled bytes.
308 * Can be less than than cbSet if the end of the S/G buffer was reached.
309 * @param pSgBuf The S/G buffer.
310 * @param bFill The byte to fill the buffer with.
311 * @param cbToSet How many bytes to set.
312 *
313 * @note This operation advances the internal buffer pointer of the S/G buffer.
314 */
315RTDECL(size_t) RTSgBufSet(PRTSGBUF pSgBuf, uint8_t bFill, size_t cbToSet);
316
317/**
318 * Copies data from an S/G buffer into a given non scattered buffer.
319 *
320 * @returns Number of bytes copied.
321 * @param pSgBuf The S/G buffer to copy from.
322 * @param pvBuf Buffer to copy the data into.
323 * @param cbCopy How many bytes to copy.
324 *
325 * @note This operation advances the internal buffer pointer of the S/G buffer.
326 */
327RTDECL(size_t) RTSgBufCopyToBuf(PRTSGBUF pSgBuf, void *pvBuf, size_t cbCopy);
328
329/**
330 * Copies data from a non scattered buffer into an S/G buffer.
331 *
332 * @returns Number of bytes copied.
333 * @param pSgBuf The S/G buffer to copy to.
334 * @param pvBuf Buffer to copy the data from.
335 * @param cbCopy How many bytes to copy.
336 *
337 * @note This operation advances the internal buffer pointer of the S/G buffer.
338 */
339RTDECL(size_t) RTSgBufCopyFromBuf(PRTSGBUF pSgBuf, const void *pvBuf, size_t cbCopy);
340
341/**
342 * Copies data from the given S/G buffer to a destination handled by the given callback.
343 *
344 * @returns Number of bytes copied.
345 * @param pSgBuf The S/G buffer to copy from.
346 * @param cbCopy How many bytes to copy.
347 * @param pfnCopyTo The callback to call on every S/G buffer segment until the operation finished.
348 * @param pvUser Opaque user data to pass in the given callback.
349 *
350 * @note This operation advances the internal buffer pointer of the S/G buffer.
351 */
352RTDECL(size_t) RTSgBufCopyToFn(PRTSGBUF pSgBuf, size_t cbCopy, PFNRTSGBUFCOPYTO pfnCopyTo, void *pvUser);
353
354/**
355 * Copies data to the given S/G buffer from a destination handled by the given callback.
356 *
357 * @returns Number of bytes copied.
358 * @param pSgBuf The S/G buffer to copy to.
359 * @param cbCopy How many bytes to copy.
360 * @param pfnCopyFrom The callback to call on every S/G buffer segment until the operation finished.
361 * @param pvUser Opaque user data to pass in the given callback.
362 *
363 * @note This operation advances the internal buffer pointer of the S/G buffer.
364 */
365RTDECL(size_t) RTSgBufCopyFromFn(PRTSGBUF pSgBuf, size_t cbCopy, PFNRTSGBUFCOPYFROM pfnCopyFrom, void *pvUser);
366
367/**
368 * Advances the internal buffer pointer.
369 *
370 * @returns Number of bytes the pointer was moved forward.
371 * @param pSgBuf The S/G buffer.
372 * @param cbAdvance Number of bytes to move forward.
373 */
374RTDECL(size_t) RTSgBufAdvance(PRTSGBUF pSgBuf, size_t cbAdvance);
375
376/**
377 * Constructs a new segment array starting from the current position
378 * and describing the given number of bytes.
379 *
380 * @returns Number of bytes the array describes.
381 * @param pSgBuf The S/G buffer.
382 * @param paSeg The uninitialized segment array.
383 * If NULL pcSeg will contain the number of segments needed
384 * to describe the requested amount of data.
385 * @param pcSeg The number of segments the given array has.
386 * This will hold the actual number of entries needed upon return.
387 * @param cbData Number of bytes the new array should describe.
388 *
389 * @note This operation advances the internal buffer pointer of the S/G buffer if paSeg is not NULL.
390 */
391RTDECL(size_t) RTSgBufSegArrayCreate(PRTSGBUF pSgBuf, PRTSGSEG paSeg, unsigned *pcSeg, size_t cbData);
392
393/**
394 * Returns whether the given S/G buffer is zeroed out from the current position
395 * upto the number of bytes to check.
396 *
397 * @retval true if the buffer has only zeros
398 * @retval false otherwise.
399 * @param pSgBuf The S/G buffer.
400 * @param cbCheck Number of bytes to check.
401 *
402 * @note This operation advances the internal buffer pointer of the S/G buffer.
403 */
404RTDECL(bool) RTSgBufIsZero(PRTSGBUF pSgBuf, size_t cbCheck);
405
406/**
407 * Maps the given S/G buffer to a segment array of another type (for example to
408 * iovec on POSIX or WSABUF on Windows).
409 *
410 * @param paMapped Where to store the pointer to the start of the native
411 * array or NULL. The memory needs to be freed with
412 * RTMemTmpFree().
413 * @param pSgBuf The S/G buffer to map.
414 * @param Struct Struct used as the destination.
415 * @param pvBufField Name of the field holding the pointer to a buffer.
416 * @param TypeBufPtr Type of the buffer pointer.
417 * @param cbBufField Name of the field holding the size of the buffer.
418 * @param TypeBufSize Type of the field for the buffer size.
419 * @param cSegsMapped Where to store the number of segments the native array
420 * has.
421 *
422 * @note This operation maps the whole S/G buffer starting at the current
423 * internal position. The internal buffer position is unchanged by
424 * this operation.
425 *
426 * @remark Usage is a bit ugly but saves a few lines of duplicated code
427 * somewhere else and makes it possible to keep the S/G buffer members
428 * private without going through RTSgBufSegArrayCreate() first.
429 */
430#define RTSgBufMapToNative(paMapped, pSgBuf, Struct, pvBufField, TypeBufPtr, cbBufField, TypeBufSize, cSegsMapped) \
431 do \
432 { \
433 AssertCompileMemberSize(Struct, pvBufField, RT_SIZEOFMEMB(RTSGSEG, pvSeg)); \
434 /*AssertCompile(RT_SIZEOFMEMB(Struct, cbBufField) >= RT_SIZEOFMEMB(RTSGSEG, cbSeg));*/ \
435 (cSegsMapped) = (pSgBuf)->cSegs - (pSgBuf)->idxSeg; \
436 \
437 /* We need room for at least one segment. */ \
438 if ((pSgBuf)->cSegs == (pSgBuf)->idxSeg) \
439 (cSegsMapped)++; \
440 \
441 (paMapped) = (Struct *)RTMemTmpAllocZ((cSegsMapped) * sizeof(Struct)); \
442 if ((paMapped)) \
443 { \
444 /* The first buffer is special because we could be in the middle of a segment. */ \
445 (paMapped)[0].pvBufField = (TypeBufPtr)(pSgBuf)->pvSegCur; \
446 (paMapped)[0].cbBufField = (TypeBufSize)(pSgBuf)->cbSegLeft; \
447 \
448 for (unsigned i = 1; i < (cSegsMapped); i++) \
449 { \
450 (paMapped)[i].pvBufField = (TypeBufPtr)(pSgBuf)->paSegs[(pSgBuf)->idxSeg + i].pvSeg; \
451 (paMapped)[i].cbBufField = (TypeBufSize)(pSgBuf)->paSegs[(pSgBuf)->idxSeg + i].cbSeg; \
452 } \
453 } \
454 } while (0)
455
456RT_C_DECLS_END
457
458/** @} */
459
460#endif /* !IPRT_INCLUDED_sg_h */
461
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette