4 /*****************************************************************************\
7 * Author : Chris Koeritz *
9 *******************************************************************************
10 * Copyright (c) 1989-$now By Author. This program is free software; you can *
11 * redistribute it and/or modify it under the terms of the GNU General Public *
12 * License as published by the Free Software Foundation; either version 2 of *
13 * the License or (at your option) any later version. This is online at: *
14 * http://www.fsf.org/copyleft/gpl.html *
15 * Please send any updates to: fred@gruntose.com *
16 \*****************************************************************************/
18 #include "object_packers.h"
20 #include <basis/astring.h>
21 #include <basis/functions.h>
22 #include <basis/contracts.h>
23 #include <basis/guards.h>
25 //! A dynamic container class that holds any kind of object via pointers.
27 The object is considered owned by the amorph unless re-acquired from it,
28 and will be deleted when the amorph is destroyed.
30 An amorph has a specified number of fields at any one time, but the number
31 can be changed with "zap", "insert" and "adjust". Fields in the amorph
32 are either full or empty, where an empty field in the amorph has NIL as
33 its content. "put" adds a new field to the amorph. "get" retrieves the
34 contents of a field as a constant. "acquire" is used to check a field
35 out of the amorph, meaning that the amorph no longer possesses that field.
36 The legal range of indices in an amorph is from 0 through
37 "elements() - 1". In general, a range violation for an index is
38 treated as an invalid request and is ignored (although BAD_INDEX is
39 returned by functions with compatible return values).
43 The policy followed in amorph is that once an object is checked in with
44 "put" or "append", then the amorph owns that object. The object must not
45 be destroyed externally, because the amorph will automatically destroy
46 the object when either: 1) the amorph itself is destroyed, or 2) another
47 object is entered into the same field. If the stored object must be
48 destroyed externally, then it should be checked out of the amorph with
49 "acquire"; after that, the pointer may be deleted. "get" and "borrow"
50 return a pointer to the stored item while leaving it checked in to the
51 amorph. it is safe to modify what was "borrow"ed or to look at what one
52 "get"s, but do not destroy the pointers returned from either method.
55 namespace structures {
57 template <class contents>
58 class amorph : private basis::array<contents *>
61 amorph(int elements = 0);
62 //!< constructs an amorph capable of holding "elements" pointers.
66 int elements() const { return this->length(); }
67 //!< the maximum number of elements currently allowed in this amorph.
69 int valid_fields() const { return _fields_used; }
70 //!< Returns the number of fields that have non-NIL contents.
71 /*!< This might be different from the number of total elements. */
73 void adjust(int new_max);
74 //!< Changes the maximum number of elements for this amorph.
75 /*!< If the new number is smaller than the original, then the fields at
76 index "new_maximum" and upwards are thrown away. existing fields are
79 void reset(int new_maximum = 0);
80 //!< like adjust but doesn't keep existing contents.
82 basis::outcome put(int field, const contents *data);
83 //!< Enters an object into the field at index "field" in the amorph.
84 /*!< If "data" is NIL, then the field is cleared. The amorph considers the
85 pointer "data" to be its own property after put is invoked; "data"
86 should not be destructed since the amorph will automatically do so.
87 This restriction does not hold if the object is checked back out of
88 the amorph with acquire(). */
90 basis::outcome append(const contents *data);
91 //!< puts "data" on the end of this amorph.
92 /*!< adds an element to the end of the amorph by increasing the amorph size
93 (with "adjust") and putting the element into the new spot (with "put"). */
95 basis::outcome operator += (const contents *data) { return append(data); }
96 //!< a synonym for append.
98 //! Returns a constant pointer to the information at the index "field".
99 /*! If no information is stored or the field is out range, then NIL is
101 const contents *get(int field) const;
102 //! Returns a pointer to the information at the index "field".
103 /*! Also returns NIL for invalid indexes. DO NOT destroy the returned
104 pointer; it is still owned by the amorph. */
105 contents *borrow(int field);
108 const contents *operator [] (int field) const { return get(field); }
109 //! synonym for borrow.
110 contents *operator [] (int field) { return borrow(field); }
112 contents *acquire(int field);
113 //!< Retrieves a "field" from the amorph, taking responsibility for it back.
114 /*!< This function is similar to get, except that the contents are pulled
115 out of the amorph. The contents will no longer be destroyed when the
116 amorph is destroyed. To store the modified contents again, use put,
117 and then the amorph will take over management of the contents again.
118 Note that the index is not zapped with this function; the acquired
119 "field" will simply hold a null pointer. */
121 basis::outcome clear(int field);
122 //!< Clears the contents of the field specified.
123 /*!< Clearing an empty field has no effect. Clearing an invalid field has
124 no effect. NOTE: clearing the contents means that the contents are
125 destroyed, not just disconnected. */
128 //!< Clears every field in the amorph.
130 basis::outcome zap(int start, int end);
131 //!< Removes a range of indices from the amorph.
132 /*!< This does not just clear the field associated with the specified index
133 as "clear" does, it actually changes the number of total elements by
134 removing the indices from the amorph. The new amorph contains the old
135 elements up to just before the "start" and from the "end" + 1 through the
136 end of the amorph. AMORPH_BAD_INDEX is returned if either index is out of
137 range. If the zap succeeds, then AMORPH_OKAY is returned, even if the
138 "end" is less than the "start". */
140 basis::outcome insert(int position, int lines_to_add);
141 //!< Adds "lines_to_add" indices to the amorph at the index "position".
142 /*!< If "lines_to_add" is non-positive, the request is ignored. Inserting
143 at a position beyond the bounds of the array is ignored, but a position AT
144 elements() is allowed (it is an append...). */
146 int find_empty(basis::outcome &o) const;
147 //!< Returns the index of a free field if there are any.
148 /*!< The returned index is invalid if the "o" is IS_FULL. */
150 const contents *next_valid(int &field) const;
151 //!< Returns the contents of the next valid element at or after "field".
152 /*!< "field" is set to the location where an entry was found, if one was
153 actually found. If none exists at "field" or after it, then NIL is
156 int find(const contents *to_locate, basis::outcome &o);
157 //!< Searches the amorph for the contents specified.
158 /*!< Since only pointers to the contents are maintained, the search is
159 based on finding a pointer in the amorph that is identical to "to_locate".
160 if "o" is OKAY, then the index of the entry is returned. If
161 "o" is NOT_FOUND, then the contents are not present. */
163 void swap_contents(amorph<contents> &other);
164 //!< Exchanges the contents of "this" and "other".
165 /*!< No validation is performed but this should always succeed given
166 amorphs that are constructed properly. */
169 int _fields_used; //!< The number of fields currently full of info.
171 void check_fields(const char *where) const;
172 //!< Crashes out if the field count is wrong.
173 /*!< This is only used for the debugging version. */
175 void set_nil(int start, int end);
176 // Puts NIL in the indices between start and end.
177 /*!< Does not delete whatever used to reside there; it just sets the
180 // not to be used: amorphs should not be copied because it is intended that
181 // they support storing heavyweight objects that either have no copy
182 // constructors or have high-cost copy constructors.
183 amorph(const amorph &to_copy) {} //!< not to be used.
184 amorph &operator = (const amorph &to_copy) { return *this; }
190 // these extensions are phrased as macros to avoid including the headers
191 // necessary for compiling them. to use them, just put the macro name in
192 // the file where the template is needed.
196 //! This can be used when the templated object has a copy constructor.
197 /*! this makes the "to_assign" into a copy of the "to_copy" amorph. */
198 template <class contents>
199 void amorph_assign(amorph<contents> &to_assign,
200 const amorph<contents> &to_copy);
204 //! support for packing an amorph into an array of bytes.
206 this can be used when the underlying object is based on packable or
207 supports the same pack/unpack methods. note that if there are empty spots in
208 the amorph, they are not packed. when the packed form is unpacked again, it will
209 have only the first N elements set, where N is the number of non-empty items.
211 template <class contents>
212 void amorph_pack(basis::byte_array &packed_form, const amorph<contents> &to_pack);
214 //! unpacks the amorph from an array of bytes.
215 template <class contents>
216 bool amorph_unpack(basis::byte_array &packed_form, amorph<contents> &to_unpack);
218 //! reports how large the packed form will be.
219 template <class contents>
220 int amorph_packed_size(const amorph<contents> &to_pack);
222 // implementation for longer methods...
224 //#define DEBUG_AMORPH
225 // uncomment to enable more testing, as well as complaints on errors.
227 #undef static_class_name
228 #define static_class_name() "amorph"
229 // used in bounds_halt macro.
233 #include <basis/astring.h>
234 #define AMO_ALERT(a, b, c) basis::throw_error(basis::astring(a), basis::astring(func), basis::astring(b) + " -- " + c)
235 #define CHECK_FIELDS check_fields(func)
237 #define AMO_ALERT(a1, a2, a3) {}
238 #define CHECK_FIELDS { if (!func) {} }
243 template <class contents>
244 amorph<contents>::amorph(int elements)
245 : basis::array<contents *>(elements, NIL, basis::array<contents *>::SIMPLE_COPY
246 | basis::array<contents *>::EXPONE | basis::array<contents *>::FLUSH_INVISIBLE),
249 FUNCDEF("constructor");
250 set_nil(0, elements - 1);
254 template <class contents>
255 amorph<contents>::~amorph()
257 FUNCDEF("destructor");
262 template <class contents>
263 void amorph<contents>::set_nil(int start, int end)
265 for (int i = start; i <= end; i++)
266 basis::array<contents *>::put(i, (contents *)NIL);
269 template <class contents>
270 void amorph<contents>::check_fields(const char *where) const
272 FUNCDEF("check_fields");
274 for (int i = 0; i < elements(); i++)
275 if (basis::array<contents *>::get(i)) counter++;
276 if (_fields_used != counter) {
277 AMO_ALERT("amorph", basis::a_sprintf("check_fields for %s", where),
278 "error in _fields_used count");
282 template <class contents>
283 void amorph<contents>::reset(int new_maximum)
291 template <class contents>
292 void amorph<contents>::clear_all()
294 FUNCDEF("clear_all");
296 for (int i = 0; i < elements(); i++) clear(i);
299 template <class contents>
300 basis::outcome amorph<contents>::append(const contents *data)
304 adjust(elements() + 1);
305 return put(elements() - 1, (contents *)data);
308 template <class contents>
309 const contents *amorph<contents>::get(int field) const
313 bounds_return(field, 0, elements() - 1, NIL);
314 return basis::array<contents *>::observe()[field];
317 template <class contents>
318 void amorph<contents>::adjust(int new_maximum)
322 if (new_maximum < 0) return; // bad input here.
323 int old_max = elements();
324 if (new_maximum == old_max) return; // nothing to do.
325 if (new_maximum < old_max) {
326 // removes the elements beyond the new size of the amorph.
327 zap(new_maximum, old_max - 1);
328 // we're done tuning it.
332 // we get to here if we need more space than we used to.
333 int new_fields = new_maximum - old_max;
335 basis::array<contents *>::insert(old_max, new_fields);
336 for (int i = old_max; i < new_maximum; i++) {
337 basis::array<contents *>::put(i, NIL);
341 template <class contents>
342 basis::outcome amorph<contents>::insert(int position, int lines_to_add)
346 bounds_return(position, 0, elements(), basis::common::OUT_OF_RANGE);
347 basis::outcome o = basis::array<contents *>::insert(position, lines_to_add);
348 if (o != basis::common::OKAY) return basis::common::OUT_OF_RANGE;
349 set_nil(position, position + lines_to_add - 1);
350 return basis::common::OKAY;
353 template <class contents>
354 basis::outcome amorph<contents>::zap(int start_index, int end_index)
358 bounds_return(start_index, 0, elements() - 1, basis::common::OUT_OF_RANGE);
359 bounds_return(end_index, 0, elements() - 1, basis::common::OUT_OF_RANGE);
360 if (end_index < start_index) return basis::common::OKAY;
361 for (int i = start_index; i <= end_index; i++) clear(i);
362 basis::outcome o = basis::array<contents *>::zap(start_index, end_index);
363 return (o == basis::common::OKAY? basis::common::OKAY : basis::common::OUT_OF_RANGE);
366 template <class contents>
367 basis::outcome amorph<contents>::put(int field, const contents *data)
371 bounds_return(field, 0, elements() - 1, basis::common::OUT_OF_RANGE);
372 contents *to_whack = acquire(field);
375 basis::array<contents *>::access()[field] = (contents *)data;
378 return basis::common::OKAY;
381 template <class contents>
382 int amorph<contents>::find_empty(basis::outcome &o) const
384 FUNCDEF("find_empty");
386 if (_fields_used == elements()) { o = basis::common::IS_FULL; return 0; }
387 for (int i = 0; i < elements(); i++)
388 if (!basis::array<contents *>::get(i)) { o = basis::common::OKAY; return i; }
389 AMO_ALERT("amorph", "empty", "_fields_used is incorrect");
390 return basis::common::IS_FULL;
393 template <class contents>
394 const contents *amorph<contents>::next_valid(int &field) const
396 FUNCDEF("next_valid");
398 bounds_return(field, 0, elements() - 1, NIL);
399 for (int i = field; i < elements(); i++)
400 if (basis::array<contents *>::get(i)) {
402 return basis::array<contents *>::get(i);
407 template <class contents>
408 basis::outcome amorph<contents>::clear(int field)
412 return this->put(field, NIL);
415 template <class contents>
416 contents *amorph<contents>::acquire(int field)
420 contents *to_return = borrow(field);
423 basis::array<contents *>::access()[field] = NIL;
428 template <class contents>
429 int amorph<contents>::find(const contents *to_locate, basis::outcome &o)
433 if (!_fields_used) { o = basis::common::NOT_FOUND; return 0; }
434 for (int i = 0; i < elements(); i++) {
435 if (basis::array<contents *>::get(i) == to_locate) {
436 o = basis::common::OKAY;
440 o = basis::common::NOT_FOUND;
444 template <class contents>
445 contents *amorph<contents>::borrow(int field)
449 bounds_return(field, 0, elements() - 1, NIL);
450 return basis::array<contents *>::access()[field];
453 template <class contents>
454 void amorph<contents>::swap_contents(amorph<contents> &other)
456 FUNCDEF("swap_contents");
458 int hold_fields = _fields_used;
459 _fields_used = other._fields_used;
460 other._fields_used = hold_fields;
461 this->basis::array<contents *>::swap_contents(other);
464 template <class contents>
465 int amorph_packed_size(const amorph<contents> &to_pack)
468 for (int i = 0; i < to_pack.elements(); i++) {
469 const contents *current = to_pack.get(i);
470 if (current) parts_size += current->packed_size();
472 return PACKED_SIZE_INT32 + parts_size;
475 template <class contents>
476 void amorph_pack(basis::byte_array &packed_form, const amorph<contents> &to_pack)
478 structures::attach(packed_form, to_pack.elements());
479 for (int i = 0; i < to_pack.elements(); i++) {
480 const contents *current = to_pack.get(i);
481 if (current) current->pack(packed_form);
485 template <class contents>
486 bool amorph_unpack(basis::byte_array &packed_form, amorph<contents> &to_unpack)
490 if (!structures::detach(packed_form, elem)) return false;
491 for (int i = 0; i < elem; i++) {
492 contents *to_add = new contents;
493 if (!to_add->unpack(packed_form)) { delete to_add; return false; }
494 to_unpack.append(to_add);
499 template <class contents>
500 void amorph_assign(amorph<contents> &to_assign,
501 const amorph<contents> &to_copy)
503 if (&to_assign == &to_copy) return;
504 to_assign.clear_all();
505 if (to_assign.elements() != to_copy.elements()) {
506 to_assign.zap(0, to_assign.elements() - 1);
507 to_assign.insert(0, to_copy.elements());
509 for (int i = 0; i < to_assign.elements(); i++) {
510 if (to_copy.get(i)) to_assign.put(i, new contents(*to_copy.get(i)));
511 else to_assign.put(i, (contents *)NIL);
515 #undef static_class_name