mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-13 21:04:00 +00:00
Discussion of these changes can be found on the Mercury developers
mailing list archives from June 2018.
COPYING.LIB:
Add a special linking exception to the LGPL.
*:
Update references to COPYING.LIB.
Clean up some minor errors that have accumulated in copyright
messages.
219 lines
12 KiB
C
219 lines
12 KiB
C
// vim: ts=4 sw=4 expandtab ft=c
|
|
|
|
// Copyright (C) 1998-2000,2002, 2004-2005 The University of Melbourne.
|
|
// Copyright (C) 2016, 2018 The Mercury team.
|
|
// This file is distributed under the terms specified in COPYING.LIB.
|
|
|
|
// mercury_array_macros.h defines macros for dealing with expandable arrays.
|
|
|
|
#ifndef MERCURY_ARRAY_MACROS_H
|
|
#define MERCURY_ARRAY_MACROS_H
|
|
|
|
#include "mercury_reg_workarounds.h" // for MR_assign_structure
|
|
|
|
// The MR_ensure_room_for_next macro works with a group of three variables
|
|
// that follow the pattern
|
|
//
|
|
// Item *widgets = NULL;
|
|
// int widget_max = 0;
|
|
// int widget_next = 0;
|
|
//
|
|
// where widgets is a pointer to a MR_malloc'd array of items, widget_max
|
|
// gives the number of elements in the array, and widget_next is the
|
|
// index of the first free slot in the widgets array. Widget_max is
|
|
// zero if and only if widgets is NULL.
|
|
//
|
|
// MR_ensure_room_for_next(widget, Item, INIT_SIZE) checks whether
|
|
// there is enough room in the widgets array to add a new item.
|
|
// If not, doubles the size of the existing widgets array, or
|
|
// allocates an array of INIT_SIZE items if the widgets array
|
|
// has not been initialized before.
|
|
//
|
|
// BEWARE: YOU NEED TO BE VERY CAREFUL OF POINTERS TO OBJECTS ON THE GC HEAP:
|
|
//
|
|
// - For MR_ensure_room_for_next(), the memory is allocated with MR_malloc(),
|
|
// which means that if conservative GC is enabled, the array elements
|
|
// _MUST NOT_ point to the GC heap (or at least must not be the _only_
|
|
// pointer to any object on the GC heap); but the array address can be
|
|
// stored anywhere.
|
|
//
|
|
// - For MR_GC_ensure_room_for_next(), the memory is allocated on the GC heap,
|
|
// which means that the array elements can point to anything;
|
|
// but the array address _MUST NOT_ be stored in memory allocated with
|
|
// malloc() or MR_malloc() (unless it is also stored in some place
|
|
// which _is_ scanned by the collector).
|
|
//
|
|
// It is the caller's responsibility to deallocate the memory for the array
|
|
// if/when it is no longer needed, using MR_free() or MR_GC_free_attrib()
|
|
// respectively.
|
|
|
|
#define MR_ensure_room_for_next(base, type, init) \
|
|
do { \
|
|
if (base##_next >= base##_max) { \
|
|
if (base##_max == 0) { \
|
|
base##_max = (init); \
|
|
base##s = MR_NEW_ARRAY(type, base##_max); \
|
|
} else { \
|
|
base##_max *= 2; \
|
|
base##s = MR_RESIZE_ARRAY(base##s, type, base##_max); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
#define MR_GC_ensure_room_for_next(base, type, init, alloc_id) \
|
|
do { \
|
|
if (base##_next >= base##_max) { \
|
|
if (base##_max == 0) { \
|
|
base##_max = (init); \
|
|
base##s = MR_GC_NEW_ARRAY_ATTRIB(type, base##_max, \
|
|
(alloc_id)); \
|
|
} else { \
|
|
base##_max *= 2; \
|
|
base##s = MR_GC_RESIZE_ARRAY_ATTRIB(base##s, type, \
|
|
base##_max); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
// MR_ensure_big_enough makes the same assumptions as MR_ensure_room_for_next,
|
|
// and operates almost the same way. The exception is that it does not assume
|
|
// that the array grows one item at a time; instead it ensures that the array
|
|
// is big enough to contain the element at index `slot'. Since with this regime
|
|
// there is no notion of the "next" slot, this macro does not access, nor does
|
|
// it require the existence of, base##_next.
|
|
//
|
|
// BEWARE: YOU NEED TO BE VERY CAREFUL OF POINTERS TO OBJECTS ON THE GC HEAP.
|
|
// See the comment for MR_ensure_room_for_next().
|
|
|
|
#define MR_ensure_big_enough(slot, base, type, init) \
|
|
do { \
|
|
if ((slot) >= base##_max) { \
|
|
if (base##_max == 0) { \
|
|
base##_max = MR_max((init), (slot) + 1); \
|
|
base##s = MR_NEW_ARRAY(type, base##_max); \
|
|
} else { \
|
|
base##_max = MR_max(base##_max * 2, (slot) + 1); \
|
|
base##s = MR_RESIZE_ARRAY(base##s, type, base##_max); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
// MR_ensure_big_enough2 works like MR_ensure_big_enough, except that
|
|
// it resizes two arrays at once. These two arrays are named base##s1 and
|
|
// base##s2, and since they are always the same size, they share the
|
|
// base##_max variable.
|
|
//
|
|
// BEWARE: YOU NEED TO BE VERY CAREFUL OF POINTERS TO OBJECTS ON THE GC HEAP.
|
|
// See the comment for MR_ensure_room_for_next().
|
|
|
|
#define MR_ensure_big_enough2(slot, base, s1, s2, type, init) \
|
|
do { \
|
|
if ((slot) >= base##_max) { \
|
|
if (base##_max == 0) { \
|
|
base##_max = MR_max((init), (slot) + 1); \
|
|
base##s1 = MR_NEW_ARRAY(type, base##_max); \
|
|
base##s2 = MR_NEW_ARRAY(type, base##_max); \
|
|
} else { \
|
|
base##_max = MR_max(base##_max * 2, (slot) + 1); \
|
|
base##s1 = MR_RESIZE_ARRAY(base##s1, type, base##_max); \
|
|
base##s2 = MR_RESIZE_ARRAY(base##s2, type, base##_max); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
// MR_bsearch(int num_elements, int& element, MR_bool& found, COMPARE)
|
|
//
|
|
// Given a sorted array, this macro performs a binary search.
|
|
// If the search is successful, MR_bsearch sets the `found' parameter
|
|
// to MR_TRUE and the `element' parameter to the index of the desired item.
|
|
// If the search is unsuccessful, MR_bsearch sets `found' to MR_FALSE;
|
|
// `element' will be clobbered.
|
|
//
|
|
// The number of the elements in the array is given by the `num_elements'
|
|
// parameter.
|
|
// The `COMPARE' parameter should be an expression of type int which compares
|
|
// the value at the index specified by the current value of `element'
|
|
// with the desired value, and returns <0, 0, or >0 according to whether
|
|
// it is less than, equal to, or greater than the desired value.
|
|
//
|
|
// The name of the array to be searched is not explicitly a parameter;
|
|
// its identity is encoded in the boolean expression of the `COMPARE'
|
|
// parameter.
|
|
|
|
#define MR_bsearch(num_elements, element, found, COMPARE) \
|
|
do { \
|
|
int lo; \
|
|
int hi; \
|
|
int diff; \
|
|
\
|
|
/* \
|
|
** We initialize `element' here only to avoid gcc warnings \
|
|
** about possibly accessing an uninitialized variable \
|
|
** in code using MR_bsearch(). \
|
|
*/ \
|
|
\
|
|
(element) = 0; \
|
|
lo = 0; \
|
|
hi = (num_elements) - 1; \
|
|
(found) = MR_FALSE; \
|
|
while (lo <= hi) { \
|
|
(element) = (lo + hi) / 2; \
|
|
diff = (COMPARE); \
|
|
if (diff == 0) { \
|
|
(found) = MR_TRUE; \
|
|
break; \
|
|
} else if (diff < 0) { \
|
|
lo = (element) + 1; \
|
|
} else { \
|
|
hi = (element) - 1; \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
// MR_find_first_match(int num_elements, int& element, MR_bool& found, COMPARE)
|
|
//
|
|
// Given a sorted array, this macro finds the first element in the array
|
|
// for which `COMPARE' is zero (MR_bsearch finds an arbitrary element).
|
|
// Otherwise, the parameters and behaviour are the same as for MR_bsearch.
|
|
|
|
#define MR_find_first_match(num_elements, element, found, COMPARE) \
|
|
do { \
|
|
MR_bsearch((num_elements), (element), (found), (COMPARE)); \
|
|
if (found) { \
|
|
while ((element) > 0) { \
|
|
(element)--; \
|
|
if ((COMPARE) != 0) { \
|
|
(element)++; \
|
|
break; \
|
|
} \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
// MR_prepare_insert_into_sorted(array[], int& next, int& element, COMPARE)
|
|
//
|
|
// Given a sorted array of `items', this prepares for the insertion of a
|
|
// new item into the array at the proper point. It finds the index at which
|
|
// the new item should be inserted, and moves all items at and above that
|
|
// index one position to the right to make room for the new item.
|
|
//
|
|
// The `next' parameter holds the number of elements in the array;
|
|
// it is incremented by this macro. The macro returns the index of the slot
|
|
// at which the new item should be inserted in the `element' parameter.
|
|
// The `COMPARE' parameter should be an expression of type int which compares
|
|
// the item at the index specified by the current value of `element' with
|
|
// the item being inserted, and returns <0, 0, or >0 according to whether
|
|
// it is less than, equal to, or greater than the item being inserted.
|
|
|
|
#define MR_prepare_insert_into_sorted(items, next, element, COMPARE) \
|
|
do { \
|
|
(element) = (next) - 1; \
|
|
while ((element) >= 0 && (COMPARE) > 0) { \
|
|
MR_assign_structure(items[element + 1], items[element]); \
|
|
(element) -= 1; \
|
|
} \
|
|
(element) += 1; \
|
|
(next) += 1; \
|
|
} while (0)
|
|
|
|
#endif // MERCURY_ARRAY_MACROS_H
|