comparison src/array.c @ 5168:cf900a2f1fa3

extract gap array from extents.c, use in range tables -------------------- ChangeLog entries follow: -------------------- src/ChangeLog addition: 2010-03-22 Ben Wing <ben@xemacs.org> * Makefile.in.in (objs): * array.c: * array.c (gap_array_adjust_markers): * array.c (gap_array_move_gap): * array.c (gap_array_make_gap): * array.c (gap_array_insert_els): * array.c (gap_array_delete_els): * array.c (gap_array_make_marker): * array.c (gap_array_delete_marker): * array.c (gap_array_delete_all_markers): * array.c (gap_array_clone): * array.h: * depend: * emacs.c (main_1): * extents.c: * extents.c (EXTENT_GAP_ARRAY_AT): * extents.c (extent_list_num_els): * extents.c (extent_list_locate): * extents.c (extent_list_at): * extents.c (extent_list_delete_all): * extents.c (allocate_extent_list): * extents.c (syms_of_extents): * extents.h: * extents.h (XEXTENT_LIST_MARKER): * lisp.h: * rangetab.c: * rangetab.c (mark_range_table): * rangetab.c (print_range_table): * rangetab.c (range_table_equal): * rangetab.c (range_table_hash): * rangetab.c (verify_range_table): * rangetab.c (get_range_table_pos): * rangetab.c (Fmake_range_table): * rangetab.c (Fcopy_range_table): * rangetab.c (Fget_range_table): * rangetab.c (put_range_table): * rangetab.c (Fclear_range_table): * rangetab.c (Fmap_range_table): * rangetab.c (unified_range_table_bytes_needed): * rangetab.c (unified_range_table_copy_data): * rangetab.c (unified_range_table_lookup): * rangetab.h: * rangetab.h (struct range_table_entry): * rangetab.h (struct Lisp_Range_Table): * rangetab.h (rangetab_gap_array_at): * symsinit.h: Rename dynarr.c to array.c. Move gap array from extents.c to array.c. Extract dynarr, gap array and stack-like malloc into new file array.h. Rename GAP_ARRAY_NUM_ELS -> gap_array_length(). Add gap_array_at(), gap_array_atp(). Rewrite range table code to use gap arrays. Make put_range_table() smarter so that its operation is O(log n) for adding a localized range. * gc.c (lispdesc_block_size_1): Don't ABORT() when two elements are located at the same place. This will happen with a size-0 gap array -- both parts of the array (before and after gap) are in the same place.
author Ben Wing <ben@xemacs.org>
date Mon, 22 Mar 2010 19:12:15 -0500
parents src/dynarr.c@1fae11d56ad2
children 6c6d78781d59
comparison
equal deleted inserted replaced
5167:e374ea766cc1 5168:cf900a2f1fa3
1 /* Support for dynarrs and other types of dynamic arrays.
2 Copyright (c) 1994, 1995 Free Software Foundation, Inc.
3 Copyright (c) 1993, 1995 Sun Microsystems, Inc.
4 Copyright (c) 1995, 1996, 2000, 2002, 2003, 2004, 2005, 2010 Ben Wing.
5
6 This file is part of XEmacs.
7
8 XEmacs is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 2, or (at your option) any
11 later version.
12
13 XEmacs is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with XEmacs; see the file COPYING. If not, write to
20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA. */
22
23 /* Synched up with: Not in FSF. */
24
25 /* Written by Ben Wing, December 1993. */
26
27 #include <config.h>
28 #include "lisp.h"
29
30 #include "insdel.h"
31
32
33 /*****************************************************************************/
34 /* "dynarr" a.k.a. dynamic array */
35 /*****************************************************************************/
36
37 /*
38 A "dynamic array" or "dynarr" is a contiguous array of fixed-size elements
39 where there is no upper limit (except available memory) on the number of
40 elements in the array. Because the elements are maintained contiguously,
41 space is used efficiently (no per-element pointers necessary) and random
42 access to a particular element is in constant time. At any one point, the
43 block of memory that holds the array has an upper limit; if this limit is
44 exceeded, the memory is realloc()ed into a new array that is twice as big.
45 Assuming that the time to grow the array is on the order of the new size of
46 the array block, this scheme has a provably constant amortized time
47 \(i.e. average time over all additions).
48
49 When you add elements or retrieve elements, pointers are used. Note that
50 the element itself (of whatever size it is), and not the pointer to it,
51 is stored in the array; thus you do not have to allocate any heap memory
52 on your own. Also, returned pointers are only guaranteed to be valid
53 until the next operation that changes the length of the array.
54
55 This is a container object. Declare a dynamic array of a specific type
56 as follows:
57
58 typedef struct
59 {
60 Dynarr_declare (mytype);
61 } mytype_dynarr;
62
63 Use the following functions/macros:
64
65
66 ************* Dynarr creation *************
67
68 void *Dynarr_new(type)
69 [MACRO] Create a new dynamic-array object, with each element of the
70 specified type. The return value is cast to (type##_dynarr).
71 This requires following the convention that types are declared in
72 such a way that this type concatenation works. In particular, TYPE
73 must be a symbol, not an arbitrary C type. To make dynarrs of
74 complex types, a typedef must be declared, e.g.
75
76 typedef unsigned char *unsigned_char_ptr;
77
78 and then you can say
79
80 unsigned_char_ptr_dynarr *dyn = Dynarr_new (unsigned_char_ptr);
81
82 void *Dynarr_new2(dynarr_type, type)
83 [MACRO] Create a new dynamic-array object, with each element of the
84 specified type. The array itself is of type DYNARR_TYPE. This makes
85 it possible to create dynarrs over complex types without the need
86 to create typedefs, as described above. Use is as follows:
87
88 ucharptr_dynarr *dyn = Dynarr_new2 (ucharptr_dynarr *, unsigned char *);
89
90 Dynarr_free(d)
91 Destroy a dynamic array and the memory allocated to it.
92
93 ************* Dynarr access *************
94
95 type Dynarr_at(d, i)
96 [MACRO] Return the element at the specified index. The index must be
97 between 0 and Dynarr_largest(d), inclusive. With error-checking
98 enabled, bounds checking on the index is in the form of asserts() --
99 an out-of-bounds index causes an abort. The element itself is
100 returned, not a pointer to it.
101
102 type *Dynarr_atp(d, i)
103 [MACRO] Return a pointer to the element at the specified index.
104 Restrictions and bounds checking on the index is as for Dynarr_at.
105 The pointer may not be valid after an element is added to or
106 (conceivably) removed from the array, because this may trigger a
107 realloc() performed on the underlying dynarr storage, which may
108 involve moving the entire underlying storage to a new location in
109 memory.
110
111 type *Dynarr_begin(d)
112 [MACRO] Return a pointer to the first element in the dynarr. See
113 Dynarr_atp() for warnings about when the pointer might become invalid.
114
115 type *Dynarr_lastp(d)
116 [MACRO] Return a pointer to the last element in the dynarr. See
117 Dynarr_atp() for warnings about when the pointer might become invalid.
118
119 type *Dynarr_past_lastp(d)
120 [MACRO] Return a pointer to the beginning of the element just past the
121 last one. WARNING: This may not point to valid memory; however, the
122 byte directly before will be pointer will be valid memory. This macro
123 might be useful for various reasons, e.g. as a stopping point in a loop
124 (although Dynarr_lastp() could be used just as well) or as a place to
125 start writing elements if Dynarr_length() < Dynarr_largest().
126
127 ************* Dynarr length/size retrieval and setting *************
128
129 int Dynarr_length(d)
130 [MACRO] Return the number of elements currently in a dynamic array.
131
132 int Dynarr_largest(d)
133 [MACRO] Return the maximum value that Dynarr_length(d) would
134 ever have returned. This is used esp. in the redisplay code,
135 which reuses dynarrs for performance reasons.
136
137 int Dynarr_max(d)
138 [MACRO] Return the maximum number of elements that can fit in the
139 dynarr before it needs to be resized.
140
141 Note that Dynarr_length(d) <= Dynarr_largest(d) <= Dynarr_max(d).
142
143 Bytecount Dynarr_sizeof(d)
144 [MACRO] Return the total size of the elements currently in dynarr
145 D. This
146
147 Dynarr_set_lengthr(d, len)
148 [MACRO] Set the length of D to LEN, which must be between 0 and
149 Dynarr_largest(d), inclusive. With error-checking enabled, an
150 assertion failure will result from trying to set the length
151 to less than zero or greater than Dynarr_largest(d). The
152 restriction to Dynarr_largest() is to ensure that
153
154 Dynarr_set_length(d, len)
155 [MACRO] Set the length of D to LEN, resizing the dynarr as
156 necessary to make sure enough space is available. there are no
157 restrictions on LEN other than available memory and that it must
158 be at least 0. Note that
159
160 Dynarr_set_length_and_zero(d, len)
161 [MACRO] Like Dynarr_set_length(d, len) but also, if increasing
162 the length, zero out the memory between the old and new lengths,
163 i.e. starting just past the previous last element and up through
164 the new last element.
165
166 Dynarr_incrementr(d)
167 [MACRO] Increments the length of D by 1. Equivalent to
168 Dynarr_set_lengthr(d, Dynarr_length(d) + 1).
169
170 Dynarr_increment(d)
171 [MACRO] Increments the length of D by 1. Equivalent to
172 Dynarr_set_length(d, Dynarr_length(d) + 1).
173
174 Dynarr_reset(d)
175 [MACRO] Reset the length of a dynamic array to 0.
176
177 Dynarr_resize(d, maxval)
178 Resize the internal dynarr storage to so that it can hold at least
179 MAXVAL elements. Resizing is done using a geometric series
180 (repeatedly multiply the old maximum by a constant, normally 1.5,
181 till a large enough size is reached), so this will be efficient
182 even if resizing larger by one element at a time. This is mostly
183 an internal function.
184
185
186
187 ************* Adding/deleting elements to/from a dynarr *************
188
189 Dynarr_add(d, el)
190 [MACRO] Add an element to the end of a dynamic array. EL is a pointer
191 to the element; the element itself is stored in the array, however.
192 No function call is performed unless the array needs to be resized.
193
194 Dynarr_add_many(d, base, len)
195 [MACRO] Add LEN elements to the end of the dynamic array. The elements
196 should be contiguous in memory, starting at BASE. If BASE if NULL,
197 just make space for the elements; don't actually add them.
198
199 Dynarr_prepend_many(d, base, len)
200 [MACRO] Prepend LEN elements to the beginning of the dynamic array.
201 The elements should be contiguous in memory, starting at BASE.
202 If BASE if NULL, just make space for the elements; don't actually
203 add them.
204
205 Dynarr_insert_many(d, base, len, pos)
206 Insert LEN elements to the dynamic array starting at position
207 POS. The elements should be contiguous in memory, starting at BASE.
208 If BASE if NULL, just make space for the elements; don't actually
209 add them.
210
211 type Dynarr_pop(d)
212 [MACRO] Pop the last element off the dynarr and return it.
213
214 Dynarr_delete(d, i)
215 [MACRO] Delete an element from the dynamic array at position I.
216
217 Dynarr_delete_many(d, pos, len)
218 Delete LEN elements from the dynamic array starting at position
219 POS.
220
221 Dynarr_zero_many(d, pos, len)
222 Zero out LEN elements in the dynarr D starting at position POS.
223
224 Dynarr_delete_by_pointer(d, p)
225 [MACRO] Delete an element from the dynamic array at pointer P,
226 which must point within the block of memory that stores the data.
227 P should be obtained using Dynarr_atp().
228
229 ************* Dynarr locking *************
230
231 Dynarr_lock(d)
232 Lock the dynarr against further locking or writing. With error-checking
233 enabled, any attempts to write into a locked dynarr or re-lock an
234 already locked one will cause an assertion failure and abort.
235
236 Dynarr_unlock(d)
237 Unlock a locked dynarr, allowing writing into it.
238
239 ************* Dynarr global variables *************
240
241 Dynarr_min_size
242 Minimum allowable size for a dynamic array when it is resized.
243
244 */
245
246 static const struct memory_description const_Ascbyte_ptr_description_1[] = {
247 { XD_ASCII_STRING, 0 },
248 { XD_END }
249 };
250
251 const struct sized_memory_description const_Ascbyte_ptr_description = {
252 sizeof (const Ascbyte *),
253 const_Ascbyte_ptr_description_1
254 };
255
256 static const struct memory_description const_Ascbyte_ptr_dynarr_description_1[] = {
257 XD_DYNARR_DESC (const_Ascbyte_ptr_dynarr, &const_Ascbyte_ptr_description),
258 { XD_END }
259 };
260
261 const struct sized_memory_description const_Ascbyte_ptr_dynarr_description = {
262 sizeof (const_Ascbyte_ptr_dynarr),
263 const_Ascbyte_ptr_dynarr_description_1
264 };
265
266
267 static Elemcount Dynarr_min_size = 8;
268
269 static void
270 Dynarr_realloc (Dynarr *dy, Elemcount new_size)
271 {
272 if (DUMPEDP (dy->base))
273 {
274 void *new_base = malloc (new_size * Dynarr_elsize (dy));
275 memcpy (new_base, dy->base,
276 (Dynarr_max (dy) < new_size ? Dynarr_max (dy) : new_size) *
277 Dynarr_elsize (dy));
278 dy->base = new_base;
279 }
280 else
281 dy->base = xrealloc (dy->base, new_size * Dynarr_elsize (dy));
282 }
283
284 void *
285 Dynarr_newf (Bytecount elsize)
286 {
287 Dynarr *d = xnew_and_zero (Dynarr);
288 d->elsize_ = elsize;
289
290 return d;
291 }
292
293 #ifdef NEW_GC
294 DEFINE_DUMPABLE_INTERNAL_LISP_OBJECT ("dynarr", dynarr,
295 0, 0,
296 Dynarr);
297
298 static void
299 Dynarr_lisp_realloc (Dynarr *dy, Elemcount new_size)
300 {
301 void *new_base =
302 XPNTR (alloc_sized_lrecord_array (Dynarr_elsize (dy), new_size,
303 dy->lisp_imp));
304 if (dy->base)
305 memcpy (new_base, dy->base,
306 (Dynarr_max (dy) < new_size ? Dynarr_max (dy) : new_size) *
307 Dynarr_elsize (dy));
308 dy->base = new_base;
309 }
310
311 void *
312 Dynarr_lisp_newf (Bytecount elsize,
313 const struct lrecord_implementation *dynarr_imp,
314 const struct lrecord_implementation *imp)
315 {
316 Dynarr *d = (Dynarr *) XPNTR (alloc_sized_lrecord (sizeof (Dynarr),
317 dynarr_imp));
318 d->elsize_ = elsize;
319 d->lisp_imp = imp;
320
321 return d;
322 }
323 #endif /* not NEW_GC */
324
325 void
326 Dynarr_resize (void *d, Elemcount size)
327 {
328 Elemcount newsize;
329 double multiplier;
330 Dynarr *dy = (Dynarr *) Dynarr_verify (d);
331
332 if (Dynarr_max (dy) <= 8)
333 multiplier = 2;
334 else
335 multiplier = 1.5;
336
337 for (newsize = Dynarr_max (dy); newsize < size;)
338 newsize = max (Dynarr_min_size, (Elemcount) (multiplier * newsize));
339
340 /* Don't do anything if the array is already big enough. */
341 if (newsize > Dynarr_max (dy))
342 {
343 #ifdef NEW_GC
344 if (dy->lisp_imp)
345 Dynarr_lisp_realloc (dy, newsize);
346 else
347 Dynarr_realloc (dy, newsize);
348 #else /* not NEW_GC */
349 Dynarr_realloc (dy, newsize);
350 #endif /* not NEW_GC */
351 dy->max_ = newsize;
352 }
353 }
354
355 /* Add a number of contiguous elements to the array starting at POS. */
356
357 void
358 Dynarr_insert_many (void *d, const void *base, Elemcount len, Elemcount pos)
359 {
360 Dynarr *dy = Dynarr_verify_mod (d);
361 Elemcount old_len = Dynarr_length (dy);
362
363 /* #### This could conceivably be wrong, if code wants to access stuff
364 between len and largest. */
365 dynarr_checking_assert (pos >= 0 && pos <= old_len);
366 dynarr_checking_assert (len >= 0);
367 Dynarr_increase_length (dy, old_len + len);
368
369 if (pos != old_len)
370 {
371 memmove ((Rawbyte *) dy->base + (pos + len)*Dynarr_elsize (dy),
372 (Rawbyte *) dy->base + pos*Dynarr_elsize (dy),
373 (old_len - pos)*Dynarr_elsize (dy));
374 }
375 /* Some functions call us with a value of 0 to mean "reserve space but
376 don't write into it" */
377 if (base)
378 memcpy ((Rawbyte *) dy->base + pos*Dynarr_elsize (dy), base,
379 len*Dynarr_elsize (dy));
380 }
381
382 void
383 Dynarr_delete_many (void *d, Elemcount pos, Elemcount len)
384 {
385 Dynarr *dy = Dynarr_verify_mod (d);
386
387 dynarr_checking_assert (pos >= 0 && len >= 0 &&
388 pos + len <= Dynarr_length (dy));
389
390 memmove ((Rawbyte *) dy->base + pos*Dynarr_elsize (dy),
391 (Rawbyte *) dy->base + (pos + len)*Dynarr_elsize (dy),
392 (Dynarr_length (dy) - pos - len)*Dynarr_elsize (dy));
393
394 Dynarr_set_length_1 (dy, Dynarr_length (dy) - len);
395 }
396
397 void
398 Dynarr_free (void *d)
399 {
400 Dynarr *dy = (Dynarr *) d;
401
402 #ifdef NEW_GC
403 if (dy->base && !DUMPEDP (dy->base))
404 {
405 if (!dy->lisp_imp)
406 xfree (dy->base);
407 }
408 if(!DUMPEDP (dy))
409 {
410 if (!dy->lisp_imp)
411 xfree (dy);
412 }
413 #else /* not NEW_GC */
414 if (dy->base && !DUMPEDP (dy->base))
415 xfree (dy->base);
416 if(!DUMPEDP (dy))
417 xfree (dy);
418 #endif /* not NEW_GC */
419 }
420
421 #ifdef MEMORY_USAGE_STATS
422
423 /* Return memory usage for dynarr D. The returned value is the total
424 amount of bytes actually being used for the dynarr, including all
425 overhead. The extra amount of space in the dynarr that is
426 allocated beyond what was requested is returned in DYNARR_OVERHEAD
427 in STATS. The extra amount of space that malloc() allocates beyond
428 what was requested of it is returned in MALLOC_OVERHEAD in STATS.
429 See the comment above the definition of this structure. */
430
431 Bytecount
432 Dynarr_memory_usage (void *d, struct usage_stats *stats)
433 {
434 Bytecount total = 0;
435 Dynarr *dy = (Dynarr *) d;
436
437 /* We have to be a bit tricky here because not all of the
438 memory that malloc() will claim as "requested" was actually
439 requested. */
440
441 if (dy->base)
442 {
443 Bytecount malloc_used =
444 malloced_storage_size (dy->base, Dynarr_elsize (dy) * Dynarr_max (dy),
445 0);
446 /* #### This may or may not be correct. Some dynarrs would
447 prefer that we use dy->len instead of dy->largest here. */
448 Bytecount was_requested = Dynarr_elsize (dy) * Dynarr_largest (dy);
449 Bytecount dynarr_overhead =
450 Dynarr_elsize (dy) * (Dynarr_max (dy) - Dynarr_largest (dy));
451
452 total += malloc_used;
453 stats->was_requested += was_requested;
454 stats->dynarr_overhead += dynarr_overhead;
455 /* And the remainder must be malloc overhead. */
456 stats->malloc_overhead +=
457 malloc_used - was_requested - dynarr_overhead;
458 }
459
460 total += malloced_storage_size (d, sizeof (*dy), stats);
461
462 return total;
463 }
464
465 #endif /* MEMORY_USAGE_STATS */
466
467
468 /*****************************************************************************/
469 /* stack-like allocation */
470 /*****************************************************************************/
471
472 /* Version of malloc() that will be extremely efficient when allocation
473 nearly always occurs in LIFO (stack) order.
474
475 #### Perhaps shouldn't be in this file, but where else? */
476
477 typedef struct
478 {
479 Dynarr_declare (char_dynarr *);
480 } char_dynarr_dynarr;
481
482 char_dynarr_dynarr *stack_like_free_list;
483 char_dynarr_dynarr *stack_like_in_use_list;
484
485 void *
486 stack_like_malloc (Bytecount size)
487 {
488 char_dynarr *this_one;
489 if (!stack_like_free_list)
490 {
491 stack_like_free_list = Dynarr_new2 (char_dynarr_dynarr,
492 char_dynarr *);
493 stack_like_in_use_list = Dynarr_new2 (char_dynarr_dynarr,
494 char_dynarr *);
495 }
496
497 if (Dynarr_length (stack_like_free_list) > 0)
498 this_one = Dynarr_pop (stack_like_free_list);
499 else
500 this_one = Dynarr_new (char);
501 Dynarr_add (stack_like_in_use_list, this_one);
502 Dynarr_reset (this_one);
503 Dynarr_add_many (this_one, 0, size);
504 return Dynarr_begin (this_one);
505 }
506
507 void
508 stack_like_free (void *val)
509 {
510 Elemcount len = Dynarr_length (stack_like_in_use_list);
511 assert (len > 0);
512 /* The vast majority of times, we will be called in a last-in first-out
513 order, and the item at the end of the list will be the one we're
514 looking for, so just check for this first and avoid any function
515 calls. */
516 if (Dynarr_begin (Dynarr_at (stack_like_in_use_list, len - 1)) == val)
517 {
518 char_dynarr *this_one = Dynarr_pop (stack_like_in_use_list);
519 Dynarr_add (stack_like_free_list, this_one);
520 }
521 else
522 {
523 /* Find the item and delete it. */
524 int i;
525 assert (len >= 2);
526 for (i = len - 2; i >= 0; i--)
527 if (Dynarr_begin (Dynarr_at (stack_like_in_use_list, i)) ==
528 val)
529 {
530 char_dynarr *this_one = Dynarr_at (stack_like_in_use_list, i);
531 Dynarr_add (stack_like_free_list, this_one);
532 Dynarr_delete (stack_like_in_use_list, i);
533 return;
534 }
535
536 ABORT ();
537 }
538 }
539
540
541 /*****************************************************************************/
542 /* Generalized gap array */
543 /*****************************************************************************/
544
545 /* A "gap array" is an array that has a "gap" somewhere in the middle of it,
546 so that insertions and deletions near the gap -- or in general, highly
547 localized insertions and deletions -- are very fast. Inserting or
548 deleting works by first moving the gap to the insertion or deletion
549 position and then shortening or lengthening the gap as necessary. The
550 idea comes from the gap used in storing text in a buffer.
551
552 The gap array interface differs in a number of ways from dynarrs (####
553 and should be changed so that it works the same as dynarrs):
554
555 (1) There aren't separate type-specific gap array types. As a result,
556 operations like gap_array_at() require that the type be specified as
557 one of the arguments. It is often more convenient to use a macro
558 wrapper around this operation.
559
560 (2) The gap array type is itself a stretchy array rather than using a
561 separate block of memory to store the array. This means that certain
562 operations (especially insertions) may relocate the the gap array,
563 and as a result return a pointer to the (possibly) moved gap array,
564 which must be stored back into the location where the gap array
565 pointer resides. This also means that the caller must worry about
566 cloning the gap array in the case where it has been dumped, or you
567 will get an ABORT() inside of xrealloc().
568
569 (3) Fewer operations are available than for dynarrs, and may have
570 different names and/or different calling conventions.
571
572 (4) The mechanism for creating "Lisp-object gap arrays" isn't completely
573 developed. Currently it's only possible to create a gap-array Lisp
574 object that wraps Lisp_Object pointers (not Lisp object structures
575 directly), and only under NEW_GC.
576
577 (5) Gap arrays have a concept of a "gap array marker" that properly
578 tracks insertions and deletions; no such thing exists in dynarrs.
579 It exists in gap arrays because it's necessary for their use in
580 implementing extent lists.
581 */
582
583 extern const struct sized_memory_description gap_array_marker_description;
584
585 static const struct memory_description gap_array_marker_description_1[] = {
586 #ifdef NEW_GC
587 { XD_LISP_OBJECT, offsetof (Gap_Array_Marker, next) },
588 #else /* not NEW_GC */
589 { XD_BLOCK_PTR, offsetof (Gap_Array_Marker, next), 1,
590 { &gap_array_marker_description } },
591 #endif /* not NEW_GC */
592 { XD_END }
593 };
594
595 #ifdef NEW_GC
596 DEFINE_NODUMP_INTERNAL_LISP_OBJECT ("gap-array-marker", gap_array_marker,
597 0, gap_array_marker_description_1,
598 struct gap_array_marker);
599 #else /* not NEW_GC */
600 const struct sized_memory_description gap_array_marker_description = {
601 sizeof (Gap_Array_Marker),
602 gap_array_marker_description_1
603 };
604 #endif /* not NEW_GC */
605
606 static const struct memory_description lispobj_gap_array_description_1[] = {
607 XD_GAP_ARRAY_DESC (&lisp_object_description),
608 { XD_END }
609 };
610
611 #ifdef NEW_GC
612
613 static Bytecount
614 size_gap_array (Lisp_Object obj)
615 {
616 Gap_Array *ga = XGAP_ARRAY (obj);
617 return gap_array_byte_size (ga);
618 }
619
620 DEFINE_DUMPABLE_SIZABLE_INTERNAL_LISP_OBJECT ("gap-array", gap_array,
621 0,
622 lispobj_gap_array_description_1,
623 size_gap_array,
624 struct gap_array);
625 #else /* not NEW_GC */
626 const struct sized_memory_description lispobj_gap_array_description = {
627 0, lispobj_gap_array_description_1
628 };
629 #endif /* (not) NEW_GC */
630
631 #ifndef NEW_GC
632 static Gap_Array_Marker *gap_array_marker_freelist;
633 #endif /* not NEW_GC */
634
635 /* This generalizes the "array with a gap" model used to store buffer
636 characters. This is based on the stuff in insdel.c and should
637 probably be merged with it. This is not extent-specific and should
638 perhaps be moved into a separate file. */
639
640 /* ------------------------------- */
641 /* internal functions */
642 /* ------------------------------- */
643
644 /* Adjust the gap array markers in the range (FROM, TO]. Parallel to
645 adjust_markers() in insdel.c. */
646
647 static void
648 gap_array_adjust_markers (Gap_Array *ga, Memxpos from,
649 Memxpos to, Elemcount amount)
650 {
651 Gap_Array_Marker *m;
652
653 for (m = ga->markers; m; m = m->next)
654 m->pos = do_marker_adjustment (m->pos, from, to, amount);
655 }
656
657 static void
658 gap_array_recompute_derived_values (Gap_Array *ga)
659 {
660 ga->offset_past_gap = ga->elsize * (ga->gap + ga->gapsize);
661 ga->els_past_gap = ga->numels - ga->gap;
662 }
663
664 /* Move the gap to array position POS. Parallel to move_gap() in
665 insdel.c but somewhat simplified. */
666
667 static void
668 gap_array_move_gap (Gap_Array *ga, Elemcount pos)
669 {
670 Elemcount gap = ga->gap;
671 Elemcount gapsize = ga->gapsize;
672
673 if (pos < gap)
674 {
675 memmove (GAP_ARRAY_MEMEL_ADDR (ga, pos + gapsize),
676 GAP_ARRAY_MEMEL_ADDR (ga, pos),
677 (gap - pos)*ga->elsize);
678 gap_array_adjust_markers (ga, (Memxpos) pos, (Memxpos) gap,
679 gapsize);
680 }
681 else if (pos > gap)
682 {
683 memmove (GAP_ARRAY_MEMEL_ADDR (ga, gap),
684 GAP_ARRAY_MEMEL_ADDR (ga, gap + gapsize),
685 (pos - gap)*ga->elsize);
686 gap_array_adjust_markers (ga, (Memxpos) (gap + gapsize),
687 (Memxpos) (pos + gapsize), - gapsize);
688 }
689 ga->gap = pos;
690
691 gap_array_recompute_derived_values (ga);
692 }
693
694 /* Make the gap INCREMENT characters longer. Parallel to make_gap() in
695 insdel.c. The gap array may be moved, so assign the return value back
696 to the array pointer. */
697
698 static Gap_Array *
699 gap_array_make_gap (Gap_Array *ga, Elemcount increment)
700 {
701 Elemcount real_gap_loc;
702 Elemcount old_gap_size;
703
704 /* If we have to get more space, get enough to last a while. We use
705 a geometric progression that saves on realloc space. */
706 increment += 100 + ga->numels / 8;
707
708 #ifdef NEW_GC
709 if (ga->is_lisp)
710 ga = (Gap_Array *) mc_realloc (ga,
711 offsetof (Gap_Array, array) +
712 (ga->numels + ga->gapsize + increment) *
713 ga->elsize);
714 else
715 #endif /* not NEW_GC */
716 ga = (Gap_Array *) xrealloc (ga,
717 offsetof (Gap_Array, array) +
718 (ga->numels + ga->gapsize + increment) *
719 ga->elsize);
720 if (ga == 0)
721 memory_full ();
722
723 real_gap_loc = ga->gap;
724 old_gap_size = ga->gapsize;
725
726 /* Call the newly allocated space a gap at the end of the whole space. */
727 ga->gap = ga->numels + ga->gapsize;
728 ga->gapsize = increment;
729
730 /* Move the new gap down to be consecutive with the end of the old one.
731 This adjusts the markers properly too. */
732 gap_array_move_gap (ga, real_gap_loc + old_gap_size);
733
734 /* Now combine the two into one large gap. */
735 ga->gapsize += old_gap_size;
736 ga->gap = real_gap_loc;
737
738 gap_array_recompute_derived_values (ga);
739
740 return ga;
741 }
742
743 /* ------------------------------- */
744 /* external functions */
745 /* ------------------------------- */
746
747 Bytecount
748 gap_array_byte_size (Gap_Array *ga)
749 {
750 return offsetof (Gap_Array, array) + (ga->numels + ga->gapsize) * ga->elsize;
751 }
752
753 /* Insert NUMELS elements (pointed to by ELPTR) into the specified
754 gap array at POS. The gap array may be moved, so assign the
755 return value back to the array pointer. */
756
757 Gap_Array *
758 gap_array_insert_els (Gap_Array *ga, Elemcount pos, void *elptr,
759 Elemcount numels)
760 {
761 assert (pos >= 0 && pos <= ga->numels);
762 if (ga->gapsize < numels)
763 ga = gap_array_make_gap (ga, numels - ga->gapsize);
764 if (pos != ga->gap)
765 gap_array_move_gap (ga, pos);
766
767 memcpy (GAP_ARRAY_MEMEL_ADDR (ga, ga->gap), (char *) elptr,
768 numels*ga->elsize);
769 ga->gapsize -= numels;
770 ga->gap += numels;
771 ga->numels += numels;
772 gap_array_recompute_derived_values (ga);
773 /* This is the equivalent of insert-before-markers.
774
775 #### Should only happen if marker is "moves forward at insert" type.
776 */
777
778 gap_array_adjust_markers (ga, pos - 1, pos, numels);
779 return ga;
780 }
781
782 /* Delete NUMELS elements from the specified gap array, starting at FROM. */
783
784 void
785 gap_array_delete_els (Gap_Array *ga, Elemcount from, Elemcount numdel)
786 {
787 Elemcount to = from + numdel;
788 Elemcount gapsize = ga->gapsize;
789
790 assert (from >= 0);
791 assert (numdel >= 0);
792 assert (to <= ga->numels);
793
794 /* Make sure the gap is somewhere in or next to what we are deleting. */
795 if (to < ga->gap)
796 gap_array_move_gap (ga, to);
797 if (from > ga->gap)
798 gap_array_move_gap (ga, from);
799
800 /* Relocate all markers pointing into the new, larger gap
801 to point at the end of the text before the gap. */
802 gap_array_adjust_markers (ga, to + gapsize, to + gapsize,
803 - numdel - gapsize);
804
805 ga->gapsize += numdel;
806 ga->numels -= numdel;
807 ga->gap = from;
808 gap_array_recompute_derived_values (ga);
809 }
810
811 Gap_Array_Marker *
812 gap_array_make_marker (Gap_Array *ga, Elemcount pos)
813 {
814 Gap_Array_Marker *m;
815
816 assert (pos >= 0 && pos <= ga->numels);
817 #ifdef NEW_GC
818 m = XGAP_ARRAY_MARKER (ALLOC_NORMAL_LISP_OBJECT (gap_array_marker));
819 #else /* not NEW_GC */
820 if (gap_array_marker_freelist)
821 {
822 m = gap_array_marker_freelist;
823 gap_array_marker_freelist = gap_array_marker_freelist->next;
824 }
825 else
826 m = xnew (Gap_Array_Marker);
827 #endif /* not NEW_GC */
828
829 m->pos = GAP_ARRAY_ARRAY_TO_MEMORY_POS (ga, pos);
830 m->next = ga->markers;
831 ga->markers = m;
832 return m;
833 }
834
835 void
836 gap_array_delete_marker (Gap_Array *ga, Gap_Array_Marker *m)
837 {
838 Gap_Array_Marker *p, *prev;
839
840 for (prev = 0, p = ga->markers; p && p != m; prev = p, p = p->next)
841 ;
842 assert (p);
843 if (prev)
844 prev->next = p->next;
845 else
846 ga->markers = p->next;
847 #ifndef NEW_GC
848 m->next = gap_array_marker_freelist;
849 m->pos = 0xDEADBEEF; /* -559038737 base 10 */
850 gap_array_marker_freelist = m;
851 #endif /* not NEW_GC */
852 }
853
854 #ifndef NEW_GC
855 void
856 gap_array_delete_all_markers (Gap_Array *ga)
857 {
858 Gap_Array_Marker *p, *next;
859
860 for (p = ga->markers; p; p = next)
861 {
862 next = p->next;
863 p->next = gap_array_marker_freelist;
864 p->pos = 0xDEADBEEF; /* -559038737 as an int */
865 gap_array_marker_freelist = p;
866 }
867 }
868 #endif /* not NEW_GC */
869
870 void
871 gap_array_move_marker (Gap_Array *ga, Gap_Array_Marker *m, Elemcount pos)
872 {
873 assert (pos >= 0 && pos <= ga->numels);
874 m->pos = GAP_ARRAY_ARRAY_TO_MEMORY_POS (ga, pos);
875 }
876
877 Gap_Array *
878 make_gap_array (Elemcount elsize, int USED_IF_NEW_GC (do_lisp))
879 {
880 Gap_Array *ga;
881 #ifdef NEW_GC
882 /* #### I don't quite understand why it's necessary to make all these
883 internal objects into Lisp objects under NEW_GC. It's a pain in the
884 ass to code around this. I'm proceeding on the assumption that it's
885 not really necessary to do it after all, and so we only make a Lisp-
886 object gap array when the object being held is a Lisp_Object, i.e. a
887 pointer to a Lisp object. In the case where instead we hold a `struct
888 range_table_entry', just blow it off. Otherwise we either need to do
889 a bunch of painful and/or boring rewriting. --ben */
890 if (do_lisp)
891 {
892 ga = XGAP_ARRAY (ALLOC_SIZED_LISP_OBJECT (sizeof (Gap_Array),
893 gap_array));
894 ga->is_lisp = 1;
895 }
896 else
897 #endif /* not NEW_GC */
898 ga = xnew_and_zero (Gap_Array);
899 ga->elsize = elsize;
900 return ga;
901 }
902
903 Gap_Array *
904 gap_array_clone (Gap_Array *ga)
905 {
906 Bytecount size = gap_array_byte_size (ga);
907 Gap_Array *ga2;
908 Gap_Array_Marker *m;
909
910 #ifdef NEW_GC
911 if (ga->is_lisp)
912 {
913 ga2 = XGAP_ARRAY (ALLOC_SIZED_LISP_OBJECT (size, gap_array));
914 copy_lisp_object (wrap_gap_array (ga2), wrap_gap_array (ga));
915 }
916 else
917 #endif
918 {
919 ga2 = (Gap_Array *) xmalloc (size);
920 memcpy (ga2, ga, size);
921 }
922 ga2->markers = NULL;
923 for (m = ga->markers; m; m = m->next)
924 gap_array_make_marker (ga2, m->pos);
925 return ga2;
926 }
927
928 #ifndef NEW_GC
929 void
930 free_gap_array (Gap_Array *ga)
931 {
932 gap_array_delete_all_markers (ga);
933 xfree (ga);
934 }
935 #endif /* not NEW_GC */
936
937
938 /*****************************************************************************/
939 /* Initialization */
940 /*****************************************************************************/
941
942 void
943 syms_of_array (void)
944 {
945 #ifdef NEW_GC
946 INIT_LISP_OBJECT (gap_array_marker);
947 INIT_LISP_OBJECT (gap_array);
948 #endif /* NEW_GC */
949 }
950