Fixed-capacity (“in place”) array containers.
Motivation: HPC inner loops where the upper bound on the number of
elements is known ahead of time, where an allocate is unacceptable,
and where push/pop/size semantics with bounds checking are still
wanted. The element storage is a fixed-size component, never
allocatable and never a pointer, so an object of these types can
live on the stack, in a block construct, or as a local of an
OpenMP/OpenACC region without touching the heap.
Capacity is the module parameter PIC_FIXED_ARRAY_CAPACITY. It is a
compile-time constant shared by every instance; change it in one place
if your problem needs a different bound. A deliberate design choice is
that this module does not use a parameterized derived type (a len
type parameter would be the textbook answer) because PDT support is
poor or absent on several of the compilers PIC exists to support.
Usage: type(fixed_array_int_t) :: stack type(error_t) :: err integer(default_int) :: value
call stack%push_back(42_default_int, err) if (err%has_error()) call err%fatal() call stack%pop_back(value, err)
Every operation that can fail reports through error_t with
ERROR_VALIDATION: overflowing the capacity, popping an empty
container and out-of-range indexing are all errors, never a silent
write past the end and never a crash.
The err argument is intent(inout) and is written to only when
the operation fails; a successful call leaves it exactly as it was.
This is deliberate and is what keeps the promise of no heap traffic:
error_t has an allocatable message component, so an
intent(out) error argument would make the compiler emit a
deallocation of that component on entry to every single call,
putting a free in the middle of the hot loop. Callers that reuse
one error_t across several calls should call err%clear() between
them, exactly as elsewhere in PIC.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer(kind=default_int), | public, | parameter | :: | PIC_FIXED_ARRAY_CAPACITY | = | 256_default_int |
Maximum number of elements any fixed array can hold. Compile-time
constant: the storage of every instance is exactly this many
elements, so a |
| character(len=*), | private, | parameter | :: | MSG_OUT_OF_RANGE | = | "fixed array index out of range, valid range is 1:size()" | |
| character(len=*), | private, | parameter | :: | MSG_OVERFLOW | = | "fixed array is full, cannot push_back beyond PIC_FIXED_ARRAY_CAPACITY" | |
| character(len=*), | private, | parameter | :: | MSG_UNDERFLOW | = | "fixed array is empty, cannot pop_back" |
Bounded array of real(dp) with fixed inline storage.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| real(kind=dp), | private | :: | items(PIC_FIXED_ARRAY_CAPACITY) |
Inline storage. Deliberately not |
|||
| integer(kind=default_int), | private | :: | n_items | = | 0 |
Number of live elements, always in |
| procedure, public :: as_array => fixed_array_dp_as_array | |
| procedure, public :: at => fixed_array_dp_at | |
| procedure, public :: capacity => fixed_array_dp_capacity | |
| procedure, public :: clear => fixed_array_dp_clear | |
| procedure, public :: get_unchecked => fixed_array_dp_get_unchecked | |
| procedure, public :: is_empty => fixed_array_dp_is_empty | |
| procedure, public :: is_full => fixed_array_dp_is_full | |
| procedure, public :: pop_back => fixed_array_dp_pop_back | |
| procedure, public :: push_back => fixed_array_dp_push_back | |
| procedure, public :: size => fixed_array_dp_size |
Bounded array of integer(default_int) with fixed inline storage.
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer(kind=default_int), | private | :: | items(PIC_FIXED_ARRAY_CAPACITY) |
Inline storage. Deliberately not |
|||
| integer(kind=default_int), | private | :: | n_items | = | 0 |
Number of live elements, always in |
| procedure, public :: as_array => fixed_array_int_as_array | |
| procedure, public :: at => fixed_array_int_at | |
| procedure, public :: capacity => fixed_array_int_capacity | |
| procedure, public :: clear => fixed_array_int_clear | |
| procedure, public :: get_unchecked => fixed_array_int_get_unchecked | |
| procedure, public :: is_empty => fixed_array_int_is_empty | |
| procedure, public :: is_full => fixed_array_int_is_full | |
| procedure, public :: pop_back => fixed_array_int_pop_back | |
| procedure, public :: push_back => fixed_array_int_push_back | |
| procedure, public :: size => fixed_array_int_size |
Copy of the live elements as a plain array of extent size(),
not capacity(). Returns a zero-length array when empty. This is
how the contents are handed to ordinary Fortran routines; note
that it is the one operation here that does allocate, so keep it
out of the hot path.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_dp_t), | intent(in) | :: | self |
Maximum number of elements, i.e. PIC_FIXED_ARRAY_CAPACITY.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_dp_t), | intent(in) | :: | self |
UNCHECKED element access for inner loops: no bounds check at all.
The caller is responsible for guaranteeing 1 <= index <= size(),
typically by looping over 1, array%size(). Reading an index
outside that range returns undefined data. Use at unless the
bounds check is measurably in the way.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_dp_t), | intent(in) | :: | self | |||
| integer(kind=default_int), | intent(in) | :: | index |
True when no elements are live.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_dp_t), | intent(in) | :: | self |
True when a further push_back would fail.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_dp_t), | intent(in) | :: | self |
Number of live elements.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_dp_t), | intent(in) | :: | self |
Copy of the live elements as a plain array of extent size(),
not capacity(). Returns a zero-length array when empty. This is
how the contents are handed to ordinary Fortran routines; note
that it is the one operation here that does allocate, so keep it
out of the hot path.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_int_t), | intent(in) | :: | self |
Maximum number of elements, i.e. PIC_FIXED_ARRAY_CAPACITY.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_int_t), | intent(in) | :: | self |
UNCHECKED element access for inner loops: no bounds check at all.
The caller is responsible for guaranteeing 1 <= index <= size(),
typically by looping over 1, array%size(). Reading an index
outside that range returns undefined data. Use at unless the
bounds check is measurably in the way.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_int_t), | intent(in) | :: | self | |||
| integer(kind=default_int), | intent(in) | :: | index |
True when no elements are live.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_int_t), | intent(in) | :: | self |
True when a further push_back would fail.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_int_t), | intent(in) | :: | self |
Number of live elements.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_int_t), | intent(in) | :: | self |
Bounds-checked element access, 1-based. Any index outside
1:size() (including indices that are within the capacity but
beyond the live elements) sets value to zero and err to
ERROR_VALIDATION. On success err is left untouched.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_dp_t), | intent(in) | :: | self | |||
| integer(kind=default_int), | intent(in) | :: | index | |||
| real(kind=dp), | intent(out) | :: | value | |||
| type(error_t), | intent(inout) | :: | err |
Drop every element. The storage itself is untouched, so this is a single integer assignment; the array can be reused immediately.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_dp_t), | intent(inout) | :: | self |
Remove and return the last element (LIFO). On an empty array
value is set to zero and err is set to ERROR_VALIDATION.
On success err is left untouched.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_dp_t), | intent(inout) | :: | self | |||
| real(kind=dp), | intent(out) | :: | value | |||
| type(error_t), | intent(inout) | :: | err |
Append an element. If the array is already at capacity the element
is not stored, the size is left unchanged and err is set to
ERROR_VALIDATION. On success err is left untouched.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_dp_t), | intent(inout) | :: | self | |||
| real(kind=dp), | intent(in) | :: | value | |||
| type(error_t), | intent(inout) | :: | err |
Bounds-checked element access, 1-based. Any index outside
1:size() (including indices that are within the capacity but
beyond the live elements) sets value to zero and err to
ERROR_VALIDATION. On success err is left untouched.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_int_t), | intent(in) | :: | self | |||
| integer(kind=default_int), | intent(in) | :: | index | |||
| integer(kind=default_int), | intent(out) | :: | value | |||
| type(error_t), | intent(inout) | :: | err |
Drop every element. The storage itself is untouched, so this is a single integer assignment; the array can be reused immediately.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_int_t), | intent(inout) | :: | self |
Remove and return the last element (LIFO). On an empty array
value is set to zero and err is set to ERROR_VALIDATION.
On success err is left untouched.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_int_t), | intent(inout) | :: | self | |||
| integer(kind=default_int), | intent(out) | :: | value | |||
| type(error_t), | intent(inout) | :: | err |
Append an element. If the array is already at capacity the element
is not stored, the size is left unchanged and err is set to
ERROR_VALIDATION. On success err is left untouched.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(fixed_array_int_t), | intent(inout) | :: | self | |||
| integer(kind=default_int), | intent(in) | :: | value | |||
| type(error_t), | intent(inout) | :: | err |