pic_fixed_array Module

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.


Uses

  • module~~pic_fixed_array~~UsesGraph module~pic_fixed_array pic_fixed_array module~pic_error pic_error module~pic_fixed_array->module~pic_error module~pic_types pic_types module~pic_fixed_array->module~pic_types module~pic_error->module~pic_types module~pic_global_definitions pic_global_definitions module~pic_error->module~pic_global_definitions iso_c_binding iso_c_binding module~pic_types->iso_c_binding iso_fortran_env iso_fortran_env module~pic_types->iso_fortran_env module~pic_global_definitions->module~pic_types

Variables

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 fixed_array_dp_t occupies 256 doubles (2 KiB) regardless of how many elements are live.

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"

Derived Types

type, public ::  fixed_array_dp_t

Bounded array of real(dp) with fixed inline storage.

Components

Type Visibility Attributes Name Initial
real(kind=dp), private :: items(PIC_FIXED_ARRAY_CAPACITY)

Inline storage. Deliberately not allocatable: no heap, ever. Elements above n_items are undefined and must not be read.

integer(kind=default_int), private :: n_items = 0

Number of live elements, always in 0:PIC_FIXED_ARRAY_CAPACITY

Type-Bound Procedures

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

type, public ::  fixed_array_int_t

Bounded array of integer(default_int) with fixed inline storage.

Components

Type Visibility Attributes Name Initial
integer(kind=default_int), private :: items(PIC_FIXED_ARRAY_CAPACITY)

Inline storage. Deliberately not allocatable: no heap, ever. Elements above n_items are undefined and must not be read.

integer(kind=default_int), private :: n_items = 0

Number of live elements, always in 0:PIC_FIXED_ARRAY_CAPACITY

Type-Bound Procedures

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

Functions

private pure function fixed_array_dp_as_array(self) result(arr)

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.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_dp_t), intent(in) :: self

Return Value real(kind=dp), allocatable, (:)

private pure function fixed_array_dp_capacity(self) result(n)

Maximum number of elements, i.e. PIC_FIXED_ARRAY_CAPACITY.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_dp_t), intent(in) :: self

Return Value integer(kind=default_int)

private pure function fixed_array_dp_get_unchecked(self, index) result(value)

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.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_dp_t), intent(in) :: self
integer(kind=default_int), intent(in) :: index

Return Value real(kind=dp)

private pure function fixed_array_dp_is_empty(self) result(empty)

True when no elements are live.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_dp_t), intent(in) :: self

Return Value logical

private pure function fixed_array_dp_is_full(self) result(full)

True when a further push_back would fail.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_dp_t), intent(in) :: self

Return Value logical

private pure function fixed_array_dp_size(self) result(n)

Number of live elements.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_dp_t), intent(in) :: self

Return Value integer(kind=default_int)

private pure function fixed_array_int_as_array(self) result(arr)

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.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_int_t), intent(in) :: self

Return Value integer(kind=default_int), allocatable, (:)

private pure function fixed_array_int_capacity(self) result(n)

Maximum number of elements, i.e. PIC_FIXED_ARRAY_CAPACITY.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_int_t), intent(in) :: self

Return Value integer(kind=default_int)

private pure function fixed_array_int_get_unchecked(self, index) result(value)

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.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_int_t), intent(in) :: self
integer(kind=default_int), intent(in) :: index

Return Value integer(kind=default_int)

private pure function fixed_array_int_is_empty(self) result(empty)

True when no elements are live.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_int_t), intent(in) :: self

Return Value logical

private pure function fixed_array_int_is_full(self) result(full)

True when a further push_back would fail.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_int_t), intent(in) :: self

Return Value logical

private pure function fixed_array_int_size(self) result(n)

Number of live elements.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_int_t), intent(in) :: self

Return Value integer(kind=default_int)


Subroutines

private pure subroutine fixed_array_dp_at(self, index, value, 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.

Arguments

Type IntentOptional 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

private pure subroutine fixed_array_dp_clear(self)

Drop every element. The storage itself is untouched, so this is a single integer assignment; the array can be reused immediately.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_dp_t), intent(inout) :: self

private pure subroutine fixed_array_dp_pop_back(self, value, err)

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.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_dp_t), intent(inout) :: self
real(kind=dp), intent(out) :: value
type(error_t), intent(inout) :: err

private pure subroutine fixed_array_dp_push_back(self, value, 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.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_dp_t), intent(inout) :: self
real(kind=dp), intent(in) :: value
type(error_t), intent(inout) :: err

private pure subroutine fixed_array_int_at(self, index, value, 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.

Arguments

Type IntentOptional 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

private pure subroutine fixed_array_int_clear(self)

Drop every element. The storage itself is untouched, so this is a single integer assignment; the array can be reused immediately.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_int_t), intent(inout) :: self

private pure subroutine fixed_array_int_pop_back(self, value, err)

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.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_int_t), intent(inout) :: self
integer(kind=default_int), intent(out) :: value
type(error_t), intent(inout) :: err

private pure subroutine fixed_array_int_push_back(self, value, 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.

Arguments

Type IntentOptional Attributes Name
class(fixed_array_int_t), intent(inout) :: self
integer(kind=default_int), intent(in) :: value
type(error_t), intent(inout) :: err