Actual source code: segbuffer.c

  1: #include <petscsys.h>

  3: struct _PetscSegBufferLink {
  4:   struct _PetscSegBufferLink *tail;
  5:   PetscCount                  alloc; /* number of units allocated */
  6:   PetscCount                  used;
  7:   PetscCount                  tailused;
  8:   union
  9:   { /* Dummy types to ensure alignment */
 10:     PetscReal dummy_real;
 11:     PetscInt  dummy_int;
 12:     char      array[1]; /* This array is over-allocated for the size of the link */
 13:   } u;
 14: };

 16: /* Segmented (extendable) array implementation */
 17: struct _n_PetscSegBuffer {
 18:   struct _PetscSegBufferLink *head;
 19:   size_t                      unitbytes;
 20: };

 22: static PetscErrorCode PetscSegBufferAlloc_Private(PetscSegBuffer seg, PetscCount count)
 23: {
 24:   PetscCount                  alloc;
 25:   struct _PetscSegBufferLink *newlink, *s;

 27:   PetscFunctionBegin;
 28:   s = seg->head;
 29:   /* Grow at least fast enough to hold next item, like Fibonacci otherwise (up to 1MB chunks) */
 30:   alloc = PetscMax(s->used + count, PetscMin(1000000 / ((PetscCount)seg->unitbytes) + 1, s->alloc + s->tailused));
 31:   PetscCall(PetscMalloc(offsetof(struct _PetscSegBufferLink, u) + alloc * seg->unitbytes, &newlink));
 32:   PetscCall(PetscMemzero(newlink, offsetof(struct _PetscSegBufferLink, u)));

 34:   newlink->tailused = s->used + s->tailused;
 35:   newlink->tail     = s;
 36:   newlink->alloc    = alloc;
 37:   seg->head         = newlink;
 38:   PetscFunctionReturn(PETSC_SUCCESS);
 39: }

 41: /*@C
 42:   PetscSegBufferCreate - create a segmented buffer

 44:   Not Collective, No Fortran Support

 46:   Input Parameters:
 47: + unitbytes - number of bytes that each entry will contain
 48: - expected  - expected/typical number of entries

 50:   Output Parameter:
 51: . seg - `PetscSegBuffer` object

 53:   Level: developer

 55: .seealso: `PetscSegBufferGet()`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`, `PetscSegBufferExtractInPlace()`, `PetscSegBufferDestroy()`,
 56:           `PetscSegBuffer`
 57: @*/
 58: PetscErrorCode PetscSegBufferCreate(size_t unitbytes, PetscCount expected, PetscSegBuffer *seg)
 59: {
 60:   struct _PetscSegBufferLink *head;

 62:   PetscFunctionBegin;
 63:   PetscCall(PetscNew(seg));
 64:   PetscCall(PetscMalloc(offsetof(struct _PetscSegBufferLink, u) + expected * unitbytes, &head));
 65:   PetscCall(PetscMemzero(head, offsetof(struct _PetscSegBufferLink, u)));

 67:   head->alloc       = expected;
 68:   (*seg)->unitbytes = unitbytes;
 69:   (*seg)->head      = head;
 70:   PetscFunctionReturn(PETSC_SUCCESS);
 71: }

 73: /*@C
 74:   PetscSegBufferGet - get new buffer space from a segmented buffer

 76:   Not Collective, No Fortran Support

 78:   Input Parameters:
 79: + seg   - `PetscSegBuffer` buffer
 80: - count - number of entries needed

 82:   Output Parameter:
 83: . buf - address of new buffer for contiguous data

 85:   Level: developer

 87: .seealso: `PetscSegBufferCreate()`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`, `PetscSegBufferExtractInPlace()`, `PetscSegBufferDestroy()`,
 88:           `PetscSegBuffer`, `PetscSegBufferGetInts()`
 89: @*/
 90: PetscErrorCode PetscSegBufferGet(PetscSegBuffer seg, PetscCount count, void *buf)
 91: {
 92:   struct _PetscSegBufferLink *s;

 94:   PetscFunctionBegin;
 95:   s = seg->head;
 96:   if (PetscUnlikely(s->used + count > s->alloc)) PetscCall(PetscSegBufferAlloc_Private(seg, count));
 97:   s             = seg->head;
 98:   *(char **)buf = &s->u.array[s->used * seg->unitbytes];
 99:   s->used += count;
100:   PetscFunctionReturn(PETSC_SUCCESS);
101: }

103: /*@C
104:   PetscSegBufferDestroy - destroy segmented buffer

106:   Not Collective, No Fortran Support

108:   Input Parameter:
109: . seg - address of segmented buffer object

111:   Level: developer

113: .seealso: `PetscSegBuffer`, `PetscSegBufferCreate()`
114: @*/
115: PetscErrorCode PetscSegBufferDestroy(PetscSegBuffer *seg)
116: {
117:   struct _PetscSegBufferLink *s;

119:   PetscFunctionBegin;
120:   if (!*seg) PetscFunctionReturn(PETSC_SUCCESS);
121:   for (s = (*seg)->head; s;) {
122:     struct _PetscSegBufferLink *tail = s->tail;
123:     PetscCall(PetscFree(s));
124:     s = tail;
125:   }
126:   PetscCall(PetscFree(*seg));
127:   PetscFunctionReturn(PETSC_SUCCESS);
128: }

130: /*@C
131:   PetscSegBufferExtractTo - extract contiguous data to provided buffer and reset segmented buffer

133:   Not Collective, No Fortran Support

135:   Input Parameters:
136: + seg    - segmented buffer
137: - contig - allocated buffer to hold contiguous data

139:   Level: developer

141: .seealso: `PetscSegBufferCreate()`, `PetscSegBufferGet()`, `PetscSegBufferDestroy()`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractInPlace()`,
142:           `PetscSegBuffer`
143: @*/
144: PetscErrorCode PetscSegBufferExtractTo(PetscSegBuffer seg, void *contig)
145: {
146:   size_t                      unitbytes;
147:   struct _PetscSegBufferLink *s, *t;
148:   char                       *ptr;

150:   PetscFunctionBegin;
151:   unitbytes = seg->unitbytes;
152:   s         = seg->head;
153:   ptr       = PetscSafePointerPlusOffset((char *)contig, s->tailused * unitbytes);
154:   PetscCall(PetscMemcpy(ptr, s->u.array, s->used * unitbytes));
155:   for (t = s->tail; t;) {
156:     struct _PetscSegBufferLink *tail = t->tail;
157:     ptr -= t->used * unitbytes;
158:     PetscCall(PetscMemcpy(ptr, t->u.array, t->used * unitbytes));
159:     PetscCall(PetscFree(t));
160:     t = tail;
161:   }
162:   PetscCheck(ptr == contig, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Tail count does not match");
163:   s->used     = 0;
164:   s->tailused = 0;
165:   s->tail     = NULL;
166:   PetscFunctionReturn(PETSC_SUCCESS);
167: }

169: /*@C
170:   PetscSegBufferExtractAlloc - extract contiguous data to new allocation and reset segmented buffer

172:   Not Collective, No Fortran Support

174:   Input Parameter:
175: . seg - `PetscSegBuffer` buffer

177:   Output Parameter:
178: . contiguous - address of new array containing contiguous data, caller frees with `PetscFree()`

180:   Level: developer

182:   Developer Notes:
183:   'seg' argument is a pointer so that implementation could reallocate, though this is not currently done

185: .seealso: `PetscSegBufferCreate()`, `PetscSegBufferGet()`, `PetscSegBufferDestroy()`, `PetscSegBufferExtractTo()`, `PetscSegBufferExtractInPlace()`,
186:           `PetscSegBuffer`
187: @*/
188: PetscErrorCode PetscSegBufferExtractAlloc(PetscSegBuffer seg, void *contiguous)
189: {
190:   struct _PetscSegBufferLink *s;
191:   void                       *contig;

193:   PetscFunctionBegin;
194:   s = seg->head;

196:   PetscCall(PetscMalloc((s->used + s->tailused) * seg->unitbytes, &contig));
197:   PetscCall(PetscSegBufferExtractTo(seg, contig));
198:   *(void **)contiguous = contig;
199:   PetscFunctionReturn(PETSC_SUCCESS);
200: }

202: /*@C
203:   PetscSegBufferExtractInPlace - extract in-place contiguous representation of data and reset segmented buffer for reuse

205:   Not Collective, No Fortran Support

207:   Input Parameter:
208: . seg - `PetscSegBuffer` object

210:   Output Parameter:
211: . contig - address of pointer to contiguous memory, may be `NULL`

213:   Level: developer

215: .seealso: `PetscSegBuffer`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`
216: @*/
217: PetscErrorCode PetscSegBufferExtractInPlace(PetscSegBuffer seg, void *contig)
218: {
219:   struct _PetscSegBufferLink *head;

221:   PetscFunctionBegin;
222:   head = seg->head;
223:   if (PetscUnlikely(head->tail)) {
224:     PetscSegBuffer newseg;

226:     PetscCall(PetscSegBufferCreate(seg->unitbytes, head->used + head->tailused, &newseg));
227:     PetscCall(PetscSegBufferExtractTo(seg, newseg->head->u.array));
228:     seg->head    = newseg->head;
229:     newseg->head = head;
230:     PetscCall(PetscSegBufferDestroy(&newseg));
231:     head = seg->head;
232:   }
233:   if (contig) *(char **)contig = head->u.array;
234:   head->used = 0;
235:   PetscFunctionReturn(PETSC_SUCCESS);
236: }

238: /*@C
239:   PetscSegBufferGetSize - get currently used number of entries of a `PetscSegBuffer`

241:   Not Collective, No Fortran Support

243:   Input Parameter:
244: . seg - `PetscSegBuffer` object

246:   Output Parameter:
247: . usedsize - number of used units

249:   Level: developer

251: .seealso: `PetscSegBuffer`, `PetscSegBufferExtractAlloc()`, `PetscSegBufferExtractTo()`, `PetscSegBufferCreate()`, `PetscSegBufferGet()`
252: @*/
253: PetscErrorCode PetscSegBufferGetSize(PetscSegBuffer seg, PetscCount *usedsize)
254: {
255:   PetscFunctionBegin;
256:   *usedsize = seg->head->tailused + seg->head->used;
257:   PetscFunctionReturn(PETSC_SUCCESS);
258: }

260: /*@C
261:   PetscSegBufferUnuse - return some unused entries obtained with an overzealous `PetscSegBufferGet()`

263:   Not Collective, No Fortran Support

265:   Input Parameters:
266: + seg    - `PetscSegBuffer` object
267: - unused - number of unused units to return

269:   Level: developer

271: .seealso: `PetscSegBuffer`, `PetscSegBufferCreate()`, `PetscSegBufferGet()`
272: @*/
273: PetscErrorCode PetscSegBufferUnuse(PetscSegBuffer seg, PetscCount unused)
274: {
275:   struct _PetscSegBufferLink *head;

277:   PetscFunctionBegin;
278:   head = seg->head;
279:   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);
280:   head->used -= unused;
281:   PetscFunctionReturn(PETSC_SUCCESS);
282: }