comparison src/rangetab.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 8de8e3f6228a
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
1 /* XEmacs routines to deal with range tables.
2 Copyright (C) 1995 Sun Microsystems, Inc.
3 Copyright (C) 1995 Ben Wing.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22 /* Synched up with: Not in FSF. */
23
24 /* Written by Ben Wing, August 1995. */
25
26 #include <config.h>
27 #include "lisp.h"
28 #include "rangetab.h"
29
30 Lisp_Object Qrange_tablep;
31 Lisp_Object Qrange_table;
32
33
34 /************************************************************************/
35 /* Range table object */
36 /************************************************************************/
37
38 /* We use a sorted array of ranges.
39
40 #### We should be using the gap array stuff from extents.c. This
41 is not hard but just requires moving that stuff out of that file. */
42
43 static Lisp_Object
44 mark_range_table (Lisp_Object obj)
45 {
46 struct Lisp_Range_Table *rt = XRANGE_TABLE (obj);
47 int i;
48
49 for (i = 0; i < Dynarr_length (rt->entries); i++)
50 mark_object (Dynarr_at (rt->entries, i).val);
51 return Qnil;
52 }
53
54 static void
55 print_range_table (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
56 {
57 struct Lisp_Range_Table *rt = XRANGE_TABLE (obj);
58 char buf[200];
59 int i;
60
61 write_c_string ("#s(range-table data (", printcharfun);
62 for (i = 0; i < Dynarr_length (rt->entries); i++)
63 {
64 struct range_table_entry *rte = Dynarr_atp (rt->entries, i);
65 if (i > 0)
66 write_c_string (" ", printcharfun);
67 if (rte->first == rte->last)
68 sprintf (buf, "%ld ", (long) (rte->first));
69 else
70 sprintf (buf, "(%ld %ld) ", (long) (rte->first), (long) (rte->last));
71 write_c_string (buf, printcharfun);
72 print_internal (rte->val, printcharfun, 1);
73 }
74 write_c_string ("))", printcharfun);
75 }
76
77 static int
78 range_table_equal (Lisp_Object obj1, Lisp_Object obj2, int depth)
79 {
80 struct Lisp_Range_Table *rt1 = XRANGE_TABLE (obj1);
81 struct Lisp_Range_Table *rt2 = XRANGE_TABLE (obj2);
82 int i;
83
84 if (Dynarr_length (rt1->entries) != Dynarr_length (rt2->entries))
85 return 0;
86
87 for (i = 0; i < Dynarr_length (rt1->entries); i++)
88 {
89 struct range_table_entry *rte1 = Dynarr_atp (rt1->entries, i);
90 struct range_table_entry *rte2 = Dynarr_atp (rt2->entries, i);
91
92 if (rte1->first != rte2->first
93 || rte1->last != rte2->last
94 || !internal_equal (rte1->val, rte2->val, depth + 1))
95 return 0;
96 }
97
98 return 1;
99 }
100
101 static unsigned long
102 range_table_entry_hash (struct range_table_entry *rte, int depth)
103 {
104 return HASH3 (rte->first, rte->last, internal_hash (rte->val, depth + 1));
105 }
106
107 static unsigned long
108 range_table_hash (Lisp_Object obj, int depth)
109 {
110 struct Lisp_Range_Table *rt = XRANGE_TABLE (obj);
111 int i;
112 int size = Dynarr_length (rt->entries);
113 unsigned long hash = size;
114
115 /* approach based on internal_array_hash(). */
116 if (size <= 5)
117 {
118 for (i = 0; i < size; i++)
119 hash = HASH2 (hash,
120 range_table_entry_hash (Dynarr_atp (rt->entries, i),
121 depth));
122 return hash;
123 }
124
125 /* just pick five elements scattered throughout the array.
126 A slightly better approach would be to offset by some
127 noise factor from the points chosen below. */
128 for (i = 0; i < 5; i++)
129 hash = HASH2 (hash, range_table_entry_hash (Dynarr_atp (rt->entries,
130 i*size/5),
131 depth));
132 return hash;
133 }
134
135 static const struct lrecord_description rte_description_1[] = {
136 { XD_LISP_OBJECT, offsetof(range_table_entry, val), 1 },
137 { XD_END }
138 };
139
140 static const struct struct_description rte_description = {
141 sizeof(range_table_entry),
142 rte_description_1
143 };
144
145 static const struct lrecord_description rted_description_1[] = {
146 XD_DYNARR_DESC(range_table_entry_dynarr, &rte_description),
147 { XD_END }
148 };
149
150 static const struct struct_description rted_description = {
151 sizeof(range_table_entry_dynarr),
152 rted_description_1
153 };
154
155 static const struct lrecord_description range_table_description[] = {
156 { XD_STRUCT_PTR, offsetof(struct Lisp_Range_Table, entries), 1, &rted_description },
157 { XD_END }
158 };
159
160 DEFINE_LRECORD_IMPLEMENTATION ("range-table", range_table,
161 mark_range_table, print_range_table, 0,
162 range_table_equal, range_table_hash,
163 range_table_description,
164 struct Lisp_Range_Table);
165
166 /************************************************************************/
167 /* Range table operations */
168 /************************************************************************/
169
170 #ifdef ERROR_CHECK_TYPECHECK
171
172 static void
173 verify_range_table (struct Lisp_Range_Table *rt)
174 {
175 int i;
176
177 for (i = 0; i < Dynarr_length (rt->entries); i++)
178 {
179 struct range_table_entry *rte = Dynarr_atp (rt->entries, i);
180 assert (rte->last >= rte->first);
181 if (i > 0)
182 assert (Dynarr_at (rt->entries, i - 1).last < rte->first);
183 }
184 }
185
186 #else
187
188 #define verify_range_table(rt)
189
190 #endif
191
192 /* Look up in a range table without the Dynarr wrapper.
193 Used also by the unified range table format. */
194
195 static Lisp_Object
196 get_range_table (EMACS_INT pos, int nentries, struct range_table_entry *tab,
197 Lisp_Object default_)
198 {
199 int left = 0, right = nentries;
200
201 /* binary search for the entry. Based on similar code in
202 extent_list_locate(). */
203 while (left != right)
204 {
205 /* RIGHT might not point to a valid entry (i.e. it's at the end
206 of the list), so NEWPOS must round down. */
207 unsigned int newpos = (left + right) >> 1;
208 struct range_table_entry *entry = tab + newpos;
209 if (pos > entry->last)
210 left = newpos+1;
211 else if (pos < entry->first)
212 right = newpos;
213 else
214 return entry->val;
215 }
216
217 return default_;
218 }
219
220 DEFUN ("range-table-p", Frange_table_p, 1, 1, 0, /*
221 Return non-nil if OBJECT is a range table.
222 */
223 (object))
224 {
225 return RANGE_TABLEP (object) ? Qt : Qnil;
226 }
227
228 DEFUN ("make-range-table", Fmake_range_table, 0, 0, 0, /*
229 Return a new, empty range table.
230 You can manipulate it using `put-range-table', `get-range-table',
231 `remove-range-table', and `clear-range-table'.
232 */
233 ())
234 {
235 Lisp_Object obj;
236 struct Lisp_Range_Table *rt = alloc_lcrecord_type (struct Lisp_Range_Table,
237 &lrecord_range_table);
238 rt->entries = Dynarr_new (range_table_entry);
239 XSETRANGE_TABLE (obj, rt);
240 return obj;
241 }
242
243 DEFUN ("copy-range-table", Fcopy_range_table, 1, 1, 0, /*
244 Make a new range table which contains the same values for the same
245 ranges as the given table. The values will not themselves be copied.
246 */
247 (old_table))
248 {
249 struct Lisp_Range_Table *rt, *rtnew;
250 Lisp_Object obj;
251
252 CHECK_RANGE_TABLE (old_table);
253 rt = XRANGE_TABLE (old_table);
254
255 rtnew = alloc_lcrecord_type (struct Lisp_Range_Table, &lrecord_range_table);
256 rtnew->entries = Dynarr_new (range_table_entry);
257
258 Dynarr_add_many (rtnew->entries, Dynarr_atp (rt->entries, 0),
259 Dynarr_length (rt->entries));
260 XSETRANGE_TABLE (obj, rtnew);
261 return obj;
262 }
263
264 DEFUN ("get-range-table", Fget_range_table, 2, 3, 0, /*
265 Find value for position POS in TABLE.
266 If there is no corresponding value, return DEFAULT (defaults to nil).
267 */
268 (pos, table, default_))
269 {
270 struct Lisp_Range_Table *rt;
271
272 CHECK_RANGE_TABLE (table);
273 rt = XRANGE_TABLE (table);
274
275 CHECK_INT_COERCE_CHAR (pos);
276
277 return get_range_table (XINT (pos), Dynarr_length (rt->entries),
278 Dynarr_atp (rt->entries, 0), default_);
279 }
280
281 void
282 put_range_table (Lisp_Object table, EMACS_INT first,
283 EMACS_INT last, Lisp_Object val)
284 {
285 int i;
286 int insert_me_here = -1;
287 struct Lisp_Range_Table *rt = XRANGE_TABLE (table);
288
289 /* Now insert in the proper place. This gets tricky because
290 we may be overlapping one or more existing ranges and need
291 to fix them up. */
292
293 /* First delete all sections of any existing ranges that overlap
294 the new range. */
295 for (i = 0; i < Dynarr_length (rt->entries); i++)
296 {
297 struct range_table_entry *entry = Dynarr_atp (rt->entries, i);
298 /* We insert before the first range that begins at or after the
299 new range. */
300 if (entry->first >= first && insert_me_here < 0)
301 insert_me_here = i;
302 if (entry->last < first)
303 /* completely before the new range. */
304 continue;
305 if (entry->first > last)
306 /* completely after the new range. No more possibilities of
307 finding overlapping ranges. */
308 break;
309 if (entry->first < first && entry->last <= last)
310 {
311 /* looks like:
312
313 [ NEW ]
314 [ EXISTING ]
315
316 */
317 /* truncate the end off of it. */
318 entry->last = first - 1;
319 }
320 else if (entry->first < first && entry->last > last)
321 /* looks like:
322
323 [ NEW ]
324 [ EXISTING ]
325
326 */
327 /* need to split this one in two. */
328 {
329 struct range_table_entry insert_me_too;
330
331 insert_me_too.first = last + 1;
332 insert_me_too.last = entry->last;
333 insert_me_too.val = entry->val;
334 entry->last = first - 1;
335 Dynarr_insert_many (rt->entries, &insert_me_too, 1, i + 1);
336 }
337 else if (entry->last > last)
338 {
339 /* looks like:
340
341 [ NEW ]
342 [ EXISTING ]
343
344 */
345 /* truncate the start off of it. */
346 entry->first = last + 1;
347 }
348 else
349 {
350 /* existing is entirely within new. */
351 Dynarr_delete_many (rt->entries, i, 1);
352 i--; /* back up since everything shifted one to the left. */
353 }
354 }
355
356 /* Someone asked us to delete the range, not insert it. */
357 if (UNBOUNDP (val))
358 return;
359
360 /* Now insert the new entry, maybe at the end. */
361
362 if (insert_me_here < 0)
363 insert_me_here = i;
364
365 {
366 struct range_table_entry insert_me;
367
368 insert_me.first = first;
369 insert_me.last = last;
370 insert_me.val = val;
371
372 Dynarr_insert_many (rt->entries, &insert_me, 1, insert_me_here);
373 }
374
375 /* Now see if we can combine this entry with adjacent ones just
376 before or after. */
377
378 if (insert_me_here > 0)
379 {
380 struct range_table_entry *entry = Dynarr_atp (rt->entries,
381 insert_me_here - 1);
382 if (EQ (val, entry->val) && entry->last == first - 1)
383 {
384 entry->last = last;
385 Dynarr_delete_many (rt->entries, insert_me_here, 1);
386 insert_me_here--;
387 /* We have morphed into a larger range. Update our records
388 in case we also combine with the one after. */
389 first = entry->first;
390 }
391 }
392
393 if (insert_me_here < Dynarr_length (rt->entries) - 1)
394 {
395 struct range_table_entry *entry = Dynarr_atp (rt->entries,
396 insert_me_here + 1);
397 if (EQ (val, entry->val) && entry->first == last + 1)
398 {
399 entry->first = first;
400 Dynarr_delete_many (rt->entries, insert_me_here, 1);
401 }
402 }
403 }
404
405 DEFUN ("put-range-table", Fput_range_table, 4, 4, 0, /*
406 Set the value for range (START, END) to be VAL in TABLE.
407 */
408 (start, end, val, table))
409 {
410 EMACS_INT first, last;
411
412 CHECK_RANGE_TABLE (table);
413 CHECK_INT_COERCE_CHAR (start);
414 first = XINT (start);
415 CHECK_INT_COERCE_CHAR (end);
416 last = XINT (end);
417 if (first > last)
418 signal_simple_error_2 ("start must be <= end", start, end);
419
420 put_range_table (table, first, last, val);
421 verify_range_table (XRANGE_TABLE (table));
422 return Qnil;
423 }
424
425 DEFUN ("remove-range-table", Fremove_range_table, 3, 3, 0, /*
426 Remove the value for range (START, END) in TABLE.
427 */
428 (start, end, table))
429 {
430 return Fput_range_table (start, end, Qunbound, table);
431 }
432
433 DEFUN ("clear-range-table", Fclear_range_table, 1, 1, 0, /*
434 Flush TABLE.
435 */
436 (table))
437 {
438 CHECK_RANGE_TABLE (table);
439 Dynarr_reset (XRANGE_TABLE (table)->entries);
440 return Qnil;
441 }
442
443 DEFUN ("map-range-table", Fmap_range_table, 2, 2, 0, /*
444 Map FUNCTION over entries in TABLE, calling it with three args,
445 the beginning and end of the range and the corresponding value.
446 */
447 (function, table))
448 {
449 error ("not yet implemented");
450 return Qnil;
451 }
452
453
454 /************************************************************************/
455 /* Range table read syntax */
456 /************************************************************************/
457
458 static int
459 rangetab_data_validate (Lisp_Object keyword, Lisp_Object value,
460 Error_behavior errb)
461 {
462 Lisp_Object rest;
463
464 /* #### should deal with errb */
465 EXTERNAL_LIST_LOOP (rest, value)
466 {
467 Lisp_Object range = XCAR (rest);
468 rest = XCDR (rest);
469 if (!CONSP (rest))
470 signal_simple_error ("Invalid list format", value);
471 if (!INTP (range) && !CHARP (range)
472 && !(CONSP (range) && CONSP (XCDR (range))
473 && NILP (XCDR (XCDR (range)))
474 && (INTP (XCAR (range)) || CHARP (XCAR (range)))
475 && (INTP (XCAR (XCDR (range))) || CHARP (XCAR (XCDR (range))))))
476 signal_simple_error ("Invalid range format", range);
477 }
478
479 return 1;
480 }
481
482 static Lisp_Object
483 rangetab_instantiate (Lisp_Object data)
484 {
485 Lisp_Object rangetab = Fmake_range_table ();
486
487 if (!NILP (data))
488 {
489 data = Fcar (Fcdr (data)); /* skip over 'data keyword */
490 while (!NILP (data))
491 {
492 Lisp_Object range = Fcar (data);
493 Lisp_Object val = Fcar (Fcdr (data));
494
495 data = Fcdr (Fcdr (data));
496 if (CONSP (range))
497 Fput_range_table (Fcar (range), Fcar (Fcdr (range)), val,
498 rangetab);
499 else
500 Fput_range_table (range, range, val, rangetab);
501 }
502 }
503
504 return rangetab;
505 }
506
507
508 /************************************************************************/
509 /* Unified range tables */
510 /************************************************************************/
511
512 /* A "unified range table" is a format for storing range tables
513 as contiguous blocks of memory. This is used by the regexp
514 code, which needs to use range tables to properly handle []
515 constructs in the presence of extended characters but wants to
516 store an entire compiled pattern as a contiguous block of memory.
517
518 Unified range tables are designed so that they can be placed
519 at an arbitrary (possibly mis-aligned) place in memory.
520 (Dealing with alignment is a pain in the ass.)
521
522 WARNING: No provisions for garbage collection are currently made.
523 This means that there must not be any Lisp objects in a unified
524 range table that need to be marked for garbage collection.
525 Good candidates for objects that can go into a range table are
526
527 -- numbers and characters (do not need to be marked)
528 -- nil, t (marked elsewhere)
529 -- charsets and coding systems (automatically marked because
530 they are in a marked list,
531 and can't be removed)
532
533 Good but slightly less so:
534
535 -- symbols (could be uninterned, but that is not likely)
536
537 Somewhat less good:
538
539 -- buffers, frames, devices (could get deleted)
540
541
542 It is expected that you work with range tables in the normal
543 format and then convert to unified format when you are done
544 making modifications. As such, no functions are provided
545 for modifying a unified range table. The only operations
546 you can do to unified range tables are
547
548 -- look up a value
549 -- retrieve all the ranges in an iterative fashion
550
551 */
552
553 /* The format of a unified range table is as follows:
554
555 -- The first byte contains the number of bytes to skip to find the
556 actual start of the table. This deals with alignment constraints,
557 since the table might want to go at any arbitrary place in memory.
558 -- The next three bytes contain the number of bytes to skip (from the
559 *first* byte) to find the stuff after the table. It's stored in
560 little-endian format because that's how God intended things. We don't
561 necessarily start the stuff at the very end of the table because
562 we want to have at least ALIGNOF (EMACS_INT) extra space in case
563 we have to move the range table around. (It appears that some
564 architectures don't maintain alignment when reallocing.)
565 -- At the prescribed offset is a struct unified_range_table, containing
566 some number of `struct range_table_entry' entries. */
567
568 struct unified_range_table
569 {
570 int nentries;
571 struct range_table_entry first;
572 };
573
574 /* Return size in bytes needed to store the data in a range table. */
575
576 int
577 unified_range_table_bytes_needed (Lisp_Object rangetab)
578 {
579 return (sizeof (struct range_table_entry) *
580 (Dynarr_length (XRANGE_TABLE (rangetab)->entries) - 1) +
581 sizeof (struct unified_range_table) +
582 /* ALIGNOF a struct may be too big. */
583 /* We have four bytes for the size numbers, and an extra
584 four or eight bytes for making sure we get the alignment
585 OK. */
586 ALIGNOF (EMACS_INT) + 4);
587 }
588
589 /* Convert a range table into unified format and store in DEST,
590 which must be able to hold the number of bytes returned by
591 range_table_bytes_needed(). */
592
593 void
594 unified_range_table_copy_data (Lisp_Object rangetab, void *dest)
595 {
596 /* We cast to the above structure rather than just casting to
597 char * and adding sizeof(int), because that will lead to
598 mis-aligned data on the Alpha machines. */
599 struct unified_range_table *un;
600 range_table_entry_dynarr *rted = XRANGE_TABLE (rangetab)->entries;
601 int total_needed = unified_range_table_bytes_needed (rangetab);
602 void *new_dest = ALIGN_PTR ((char *) dest + 4, ALIGNOF (EMACS_INT));
603
604 * (char *) dest = (char) ((char *) new_dest - (char *) dest);
605 * ((unsigned char *) dest + 1) = total_needed & 0xFF;
606 total_needed >>= 8;
607 * ((unsigned char *) dest + 2) = total_needed & 0xFF;
608 total_needed >>= 8;
609 * ((unsigned char *) dest + 3) = total_needed & 0xFF;
610 un = (struct unified_range_table *) new_dest;
611 un->nentries = Dynarr_length (rted);
612 memcpy (&un->first, Dynarr_atp (rted, 0),
613 sizeof (struct range_table_entry) * Dynarr_length (rted));
614 }
615
616 /* Return number of bytes actually used by a unified range table. */
617
618 int
619 unified_range_table_bytes_used (void *unrangetab)
620 {
621 return ((* ((unsigned char *) unrangetab + 1))
622 + ((* ((unsigned char *) unrangetab + 2)) << 8)
623 + ((* ((unsigned char *) unrangetab + 3)) << 16));
624 }
625
626 /* Make sure the table is aligned, and move it around if it's not. */
627 static void
628 align_the_damn_table (void *unrangetab)
629 {
630 void *cur_dest = (char *) unrangetab + * (char *) unrangetab;
631 #if LONGBITS == 64
632 if ((((long) cur_dest) & 7) != 0)
633 #else
634 if ((((int) cur_dest) & 3) != 0)
635 #endif
636 {
637 int count = (unified_range_table_bytes_used (unrangetab) - 4
638 - ALIGNOF (EMACS_INT));
639 /* Find the proper location, just like above. */
640 void *new_dest = ALIGN_PTR ((char *) unrangetab + 4,
641 ALIGNOF (EMACS_INT));
642 /* memmove() works in the presence of overlapping data. */
643 memmove (new_dest, cur_dest, count);
644 * (char *) unrangetab = (char) ((char *) new_dest - (char *) unrangetab);
645 }
646 }
647
648 /* Look up a value in a unified range table. */
649
650 Lisp_Object
651 unified_range_table_lookup (void *unrangetab, EMACS_INT pos,
652 Lisp_Object default_)
653 {
654 void *new_dest;
655 struct unified_range_table *un;
656
657 align_the_damn_table (unrangetab);
658 new_dest = (char *) unrangetab + * (char *) unrangetab;
659 un = (struct unified_range_table *) new_dest;
660
661 return get_range_table (pos, un->nentries, &un->first, default_);
662 }
663
664 /* Return number of entries in a unified range table. */
665
666 int
667 unified_range_table_nentries (void *unrangetab)
668 {
669 void *new_dest;
670 struct unified_range_table *un;
671
672 align_the_damn_table (unrangetab);
673 new_dest = (char *) unrangetab + * (char *) unrangetab;
674 un = (struct unified_range_table *) new_dest;
675 return un->nentries;
676 }
677
678 /* Return the OFFSETth range (counting from 0) in UNRANGETAB. */
679 void
680 unified_range_table_get_range (void *unrangetab, int offset,
681 EMACS_INT *min, EMACS_INT *max,
682 Lisp_Object *val)
683 {
684 void *new_dest;
685 struct unified_range_table *un;
686 struct range_table_entry *tab;
687
688 align_the_damn_table (unrangetab);
689 new_dest = (char *) unrangetab + * (char *) unrangetab;
690 un = (struct unified_range_table *) new_dest;
691
692 assert (offset >= 0 && offset < un->nentries);
693 tab = (&un->first) + offset;
694 *min = tab->first;
695 *max = tab->last;
696 *val = tab->val;
697 }
698
699
700 /************************************************************************/
701 /* Initialization */
702 /************************************************************************/
703
704 void
705 syms_of_rangetab (void)
706 {
707 defsymbol (&Qrange_tablep, "range-table-p");
708 defsymbol (&Qrange_table, "range-table");
709
710 DEFSUBR (Frange_table_p);
711 DEFSUBR (Fmake_range_table);
712 DEFSUBR (Fcopy_range_table);
713 DEFSUBR (Fget_range_table);
714 DEFSUBR (Fput_range_table);
715 DEFSUBR (Fremove_range_table);
716 DEFSUBR (Fclear_range_table);
717 DEFSUBR (Fmap_range_table);
718 }
719
720 void
721 structure_type_create_rangetab (void)
722 {
723 struct structure_type *st;
724
725 st = define_structure_type (Qrange_table, 0, rangetab_instantiate);
726
727 define_structure_type_keyword (st, Qdata, rangetab_data_validate);
728 }