--- /dev/null
+#ifndef AMORPH_CLASS
+#define AMORPH_CLASS
+
+/*****************************************************************************\
+* *
+* Name : amorph *
+* Author : Chris Koeritz *
+* *
+*******************************************************************************
+* Copyright (c) 1989-$now By Author. This program is free software; you can *
+* redistribute it and/or modify it under the terms of the GNU General Public *
+* License as published by the Free Software Foundation; either version 2 of *
+* the License or (at your option) any later version. This is online at: *
+* http://www.fsf.org/copyleft/gpl.html *
+* Please send any updates to: fred@gruntose.com *
+\*****************************************************************************/
+
+#include "object_packers.h"
+
+#include <basis/astring.h>
+#include <basis/functions.h>
+#include <basis/contracts.h>
+#include <basis/guards.h>
+
+//! A dynamic container class that holds any kind of object via pointers.
+/*!
+ The object is considered owned by the amorph unless re-acquired from it,
+ and will be deleted when the amorph is destroyed.
+
+ An amorph has a specified number of fields at any one time, but the number
+ can be changed with "zap", "insert" and "adjust". Fields in the amorph
+ are either full or empty, where an empty field in the amorph has NIL as
+ its content. "put" adds a new field to the amorph. "get" retrieves the
+ contents of a field as a constant. "acquire" is used to check a field
+ out of the amorph, meaning that the amorph no longer possesses that field.
+ The legal range of indices in an amorph is from 0 through
+ "elements() - 1". In general, a range violation for an index is
+ treated as an invalid request and is ignored (although BAD_INDEX is
+ returned by functions with compatible return values).
+
+ Model:
+
+ The policy followed in amorph is that once an object is checked in with
+ "put" or "append", then the amorph owns that object. The object must not
+ be destroyed externally, because the amorph will automatically destroy
+ the object when either: 1) the amorph itself is destroyed, or 2) another
+ object is entered into the same field. If the stored object must be
+ destroyed externally, then it should be checked out of the amorph with
+ "acquire"; after that, the pointer may be deleted. "get" and "borrow"
+ return a pointer to the stored item while leaving it checked in to the
+ amorph. it is safe to modify what was "borrow"ed or to look at what one
+ "get"s, but do not destroy the pointers returned from either method.
+*/
+
+namespace structures {
+
+template <class contents>
+class amorph : private basis::array<contents *>
+{
+public:
+ amorph(int elements = 0);
+ //!< constructs an amorph capable of holding "elements" pointers.
+
+ ~amorph();
+
+ int elements() const { return this->length(); }
+ //!< the maximum number of elements currently allowed in this amorph.
+
+ int valid_fields() const { return _fields_used; }
+ //!< Returns the number of fields that have non-NIL contents.
+ /*!< This might be different from the number of total elements. */
+
+ void adjust(int new_max);
+ //!< Changes the maximum number of elements for this amorph.
+ /*!< If the new number is smaller than the original, then the fields at
+ index "new_maximum" and upwards are thrown away. existing fields are
+ kept. */
+
+ void reset(int new_maximum = 0);
+ //!< like adjust but doesn't keep existing contents.
+
+ basis::outcome put(int field, const contents *data);
+ //!< Enters an object into the field at index "field" in the amorph.
+ /*!< If "data" is NIL, then the field is cleared. The amorph considers the
+ pointer "data" to be its own property after put is invoked; "data"
+ should not be destructed since the amorph will automatically do so.
+ This restriction does not hold if the object is checked back out of
+ the amorph with acquire(). */
+
+ basis::outcome append(const contents *data);
+ //!< puts "data" on the end of this amorph.
+ /*!< adds an element to the end of the amorph by increasing the amorph size
+ (with "adjust") and putting the element into the new spot (with "put"). */
+
+ basis::outcome operator += (const contents *data) { return append(data); }
+ //!< a synonym for append.
+
+ //! Returns a constant pointer to the information at the index "field".
+ /*! If no information is stored or the field is out range, then NIL is
+ returned. */
+ const contents *get(int field) const;
+ //! Returns a pointer to the information at the index "field".
+ /*! Also returns NIL for invalid indexes. DO NOT destroy the returned
+ pointer; it is still owned by the amorph. */
+ contents *borrow(int field);
+
+ //! synonym for get.
+ const contents *operator [] (int field) const { return get(field); }
+ //! synonym for borrow.
+ contents *operator [] (int field) { return borrow(field); }
+
+ contents *acquire(int field);
+ //!< Retrieves a "field" from the amorph, taking responsibility for it back.
+ /*!< This function is similar to get, except that the contents are pulled
+ out of the amorph. The contents will no longer be destroyed when the
+ amorph is destroyed. To store the modified contents again, use put,
+ and then the amorph will take over management of the contents again.
+ Note that the index is not zapped with this function; the acquired
+ "field" will simply hold a null pointer. */
+
+ basis::outcome clear(int field);
+ //!< Clears the contents of the field specified.
+ /*!< Clearing an empty field has no effect. Clearing an invalid field has
+ no effect. NOTE: clearing the contents means that the contents are
+ destroyed, not just disconnected. */
+
+ void clear_all();
+ //!< Clears every field in the amorph.
+
+ basis::outcome zap(int start, int end);
+ //!< Removes a range of indices from the amorph.
+ /*!< This does not just clear the field associated with the specified index
+ as "clear" does, it actually changes the number of total elements by
+ removing the indices from the amorph. The new amorph contains the old
+ elements up to just before the "start" and from the "end" + 1 through the
+ end of the amorph. AMORPH_BAD_INDEX is returned if either index is out of
+ range. If the zap succeeds, then AMORPH_OKAY is returned, even if the
+ "end" is less than the "start". */
+
+ basis::outcome insert(int position, int lines_to_add);
+ //!< Adds "lines_to_add" indices to the amorph at the index "position".
+ /*!< If "lines_to_add" is non-positive, the request is ignored. Inserting
+ at a position beyond the bounds of the array is ignored, but a position AT
+ elements() is allowed (it is an append...). */
+
+ int find_empty(basis::outcome &o) const;
+ //!< Returns the index of a free field if there are any.
+ /*!< The returned index is invalid if the "o" is IS_FULL. */
+
+ const contents *next_valid(int &field) const;
+ //!< Returns the contents of the next valid element at or after "field".
+ /*!< "field" is set to the location where an entry was found, if one was
+ actually found. If none exists at "field" or after it, then NIL is
+ returned. */
+
+ int find(const contents *to_locate, basis::outcome &o);
+ //!< Searches the amorph for the contents specified.
+ /*!< Since only pointers to the contents are maintained, the search is
+ based on finding a pointer in the amorph that is identical to "to_locate".
+ if "o" is OKAY, then the index of the entry is returned. If
+ "o" is NOT_FOUND, then the contents are not present. */
+
+ void swap_contents(amorph<contents> &other);
+ //!< Exchanges the contents of "this" and "other".
+ /*!< No validation is performed but this should always succeed given
+ amorphs that are constructed properly. */
+
+private:
+ int _fields_used; //!< The number of fields currently full of info.
+
+ void check_fields(const char *where) const;
+ //!< Crashes out if the field count is wrong.
+ /*!< This is only used for the debugging version. */
+
+ void set_nil(int start, int end);
+ // Puts NIL in the indices between start and end.
+ /*!< Does not delete whatever used to reside there; it just sets the
+ pointers to NIL. */
+
+ // not to be used: amorphs should not be copied because it is intended that
+ // they support storing heavyweight objects that either have no copy
+ // constructors or have high-cost copy constructors.
+ amorph(const amorph &to_copy) {} //!< not to be used.
+ amorph &operator = (const amorph &to_copy) { return *this; }
+ //!< not to be used.
+};
+
+//////////////
+
+// these extensions are phrased as macros to avoid including the headers
+// necessary for compiling them. to use them, just put the macro name in
+// the file where the template is needed.
+
+//////////////
+
+//! This can be used when the templated object has a copy constructor.
+/*! this makes the "to_assign" into a copy of the "to_copy" amorph. */
+template <class contents>
+void amorph_assign(amorph<contents> &to_assign,
+ const amorph<contents> &to_copy);
+
+//////////////
+
+//! support for packing an amorph into an array of bytes.
+/*!
+ this can be used when the underlying object is based on packable or
+ supports the same pack/unpack methods. note that if there are empty spots in
+ the amorph, they are not packed. when the packed form is unpacked again, it will
+ have only the first N elements set, where N is the number of non-empty items.
+*/
+template <class contents>
+void amorph_pack(basis::byte_array &packed_form, const amorph<contents> &to_pack);
+
+//! unpacks the amorph from an array of bytes.
+template <class contents>
+bool amorph_unpack(basis::byte_array &packed_form, amorph<contents> &to_unpack);
+
+//! reports how large the packed form will be.
+template <class contents>
+int amorph_packed_size(const amorph<contents> &to_pack);
+
+// implementation for longer methods...
+
+//#define DEBUG_AMORPH
+ // uncomment to enable more testing, as well as complaints on errors.
+
+#undef static_class_name
+#define static_class_name() "amorph"
+ // used in bounds_halt macro.
+
+#undef AMO_ALERT
+#ifdef DEBUG_AMORPH
+ #include <basis/astring.h>
+ #define AMO_ALERT(a, b, c) basis::throw_error(basis::astring(a), basis::astring(func), basis::astring(b) + " -- " + c)
+ #define CHECK_FIELDS check_fields(func)
+#else
+ #define AMO_ALERT(a1, a2, a3) {}
+ #define CHECK_FIELDS { if (!func) {} }
+#endif
+
+//////////////
+
+template <class contents>
+amorph<contents>::amorph(int elements)
+: basis::array<contents *>(elements, NIL, basis::array<contents *>::SIMPLE_COPY
+ | basis::array<contents *>::EXPONE | basis::array<contents *>::FLUSH_INVISIBLE),
+ _fields_used(0)
+{
+ FUNCDEF("constructor");
+ set_nil(0, elements - 1);
+ CHECK_FIELDS;
+}
+
+template <class contents>
+amorph<contents>::~amorph()
+{
+ FUNCDEF("destructor");
+ CHECK_FIELDS;
+ clear_all();
+}
+
+template <class contents>
+void amorph<contents>::set_nil(int start, int end)
+{
+ for (int i = start; i <= end; i++)
+ basis::array<contents *>::put(i, (contents *)NIL);
+}
+
+template <class contents>
+void amorph<contents>::check_fields(const char *where) const
+{
+ FUNCDEF("check_fields");
+ int counter = 0;
+ for (int i = 0; i < elements(); i++)
+ if (basis::array<contents *>::get(i)) counter++;
+ if (_fields_used != counter) {
+ AMO_ALERT("amorph", basis::a_sprintf("check_fields for %s", where),
+ "error in _fields_used count");
+ }
+}
+
+template <class contents>
+void amorph<contents>::reset(int new_maximum)
+{
+ FUNCDEF("reset");
+ CHECK_FIELDS;
+ adjust(new_maximum);
+ clear_all();
+}
+
+template <class contents>
+void amorph<contents>::clear_all()
+{
+ FUNCDEF("clear_all");
+ CHECK_FIELDS;
+ for (int i = 0; i < elements(); i++) clear(i);
+}
+
+template <class contents>
+basis::outcome amorph<contents>::append(const contents *data)
+{
+ FUNCDEF("append");
+ CHECK_FIELDS;
+ adjust(elements() + 1);
+ return put(elements() - 1, (contents *)data);
+}
+
+template <class contents>
+const contents *amorph<contents>::get(int field) const
+{
+ FUNCDEF("get");
+ CHECK_FIELDS;
+ bounds_return(field, 0, elements() - 1, NIL);
+ return basis::array<contents *>::observe()[field];
+}
+
+template <class contents>
+void amorph<contents>::adjust(int new_maximum)
+{
+ FUNCDEF("adjust");
+ CHECK_FIELDS;
+ if (new_maximum < 0) return; // bad input here.
+ int old_max = elements();
+ if (new_maximum == old_max) return; // nothing to do.
+ if (new_maximum < old_max) {
+ // removes the elements beyond the new size of the amorph.
+ zap(new_maximum, old_max - 1);
+ // we're done tuning it.
+ return;
+ }
+
+ // we get to here if we need more space than we used to.
+ int new_fields = new_maximum - old_max;
+
+ basis::array<contents *>::insert(old_max, new_fields);
+ for (int i = old_max; i < new_maximum; i++) {
+ basis::array<contents *>::put(i, NIL);
+ }
+}
+
+template <class contents>
+basis::outcome amorph<contents>::insert(int position, int lines_to_add)
+{
+ FUNCDEF("insert");
+ CHECK_FIELDS;
+ bounds_return(position, 0, elements(), basis::common::OUT_OF_RANGE);
+ basis::outcome o = basis::array<contents *>::insert(position, lines_to_add);
+ if (o != basis::common::OKAY) return basis::common::OUT_OF_RANGE;
+ set_nil(position, position + lines_to_add - 1);
+ return basis::common::OKAY;
+}
+
+template <class contents>
+basis::outcome amorph<contents>::zap(int start_index, int end_index)
+{
+ FUNCDEF("zap");
+ CHECK_FIELDS;
+ bounds_return(start_index, 0, elements() - 1, basis::common::OUT_OF_RANGE);
+ bounds_return(end_index, 0, elements() - 1, basis::common::OUT_OF_RANGE);
+ if (end_index < start_index) return basis::common::OKAY;
+ for (int i = start_index; i <= end_index; i++) clear(i);
+ basis::outcome o = basis::array<contents *>::zap(start_index, end_index);
+ return (o == basis::common::OKAY? basis::common::OKAY : basis::common::OUT_OF_RANGE);
+}
+
+template <class contents>
+basis::outcome amorph<contents>::put(int field, const contents *data)
+{
+ FUNCDEF("put");
+ CHECK_FIELDS;
+ bounds_return(field, 0, elements() - 1, basis::common::OUT_OF_RANGE);
+ contents *to_whack = acquire(field);
+ WHACK(to_whack);
+ if (data) {
+ basis::array<contents *>::access()[field] = (contents *)data;
+ _fields_used++;
+ }
+ return basis::common::OKAY;
+}
+
+template <class contents>
+int amorph<contents>::find_empty(basis::outcome &o) const
+{
+ FUNCDEF("find_empty");
+ CHECK_FIELDS;
+ if (_fields_used == elements()) { o = basis::common::IS_FULL; return 0; }
+ for (int i = 0; i < elements(); i++)
+ if (!basis::array<contents *>::get(i)) { o = basis::common::OKAY; return i; }
+ AMO_ALERT("amorph", "empty", "_fields_used is incorrect");
+ return basis::common::IS_FULL;
+}
+
+template <class contents>
+const contents *amorph<contents>::next_valid(int &field) const
+{
+ FUNCDEF("next_valid");
+ CHECK_FIELDS;
+ bounds_return(field, 0, elements() - 1, NIL);
+ for (int i = field; i < elements(); i++)
+ if (basis::array<contents *>::get(i)) {
+ field = i;
+ return basis::array<contents *>::get(i);
+ }
+ return NIL;
+}
+
+template <class contents>
+basis::outcome amorph<contents>::clear(int field)
+{
+ FUNCDEF("clear");
+ CHECK_FIELDS;
+ return this->put(field, NIL);
+}
+
+template <class contents>
+contents *amorph<contents>::acquire(int field)
+{
+ FUNCDEF("acquire");
+ CHECK_FIELDS;
+ contents *to_return = borrow(field);
+ if (to_return) {
+ _fields_used--;
+ basis::array<contents *>::access()[field] = NIL;
+ }
+ return to_return;
+}
+
+template <class contents>
+int amorph<contents>::find(const contents *to_locate, basis::outcome &o)
+{
+ FUNCDEF("find");
+ CHECK_FIELDS;
+ if (!_fields_used) { o = basis::common::NOT_FOUND; return 0; }
+ for (int i = 0; i < elements(); i++) {
+ if (basis::array<contents *>::get(i) == to_locate) {
+ o = basis::common::OKAY;
+ return i;
+ }
+ }
+ o = basis::common::NOT_FOUND;
+ return 0;
+}
+
+template <class contents>
+contents *amorph<contents>::borrow(int field)
+{
+ FUNCDEF("borrow");
+ CHECK_FIELDS;
+ bounds_return(field, 0, elements() - 1, NIL);
+ return basis::array<contents *>::access()[field];
+}
+
+template <class contents>
+void amorph<contents>::swap_contents(amorph<contents> &other)
+{
+ FUNCDEF("swap_contents");
+ CHECK_FIELDS;
+ int hold_fields = _fields_used;
+ _fields_used = other._fields_used;
+ other._fields_used = hold_fields;
+ this->basis::array<contents *>::swap_contents(other);
+}
+
+template <class contents>
+int amorph_packed_size(const amorph<contents> &to_pack)
+{
+ int parts_size = 0;
+ for (int i = 0; i < to_pack.elements(); i++) {
+ const contents *current = to_pack.get(i);
+ if (current) parts_size += current->packed_size();
+ }
+ return PACKED_SIZE_INT32 + parts_size;
+}
+
+template <class contents>
+void amorph_pack(basis::byte_array &packed_form, const amorph<contents> &to_pack)
+{
+ structures::attach(packed_form, to_pack.elements());
+ for (int i = 0; i < to_pack.elements(); i++) {
+ const contents *current = to_pack.get(i);
+ if (current) current->pack(packed_form);
+ }
+}
+
+template <class contents>
+bool amorph_unpack(basis::byte_array &packed_form, amorph<contents> &to_unpack)
+{
+ to_unpack.reset();
+ int elem = 0;
+ if (!structures::detach(packed_form, elem)) return false;
+ for (int i = 0; i < elem; i++) {
+ contents *to_add = new contents;
+ if (!to_add->unpack(packed_form)) { delete to_add; return false; }
+ to_unpack.append(to_add);
+ }
+ return true;
+}
+
+template <class contents>
+void amorph_assign(amorph<contents> &to_assign,
+ const amorph<contents> &to_copy)
+{
+ if (&to_assign == &to_copy) return;
+ to_assign.clear_all();
+ if (to_assign.elements() != to_copy.elements()) {
+ to_assign.zap(0, to_assign.elements() - 1);
+ to_assign.insert(0, to_copy.elements());
+ }
+ for (int i = 0; i < to_assign.elements(); i++) {
+ if (to_copy.get(i)) to_assign.put(i, new contents(*to_copy.get(i)));
+ else to_assign.put(i, (contents *)NIL);
+ }
+}
+
+#undef static_class_name
+
+} //namespace.
+
+#endif
+