xref: /petsc/src/sys/utils/segbuffer.c (revision 3ba1676111f5c958fe6c2729b46ca4d523958bb3)
10f453b92SJed Brown #include <petscsys.h>
20f453b92SJed Brown 
3137cf7b6SJed Brown struct _PetscSegBufferLink {
4137cf7b6SJed Brown   struct _PetscSegBufferLink *tail;
513e3f751SJed Brown   size_t                      alloc;
613e3f751SJed Brown   size_t                      used;
713e3f751SJed Brown   size_t                      tailused;
89371c9d4SSatish Balay   union
99371c9d4SSatish Balay   { /* Dummy types to ensure alignment */
100f453b92SJed Brown     PetscReal dummy_real;
110f453b92SJed Brown     PetscInt  dummy_int;
12137cf7b6SJed Brown     char      array[1]; /* This array is over-allocated for the size of the link */
130f453b92SJed Brown   } u;
140f453b92SJed Brown };
150f453b92SJed Brown 
16137cf7b6SJed Brown /* Segmented (extendable) array implementation */
17137cf7b6SJed Brown struct _n_PetscSegBuffer {
18137cf7b6SJed Brown   struct _PetscSegBufferLink *head;
1913e3f751SJed Brown   size_t                      unitbytes;
20137cf7b6SJed Brown };
21137cf7b6SJed Brown 
22d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscSegBufferAlloc_Private(PetscSegBuffer seg, size_t count)
23d71ae5a4SJacob Faibussowitsch {
2413e3f751SJed Brown   size_t                      alloc;
25137cf7b6SJed Brown   struct _PetscSegBufferLink *newlink, *s;
260f453b92SJed Brown 
270f453b92SJed Brown   PetscFunctionBegin;
28137cf7b6SJed Brown   s = seg->head;
290f453b92SJed Brown   /* Grow at least fast enough to hold next item, like Fibonacci otherwise (up to 1MB chunks) */
30137cf7b6SJed Brown   alloc = PetscMax(s->used + count, PetscMin(1000000 / seg->unitbytes + 1, s->alloc + s->tailused));
319566063dSJacob Faibussowitsch   PetscCall(PetscMalloc(offsetof(struct _PetscSegBufferLink, u) + alloc * seg->unitbytes, &newlink));
329566063dSJacob Faibussowitsch   PetscCall(PetscMemzero(newlink, offsetof(struct _PetscSegBufferLink, u)));
330f453b92SJed Brown 
34137cf7b6SJed Brown   newlink->tailused = s->used + s->tailused;
35137cf7b6SJed Brown   newlink->tail     = s;
36137cf7b6SJed Brown   newlink->alloc    = alloc;
37137cf7b6SJed Brown   seg->head         = newlink;
38*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
390f453b92SJed Brown }
400f453b92SJed Brown 
410f453b92SJed Brown /*@C
42811af0c4SBarry Smith    PetscSegBufferCreate - create a segmented buffer
430f453b92SJed Brown 
440f453b92SJed Brown    Not Collective
450f453b92SJed Brown 
464165533cSJose E. Roman    Input Parameters:
470f453b92SJed Brown +  unitbytes - number of bytes that each entry will contain
480f453b92SJed Brown -  expected - expected/typical number of entries
490f453b92SJed Brown 
504165533cSJose E. Roman    Output Parameter:
510f453b92SJed Brown .  seg - segmented buffer object
520f453b92SJed Brown 
530f453b92SJed Brown    Level: developer
540f453b92SJed Brown 
55db781477SPatrick Sanan .seealso: `PetscSegBufferGet()`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`, `PetscSegBufferExtractInPlace()`, `PetscSegBufferDestroy()`
560f453b92SJed Brown @*/
57d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferCreate(size_t unitbytes, size_t expected, PetscSegBuffer *seg)
58d71ae5a4SJacob Faibussowitsch {
59137cf7b6SJed Brown   struct _PetscSegBufferLink *head;
600f453b92SJed Brown 
610f453b92SJed Brown   PetscFunctionBegin;
629566063dSJacob Faibussowitsch   PetscCall(PetscNew(seg));
639566063dSJacob Faibussowitsch   PetscCall(PetscMalloc(offsetof(struct _PetscSegBufferLink, u) + expected * unitbytes, &head));
649566063dSJacob Faibussowitsch   PetscCall(PetscMemzero(head, offsetof(struct _PetscSegBufferLink, u)));
650f453b92SJed Brown 
66137cf7b6SJed Brown   head->alloc       = expected;
670f453b92SJed Brown   (*seg)->unitbytes = unitbytes;
68137cf7b6SJed Brown   (*seg)->head      = head;
69*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
700f453b92SJed Brown }
710f453b92SJed Brown 
720f453b92SJed Brown /*@C
730f453b92SJed Brown    PetscSegBufferGet - get new buffer space from a segmented buffer
740f453b92SJed Brown 
750f453b92SJed Brown    Not Collective
760f453b92SJed Brown 
774165533cSJose E. Roman    Input Parameters:
780f453b92SJed Brown +  seg - address of segmented buffer
790f453b92SJed Brown -  count - number of entries needed
800f453b92SJed Brown 
814165533cSJose E. Roman    Output Parameter:
820f453b92SJed Brown .  buf - address of new buffer for contiguous data
830f453b92SJed Brown 
840f453b92SJed Brown    Level: developer
850f453b92SJed Brown 
86db781477SPatrick Sanan .seealso: `PetscSegBufferCreate()`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`, `PetscSegBufferExtractInPlace()`, `PetscSegBufferDestroy()`
870f453b92SJed Brown @*/
88d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferGet(PetscSegBuffer seg, size_t count, void *buf)
89d71ae5a4SJacob Faibussowitsch {
90137cf7b6SJed Brown   struct _PetscSegBufferLink *s;
910f453b92SJed Brown 
920f453b92SJed Brown   PetscFunctionBegin;
93137cf7b6SJed Brown   s = seg->head;
949566063dSJacob Faibussowitsch   if (PetscUnlikely(s->used + count > s->alloc)) PetscCall(PetscSegBufferAlloc_Private(seg, count));
95137cf7b6SJed Brown   s             = seg->head;
96137cf7b6SJed Brown   *(char **)buf = &s->u.array[s->used * seg->unitbytes];
970f453b92SJed Brown   s->used += count;
98*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
990f453b92SJed Brown }
1000f453b92SJed Brown 
1010f453b92SJed Brown /*@C
1020f453b92SJed Brown    PetscSegBufferDestroy - destroy segmented buffer
1030f453b92SJed Brown 
1040f453b92SJed Brown    Not Collective
1050f453b92SJed Brown 
1064165533cSJose E. Roman    Input Parameter:
1070f453b92SJed Brown .  seg - address of segmented buffer object
1080f453b92SJed Brown 
1090f453b92SJed Brown    Level: developer
1100f453b92SJed Brown 
111db781477SPatrick Sanan .seealso: `PetscSegBufferCreate()`
1120f453b92SJed Brown @*/
113d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferDestroy(PetscSegBuffer *seg)
114d71ae5a4SJacob Faibussowitsch {
115137cf7b6SJed Brown   struct _PetscSegBufferLink *s;
1160f453b92SJed Brown 
1170f453b92SJed Brown   PetscFunctionBegin;
118*3ba16761SJacob Faibussowitsch   if (!*seg) PetscFunctionReturn(PETSC_SUCCESS);
119137cf7b6SJed Brown   for (s = (*seg)->head; s;) {
120137cf7b6SJed Brown     struct _PetscSegBufferLink *tail = s->tail;
1219566063dSJacob Faibussowitsch     PetscCall(PetscFree(s));
1220f453b92SJed Brown     s = tail;
1230f453b92SJed Brown   }
1249566063dSJacob Faibussowitsch   PetscCall(PetscFree(*seg));
125*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1260f453b92SJed Brown }
1270f453b92SJed Brown 
1280f453b92SJed Brown /*@C
12947452e7bSJed Brown    PetscSegBufferExtractTo - extract contiguous data to provided buffer and reset segmented buffer
13047452e7bSJed Brown 
13147452e7bSJed Brown    Not Collective
13247452e7bSJed Brown 
1334165533cSJose E. Roman    Input Parameters:
13447452e7bSJed Brown +  seg - segmented buffer
13547452e7bSJed Brown -  contig - allocated buffer to hold contiguous data
13647452e7bSJed Brown 
13747452e7bSJed Brown    Level: developer
13847452e7bSJed Brown 
139db781477SPatrick Sanan .seealso: `PetscSegBufferCreate()`, `PetscSegBufferGet()`, `PetscSegBufferDestroy()`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractInPlace()`
14047452e7bSJed Brown @*/
141d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferExtractTo(PetscSegBuffer seg, void *contig)
142d71ae5a4SJacob Faibussowitsch {
14313e3f751SJed Brown   size_t                      unitbytes;
144137cf7b6SJed Brown   struct _PetscSegBufferLink *s, *t;
14547452e7bSJed Brown   char                       *ptr;
14647452e7bSJed Brown 
14747452e7bSJed Brown   PetscFunctionBegin;
148137cf7b6SJed Brown   unitbytes = seg->unitbytes;
149137cf7b6SJed Brown   s         = seg->head;
15047452e7bSJed Brown   ptr       = ((char *)contig) + s->tailused * unitbytes;
1519566063dSJacob Faibussowitsch   PetscCall(PetscMemcpy(ptr, s->u.array, s->used * unitbytes));
15247452e7bSJed Brown   for (t = s->tail; t;) {
153137cf7b6SJed Brown     struct _PetscSegBufferLink *tail = t->tail;
15447452e7bSJed Brown     ptr -= t->used * unitbytes;
1559566063dSJacob Faibussowitsch     PetscCall(PetscMemcpy(ptr, t->u.array, t->used * unitbytes));
1569566063dSJacob Faibussowitsch     PetscCall(PetscFree(t));
15747452e7bSJed Brown     t = tail;
15847452e7bSJed Brown   }
15908401ef6SPierre Jolivet   PetscCheck(ptr == contig, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Tail count does not match");
16047452e7bSJed Brown   s->used     = 0;
16147452e7bSJed Brown   s->tailused = 0;
16247452e7bSJed Brown   s->tail     = NULL;
163*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
16447452e7bSJed Brown }
16547452e7bSJed Brown 
16647452e7bSJed Brown /*@C
16747452e7bSJed Brown    PetscSegBufferExtractAlloc - extract contiguous data to new allocation and reset segmented buffer
1680f453b92SJed Brown 
1690f453b92SJed Brown    Not Collective
1700f453b92SJed Brown 
1714165533cSJose E. Roman    Input Parameter:
1720f453b92SJed Brown .  seg - segmented buffer
1730f453b92SJed Brown 
1744165533cSJose E. Roman    Output Parameter:
175811af0c4SBarry Smith .  contiguous - address of new array containing contiguous data, caller frees with `PetscFree()`
1760f453b92SJed Brown 
1770f453b92SJed Brown    Level: developer
1780f453b92SJed Brown 
179811af0c4SBarry Smith    Developer Note:
180811af0c4SBarry Smith    'seg' argument is a pointer so that implementation could reallocate, though this is not currently done
18147452e7bSJed Brown 
182db781477SPatrick Sanan .seealso: `PetscSegBufferCreate()`, `PetscSegBufferGet()`, `PetscSegBufferDestroy()`, `PetscSegBufferExtractTo()`, `PetscSegBufferExtractInPlace()`
1830f453b92SJed Brown @*/
184d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferExtractAlloc(PetscSegBuffer seg, void *contiguous)
185d71ae5a4SJacob Faibussowitsch {
186137cf7b6SJed Brown   struct _PetscSegBufferLink *s;
18747452e7bSJed Brown   void                       *contig;
1880f453b92SJed Brown 
1890f453b92SJed Brown   PetscFunctionBegin;
190137cf7b6SJed Brown   s = seg->head;
1910f453b92SJed Brown 
1929566063dSJacob Faibussowitsch   PetscCall(PetscMalloc((s->used + s->tailused) * seg->unitbytes, &contig));
1939566063dSJacob Faibussowitsch   PetscCall(PetscSegBufferExtractTo(seg, contig));
19447452e7bSJed Brown   *(void **)contiguous = contig;
195*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1960f453b92SJed Brown }
19747452e7bSJed Brown 
19847452e7bSJed Brown /*@C
19947452e7bSJed Brown    PetscSegBufferExtractInPlace - extract in-place contiguous representation of data and reset segmented buffer for reuse
20047452e7bSJed Brown 
20101d09641SJed Brown    Not Collective
20247452e7bSJed Brown 
2034165533cSJose E. Roman    Input Parameter:
20447452e7bSJed Brown .  seg - segmented buffer object
20547452e7bSJed Brown 
2064165533cSJose E. Roman    Output Parameter:
207d2b3fd65SBarry Smith .  contig - address of pointer to contiguous memory, may be NULL
20847452e7bSJed Brown 
20947452e7bSJed Brown    Level: developer
21047452e7bSJed Brown 
211db781477SPatrick Sanan .seealso: `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`
21247452e7bSJed Brown @*/
213d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferExtractInPlace(PetscSegBuffer seg, void *contig)
214d71ae5a4SJacob Faibussowitsch {
215137cf7b6SJed Brown   struct _PetscSegBufferLink *head;
21647452e7bSJed Brown 
21747452e7bSJed Brown   PetscFunctionBegin;
218137cf7b6SJed Brown   head = seg->head;
219137cf7b6SJed Brown   if (PetscUnlikely(head->tail)) {
220137cf7b6SJed Brown     PetscSegBuffer newseg;
22147452e7bSJed Brown 
2229566063dSJacob Faibussowitsch     PetscCall(PetscSegBufferCreate(seg->unitbytes, head->used + head->tailused, &newseg));
2239566063dSJacob Faibussowitsch     PetscCall(PetscSegBufferExtractTo(seg, newseg->head->u.array));
224137cf7b6SJed Brown     seg->head    = newseg->head;
225137cf7b6SJed Brown     newseg->head = head;
2269566063dSJacob Faibussowitsch     PetscCall(PetscSegBufferDestroy(&newseg));
227137cf7b6SJed Brown     head = seg->head;
22847452e7bSJed Brown   }
229d2b3fd65SBarry Smith   if (contig) *(char **)contig = head->u.array;
230137cf7b6SJed Brown   head->used = 0;
231*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2320f453b92SJed Brown }
23301d09641SJed Brown 
23401d09641SJed Brown /*@C
23501d09641SJed Brown    PetscSegBufferGetSize - get currently used size of segmented buffer
23601d09641SJed Brown 
23701d09641SJed Brown    Not Collective
23801d09641SJed Brown 
2394165533cSJose E. Roman    Input Parameter:
24001d09641SJed Brown .  seg - segmented buffer object
24101d09641SJed Brown 
2424165533cSJose E. Roman    Output Parameter:
24301d09641SJed Brown .  usedsize - number of used units
24401d09641SJed Brown 
24501d09641SJed Brown    Level: developer
24601d09641SJed Brown 
247db781477SPatrick Sanan .seealso: `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`, `PetscSegBufferCreate()`, `PetscSegBufferGet()`
24801d09641SJed Brown @*/
249d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferGetSize(PetscSegBuffer seg, size_t *usedsize)
250d71ae5a4SJacob Faibussowitsch {
25101d09641SJed Brown   PetscFunctionBegin;
252137cf7b6SJed Brown   *usedsize = seg->head->tailused + seg->head->used;
253*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
25401d09641SJed Brown }
25501d09641SJed Brown 
25601d09641SJed Brown /*@C
257811af0c4SBarry Smith    PetscSegBufferUnuse - return some unused entries obtained with an overzealous `PetscSegBufferGet()`
25801d09641SJed Brown 
25901d09641SJed Brown    Not Collective
26001d09641SJed Brown 
2614165533cSJose E. Roman    Input Parameters:
26201d09641SJed Brown +  seg - segmented buffer object
26301d09641SJed Brown -  unused - number of unused units
26401d09641SJed Brown 
26501d09641SJed Brown    Level: developer
26601d09641SJed Brown 
267db781477SPatrick Sanan .seealso: `PetscSegBufferCreate()`, `PetscSegBufferGet()`
26801d09641SJed Brown @*/
269d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferUnuse(PetscSegBuffer seg, size_t unused)
270d71ae5a4SJacob Faibussowitsch {
271137cf7b6SJed Brown   struct _PetscSegBufferLink *head;
27201d09641SJed Brown 
27301d09641SJed Brown   PetscFunctionBegin;
274137cf7b6SJed Brown   head = seg->head;
27508401ef6SPierre Jolivet   PetscCheck(head->used >= unused, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Attempt to return more unused entries (%zu) than previously gotten (%zu)", unused, head->used);
276137cf7b6SJed Brown   head->used -= unused;
277*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
27801d09641SJed Brown }
279