This commit was generated by cvs2svn to compensate for changes in r1383,
[chise/xemacs-chise.git.1] / src / rangetab.c
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 \f
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   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   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   Lisp_Range_Table *rt1 = XRANGE_TABLE (obj1);
81   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   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) },
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 (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                                Lisp_Range_Table);
165 \f
166 /************************************************************************/
167 /*                        Range table operations                        */
168 /************************************************************************/
169
170 #ifdef ERROR_CHECK_TYPECHECK
171
172 static void
173 verify_range_table (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   Lisp_Range_Table *rt = alloc_lcrecord_type (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   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 (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   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   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 \f
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 \f
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 \f
700 /************************************************************************/
701 /*                            Initialization                            */
702 /************************************************************************/
703
704 void
705 syms_of_rangetab (void)
706 {
707   INIT_LRECORD_IMPLEMENTATION (range_table);
708
709   defsymbol (&Qrange_tablep, "range-table-p");
710   defsymbol (&Qrange_table, "range-table");
711
712   DEFSUBR (Frange_table_p);
713   DEFSUBR (Fmake_range_table);
714   DEFSUBR (Fcopy_range_table);
715   DEFSUBR (Fget_range_table);
716   DEFSUBR (Fput_range_table);
717   DEFSUBR (Fremove_range_table);
718   DEFSUBR (Fclear_range_table);
719   DEFSUBR (Fmap_range_table);
720 }
721
722 void
723 structure_type_create_rangetab (void)
724 {
725   struct structure_type *st;
726
727   st = define_structure_type (Qrange_table, 0, rangetab_instantiate);
728
729   define_structure_type_keyword (st, Qdata, rangetab_data_validate);
730 }