xref: /petsc/src/sys/utils/segbuffer.c (revision d8e47b638cf8f604a99e9678e1df24f82d959cd7)
10f453b92SJed Brown #include <petscsys.h>
20f453b92SJed Brown 
3137cf7b6SJed Brown struct _PetscSegBufferLink {
4137cf7b6SJed Brown   struct _PetscSegBufferLink *tail;
5*6497c311SBarry Smith   PetscCount                  alloc; /* number of units allocated */
6*6497c311SBarry Smith   PetscCount                  used;
7*6497c311SBarry Smith   PetscCount                  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 
PetscSegBufferAlloc_Private(PetscSegBuffer seg,PetscCount count)22*6497c311SBarry Smith static PetscErrorCode PetscSegBufferAlloc_Private(PetscSegBuffer seg, PetscCount count)
23d71ae5a4SJacob Faibussowitsch {
24*6497c311SBarry Smith   PetscCount                  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) */
30*6497c311SBarry Smith   alloc = PetscMax(s->used + count, PetscMin(1000000 / ((PetscCount)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;
383ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
390f453b92SJed Brown }
400f453b92SJed Brown 
410f453b92SJed Brown /*@C
42811af0c4SBarry Smith   PetscSegBufferCreate - create a segmented buffer
430f453b92SJed Brown 
44cc4c1da9SBarry Smith   Not Collective, No Fortran Support
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:
51667f096bSBarry Smith . seg - `PetscSegBuffer` object
520f453b92SJed Brown 
530f453b92SJed Brown   Level: developer
540f453b92SJed Brown 
55667f096bSBarry Smith .seealso: `PetscSegBufferGet()`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`, `PetscSegBufferExtractInPlace()`, `PetscSegBufferDestroy()`,
56667f096bSBarry Smith           `PetscSegBuffer`
570f453b92SJed Brown @*/
PetscSegBufferCreate(size_t unitbytes,PetscCount expected,PetscSegBuffer * seg)58*6497c311SBarry Smith PetscErrorCode PetscSegBufferCreate(size_t unitbytes, PetscCount expected, PetscSegBuffer *seg)
59d71ae5a4SJacob Faibussowitsch {
60137cf7b6SJed Brown   struct _PetscSegBufferLink *head;
610f453b92SJed Brown 
620f453b92SJed Brown   PetscFunctionBegin;
639566063dSJacob Faibussowitsch   PetscCall(PetscNew(seg));
649566063dSJacob Faibussowitsch   PetscCall(PetscMalloc(offsetof(struct _PetscSegBufferLink, u) + expected * unitbytes, &head));
659566063dSJacob Faibussowitsch   PetscCall(PetscMemzero(head, offsetof(struct _PetscSegBufferLink, u)));
660f453b92SJed Brown 
67137cf7b6SJed Brown   head->alloc       = expected;
680f453b92SJed Brown   (*seg)->unitbytes = unitbytes;
69137cf7b6SJed Brown   (*seg)->head      = head;
703ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
710f453b92SJed Brown }
720f453b92SJed Brown 
730f453b92SJed Brown /*@C
740f453b92SJed Brown   PetscSegBufferGet - get new buffer space from a segmented buffer
750f453b92SJed Brown 
76cc4c1da9SBarry Smith   Not Collective, No Fortran Support
770f453b92SJed Brown 
784165533cSJose E. Roman   Input Parameters:
79667f096bSBarry Smith + seg   - `PetscSegBuffer` buffer
800f453b92SJed Brown - count - number of entries needed
810f453b92SJed Brown 
824165533cSJose E. Roman   Output Parameter:
830f453b92SJed Brown . buf - address of new buffer for contiguous data
840f453b92SJed Brown 
850f453b92SJed Brown   Level: developer
860f453b92SJed Brown 
87667f096bSBarry Smith .seealso: `PetscSegBufferCreate()`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`, `PetscSegBufferExtractInPlace()`, `PetscSegBufferDestroy()`,
88d598313aSBarry Smith           `PetscSegBuffer`, `PetscSegBufferGetInts()`
890f453b92SJed Brown @*/
PetscSegBufferGet(PetscSegBuffer seg,PetscCount count,void * buf)90*6497c311SBarry Smith PetscErrorCode PetscSegBufferGet(PetscSegBuffer seg, PetscCount count, void *buf)
91d71ae5a4SJacob Faibussowitsch {
92137cf7b6SJed Brown   struct _PetscSegBufferLink *s;
930f453b92SJed Brown 
940f453b92SJed Brown   PetscFunctionBegin;
95137cf7b6SJed Brown   s = seg->head;
969566063dSJacob Faibussowitsch   if (PetscUnlikely(s->used + count > s->alloc)) PetscCall(PetscSegBufferAlloc_Private(seg, count));
97137cf7b6SJed Brown   s             = seg->head;
98137cf7b6SJed Brown   *(char **)buf = &s->u.array[s->used * seg->unitbytes];
990f453b92SJed Brown   s->used += count;
1003ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1010f453b92SJed Brown }
1020f453b92SJed Brown 
1030f453b92SJed Brown /*@C
1040f453b92SJed Brown   PetscSegBufferDestroy - destroy segmented buffer
1050f453b92SJed Brown 
106cc4c1da9SBarry Smith   Not Collective, No Fortran Support
1070f453b92SJed Brown 
1084165533cSJose E. Roman   Input Parameter:
1090f453b92SJed Brown . seg - address of segmented buffer object
1100f453b92SJed Brown 
1110f453b92SJed Brown   Level: developer
1120f453b92SJed Brown 
113667f096bSBarry Smith .seealso: `PetscSegBuffer`, `PetscSegBufferCreate()`
1140f453b92SJed Brown @*/
PetscSegBufferDestroy(PetscSegBuffer * seg)115d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferDestroy(PetscSegBuffer *seg)
116d71ae5a4SJacob Faibussowitsch {
117137cf7b6SJed Brown   struct _PetscSegBufferLink *s;
1180f453b92SJed Brown 
1190f453b92SJed Brown   PetscFunctionBegin;
1203ba16761SJacob Faibussowitsch   if (!*seg) PetscFunctionReturn(PETSC_SUCCESS);
121137cf7b6SJed Brown   for (s = (*seg)->head; s;) {
122137cf7b6SJed Brown     struct _PetscSegBufferLink *tail = s->tail;
1239566063dSJacob Faibussowitsch     PetscCall(PetscFree(s));
1240f453b92SJed Brown     s = tail;
1250f453b92SJed Brown   }
1269566063dSJacob Faibussowitsch   PetscCall(PetscFree(*seg));
1273ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1280f453b92SJed Brown }
1290f453b92SJed Brown 
1300f453b92SJed Brown /*@C
13147452e7bSJed Brown   PetscSegBufferExtractTo - extract contiguous data to provided buffer and reset segmented buffer
13247452e7bSJed Brown 
133cc4c1da9SBarry Smith   Not Collective, No Fortran Support
13447452e7bSJed Brown 
1354165533cSJose E. Roman   Input Parameters:
13647452e7bSJed Brown + seg    - segmented buffer
13747452e7bSJed Brown - contig - allocated buffer to hold contiguous data
13847452e7bSJed Brown 
13947452e7bSJed Brown   Level: developer
14047452e7bSJed Brown 
141667f096bSBarry Smith .seealso: `PetscSegBufferCreate()`, `PetscSegBufferGet()`, `PetscSegBufferDestroy()`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractInPlace()`,
142667f096bSBarry Smith           `PetscSegBuffer`
14347452e7bSJed Brown @*/
PetscSegBufferExtractTo(PetscSegBuffer seg,void * contig)144d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferExtractTo(PetscSegBuffer seg, void *contig)
145d71ae5a4SJacob Faibussowitsch {
14613e3f751SJed Brown   size_t                      unitbytes;
147137cf7b6SJed Brown   struct _PetscSegBufferLink *s, *t;
14847452e7bSJed Brown   char                       *ptr;
14947452e7bSJed Brown 
15047452e7bSJed Brown   PetscFunctionBegin;
151137cf7b6SJed Brown   unitbytes = seg->unitbytes;
152137cf7b6SJed Brown   s         = seg->head;
1538e3a54c0SPierre Jolivet   ptr       = PetscSafePointerPlusOffset((char *)contig, s->tailused * unitbytes);
1549566063dSJacob Faibussowitsch   PetscCall(PetscMemcpy(ptr, s->u.array, s->used * unitbytes));
15547452e7bSJed Brown   for (t = s->tail; t;) {
156137cf7b6SJed Brown     struct _PetscSegBufferLink *tail = t->tail;
15747452e7bSJed Brown     ptr -= t->used * unitbytes;
1589566063dSJacob Faibussowitsch     PetscCall(PetscMemcpy(ptr, t->u.array, t->used * unitbytes));
1599566063dSJacob Faibussowitsch     PetscCall(PetscFree(t));
16047452e7bSJed Brown     t = tail;
16147452e7bSJed Brown   }
16208401ef6SPierre Jolivet   PetscCheck(ptr == contig, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Tail count does not match");
16347452e7bSJed Brown   s->used     = 0;
16447452e7bSJed Brown   s->tailused = 0;
16547452e7bSJed Brown   s->tail     = NULL;
1663ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
16747452e7bSJed Brown }
16847452e7bSJed Brown 
16947452e7bSJed Brown /*@C
17047452e7bSJed Brown   PetscSegBufferExtractAlloc - extract contiguous data to new allocation and reset segmented buffer
1710f453b92SJed Brown 
172cc4c1da9SBarry Smith   Not Collective, No Fortran Support
1730f453b92SJed Brown 
1744165533cSJose E. Roman   Input Parameter:
175667f096bSBarry Smith . seg - `PetscSegBuffer` buffer
1760f453b92SJed Brown 
1774165533cSJose E. Roman   Output Parameter:
178811af0c4SBarry Smith . contiguous - address of new array containing contiguous data, caller frees with `PetscFree()`
1790f453b92SJed Brown 
1800f453b92SJed Brown   Level: developer
1810f453b92SJed Brown 
182aec76313SJacob Faibussowitsch   Developer Notes:
183811af0c4SBarry Smith   'seg' argument is a pointer so that implementation could reallocate, though this is not currently done
18447452e7bSJed Brown 
185667f096bSBarry Smith .seealso: `PetscSegBufferCreate()`, `PetscSegBufferGet()`, `PetscSegBufferDestroy()`, `PetscSegBufferExtractTo()`, `PetscSegBufferExtractInPlace()`,
186667f096bSBarry Smith           `PetscSegBuffer`
1870f453b92SJed Brown @*/
PetscSegBufferExtractAlloc(PetscSegBuffer seg,void * contiguous)188d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferExtractAlloc(PetscSegBuffer seg, void *contiguous)
189d71ae5a4SJacob Faibussowitsch {
190137cf7b6SJed Brown   struct _PetscSegBufferLink *s;
19147452e7bSJed Brown   void                       *contig;
1920f453b92SJed Brown 
1930f453b92SJed Brown   PetscFunctionBegin;
194137cf7b6SJed Brown   s = seg->head;
1950f453b92SJed Brown 
1969566063dSJacob Faibussowitsch   PetscCall(PetscMalloc((s->used + s->tailused) * seg->unitbytes, &contig));
1979566063dSJacob Faibussowitsch   PetscCall(PetscSegBufferExtractTo(seg, contig));
19847452e7bSJed Brown   *(void **)contiguous = contig;
1993ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2000f453b92SJed Brown }
20147452e7bSJed Brown 
20247452e7bSJed Brown /*@C
20347452e7bSJed Brown   PetscSegBufferExtractInPlace - extract in-place contiguous representation of data and reset segmented buffer for reuse
20447452e7bSJed Brown 
205cc4c1da9SBarry Smith   Not Collective, No Fortran Support
20647452e7bSJed Brown 
2074165533cSJose E. Roman   Input Parameter:
208667f096bSBarry Smith . seg - `PetscSegBuffer` object
20947452e7bSJed Brown 
2104165533cSJose E. Roman   Output Parameter:
211667f096bSBarry Smith . contig - address of pointer to contiguous memory, may be `NULL`
21247452e7bSJed Brown 
21347452e7bSJed Brown   Level: developer
21447452e7bSJed Brown 
215667f096bSBarry Smith .seealso: `PetscSegBuffer`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`
21647452e7bSJed Brown @*/
PetscSegBufferExtractInPlace(PetscSegBuffer seg,void * contig)217d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscSegBufferExtractInPlace(PetscSegBuffer seg, void *contig)
218d71ae5a4SJacob Faibussowitsch {
219137cf7b6SJed Brown   struct _PetscSegBufferLink *head;
22047452e7bSJed Brown 
22147452e7bSJed Brown   PetscFunctionBegin;
222137cf7b6SJed Brown   head = seg->head;
223137cf7b6SJed Brown   if (PetscUnlikely(head->tail)) {
224137cf7b6SJed Brown     PetscSegBuffer newseg;
22547452e7bSJed Brown 
2269566063dSJacob Faibussowitsch     PetscCall(PetscSegBufferCreate(seg->unitbytes, head->used + head->tailused, &newseg));
2279566063dSJacob Faibussowitsch     PetscCall(PetscSegBufferExtractTo(seg, newseg->head->u.array));
228137cf7b6SJed Brown     seg->head    = newseg->head;
229137cf7b6SJed Brown     newseg->head = head;
2309566063dSJacob Faibussowitsch     PetscCall(PetscSegBufferDestroy(&newseg));
231137cf7b6SJed Brown     head = seg->head;
23247452e7bSJed Brown   }
233d2b3fd65SBarry Smith   if (contig) *(char **)contig = head->u.array;
234137cf7b6SJed Brown   head->used = 0;
2353ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2360f453b92SJed Brown }
23701d09641SJed Brown 
23801d09641SJed Brown /*@C
239*6497c311SBarry Smith   PetscSegBufferGetSize - get currently used number of entries of a `PetscSegBuffer`
24001d09641SJed Brown 
241cc4c1da9SBarry Smith   Not Collective, No Fortran Support
24201d09641SJed Brown 
2434165533cSJose E. Roman   Input Parameter:
244667f096bSBarry Smith . seg - `PetscSegBuffer` object
24501d09641SJed Brown 
2464165533cSJose E. Roman   Output Parameter:
24701d09641SJed Brown . usedsize - number of used units
24801d09641SJed Brown 
24901d09641SJed Brown   Level: developer
25001d09641SJed Brown 
251667f096bSBarry Smith .seealso: `PetscSegBuffer`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`, `PetscSegBufferCreate()`, `PetscSegBufferGet()`
25201d09641SJed Brown @*/
PetscSegBufferGetSize(PetscSegBuffer seg,PetscCount * usedsize)253*6497c311SBarry Smith PetscErrorCode PetscSegBufferGetSize(PetscSegBuffer seg, PetscCount *usedsize)
254d71ae5a4SJacob Faibussowitsch {
25501d09641SJed Brown   PetscFunctionBegin;
256137cf7b6SJed Brown   *usedsize = seg->head->tailused + seg->head->used;
2573ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
25801d09641SJed Brown }
25901d09641SJed Brown 
26001d09641SJed Brown /*@C
261811af0c4SBarry Smith   PetscSegBufferUnuse - return some unused entries obtained with an overzealous `PetscSegBufferGet()`
26201d09641SJed Brown 
263cc4c1da9SBarry Smith   Not Collective, No Fortran Support
26401d09641SJed Brown 
2654165533cSJose E. Roman   Input Parameters:
266667f096bSBarry Smith + seg    - `PetscSegBuffer` object
267*6497c311SBarry Smith - unused - number of unused units to return
26801d09641SJed Brown 
26901d09641SJed Brown   Level: developer
27001d09641SJed Brown 
271667f096bSBarry Smith .seealso: `PetscSegBuffer`, `PetscSegBufferCreate()`, `PetscSegBufferGet()`
27201d09641SJed Brown @*/
PetscSegBufferUnuse(PetscSegBuffer seg,PetscCount unused)273*6497c311SBarry Smith PetscErrorCode PetscSegBufferUnuse(PetscSegBuffer seg, PetscCount unused)
274d71ae5a4SJacob Faibussowitsch {
275137cf7b6SJed Brown   struct _PetscSegBufferLink *head;
27601d09641SJed Brown 
27701d09641SJed Brown   PetscFunctionBegin;
278137cf7b6SJed Brown   head = seg->head;
27908401ef6SPierre 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);
280137cf7b6SJed Brown   head->used -= unused;
2813ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
28201d09641SJed Brown }
283