XEmacs 21.2-b1
[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, void (*markobj) (Lisp_Object))
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     (markobj) (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 DEFINE_LRECORD_IMPLEMENTATION ("range-table", range_table,
136                                mark_range_table, print_range_table, 0,
137                                range_table_equal, range_table_hash,
138                                struct Lisp_Range_Table);
139 \f
140 /************************************************************************/
141 /*                        Range table operations                        */
142 /************************************************************************/
143
144 #ifdef ERROR_CHECK_TYPECHECK
145
146 static void
147 verify_range_table (struct Lisp_Range_Table *rt)
148 {
149   int i;
150
151   for (i = 0; i < Dynarr_length (rt->entries); i++)
152     {
153       struct range_table_entry *rte = Dynarr_atp (rt->entries, i);
154       assert (rte->last >= rte->first);
155       if (i > 0)
156         assert (Dynarr_at (rt->entries, i - 1).last < rte->first);
157     }
158 }
159
160 #else
161
162 #define verify_range_table(rt)
163
164 #endif
165
166 /* Look up in a range table without the Dynarr wrapper.
167    Used also by the unified range table format. */
168
169 static Lisp_Object
170 get_range_table (EMACS_INT pos, int nentries, struct range_table_entry *tab,
171                  Lisp_Object default_)
172 {
173   int left = 0, right = nentries;
174
175   /* binary search for the entry.  Based on similar code in
176      extent_list_locate(). */
177   while (left != right)
178     {
179       /* RIGHT might not point to a valid entry (i.e. it's at the end
180          of the list), so NEWPOS must round down. */
181       unsigned int newpos = (left + right) >> 1;
182       struct range_table_entry *entry = tab + newpos;
183       if (pos > entry->last)
184         left = newpos+1;
185       else if (pos < entry->first)
186         right = newpos;
187       else
188         return entry->val;
189     }
190
191   return default_;
192 }
193
194 DEFUN ("range-table-p", Frange_table_p, 1, 1, 0, /*
195 Return non-nil if OBJECT is a range table.
196 */
197        (object))
198 {
199   return RANGE_TABLEP (object) ? Qt : Qnil;
200 }
201
202 DEFUN ("make-range-table", Fmake_range_table, 0, 0, 0, /*
203 Return a new, empty range table.
204 You can manipulate it using `put-range-table', `get-range-table',
205 `remove-range-table', and `clear-range-table'.
206 */
207        ())
208 {
209   Lisp_Object obj;
210   struct Lisp_Range_Table *rt = alloc_lcrecord_type (struct Lisp_Range_Table,
211                                                      lrecord_range_table);
212   rt->entries = Dynarr_new (range_table_entry);
213   XSETRANGE_TABLE (obj, rt);
214   return obj;
215 }
216
217 DEFUN ("copy-range-table", Fcopy_range_table, 1, 1, 0, /*
218 Make a new range table which contains the same values for the same
219 ranges as the given table.  The values will not themselves be copied.
220 */
221        (old_table))
222 {
223   struct Lisp_Range_Table *rt, *rtnew;
224   Lisp_Object obj;
225
226   CHECK_RANGE_TABLE (old_table);
227   rt = XRANGE_TABLE (old_table);
228
229   rtnew = alloc_lcrecord_type (struct Lisp_Range_Table, lrecord_range_table);
230   rtnew->entries = Dynarr_new (range_table_entry);
231
232   Dynarr_add_many (rtnew->entries, Dynarr_atp (rt->entries, 0),
233                    Dynarr_length (rt->entries));
234   XSETRANGE_TABLE (obj, rtnew);
235   return obj;
236 }
237
238 DEFUN ("get-range-table", Fget_range_table, 2, 3, 0, /*
239 Find value for position POS in TABLE.
240 If there is no corresponding value, return DEFAULT (defaults to nil).
241 */
242        (pos, table, default_))
243 {
244   struct Lisp_Range_Table *rt;
245   EMACS_INT po;
246
247   CHECK_RANGE_TABLE (table);
248   rt = XRANGE_TABLE (table);
249
250   CHECK_INT_COERCE_CHAR (pos);
251   po = XINT (pos);
252
253   return get_range_table (po, Dynarr_length (rt->entries),
254                           Dynarr_atp (rt->entries, 0), default_);
255 }
256
257 void
258 put_range_table (Lisp_Object table, EMACS_INT first,
259                  EMACS_INT last, Lisp_Object val)
260 {
261   int i;
262   int insert_me_here = -1;
263   struct Lisp_Range_Table *rt = XRANGE_TABLE (table);
264
265   /* Now insert in the proper place.  This gets tricky because
266      we may be overlapping one or more existing ranges and need
267      to fix them up. */
268
269   /* First delete all sections of any existing ranges that overlap
270      the new range. */
271   for (i = 0; i < Dynarr_length (rt->entries); i++)
272     {
273       struct range_table_entry *entry = Dynarr_atp (rt->entries, i);
274       /* We insert before the first range that begins at or after the
275          new range. */
276       if (entry->first >= first && insert_me_here < 0)
277         insert_me_here = i;
278       if (entry->last < first)
279         /* completely before the new range. */
280         continue;
281       if (entry->first > last)
282         /* completely after the new range.  No more possibilities of
283            finding overlapping ranges. */
284         break;
285       if (entry->first < first && entry->last <= last)
286         {
287           /* looks like:
288
289                          [ NEW ]
290                  [ EXISTING ]
291
292            */
293           /* truncate the end off of it. */
294           entry->last = first - 1;
295         }
296       else if (entry->first < first && entry->last > last)
297         /* looks like:
298
299                  [ NEW ]
300                [ EXISTING ]
301
302          */
303         /* need to split this one in two. */
304         {
305           struct range_table_entry insert_me_too;
306
307           insert_me_too.first = last + 1;
308           insert_me_too.last = entry->last;
309           insert_me_too.val = entry->val;
310           entry->last = first - 1;
311           Dynarr_insert_many (rt->entries, &insert_me_too, 1, i + 1);
312         }
313       else if (entry->last > last)
314         {
315           /* looks like:
316
317                [ NEW ]
318                  [ EXISTING ]
319
320            */
321           /* truncate the start off of it. */
322           entry->first = last + 1;
323         }
324       else
325         {
326           /* existing is entirely within new. */
327           Dynarr_delete_many (rt->entries, i, 1);
328           i--; /* back up since everything shifted one to the left. */
329         }
330     }
331
332   /* Someone asked us to delete the range, not insert it. */
333   if (UNBOUNDP (val))
334     return;
335
336   /* Now insert the new entry, maybe at the end. */
337
338   if (insert_me_here < 0)
339     insert_me_here = i;
340
341   {
342     struct range_table_entry insert_me;
343
344     insert_me.first = first;
345     insert_me.last = last;
346     insert_me.val = val;
347
348     Dynarr_insert_many (rt->entries, &insert_me, 1, insert_me_here);
349   }
350
351   /* Now see if we can combine this entry with adjacent ones just
352      before or after. */
353
354   if (insert_me_here > 0)
355     {
356       struct range_table_entry *entry = Dynarr_atp (rt->entries,
357                                                     insert_me_here - 1);
358       if (EQ (val, entry->val) && entry->last == first - 1)
359         {
360           entry->last = last;
361           Dynarr_delete_many (rt->entries, insert_me_here, 1);
362           insert_me_here--;
363           /* We have morphed into a larger range.  Update our records
364              in case we also combine with the one after. */
365           first = entry->first;
366         }
367     }
368
369   if (insert_me_here < Dynarr_length (rt->entries) - 1)
370     {
371       struct range_table_entry *entry = Dynarr_atp (rt->entries,
372                                                     insert_me_here + 1);
373       if (EQ (val, entry->val) && entry->first == last + 1)
374         {
375           entry->first = first;
376           Dynarr_delete_many (rt->entries, insert_me_here, 1);
377         }
378     }
379 }
380
381 DEFUN ("put-range-table", Fput_range_table, 4, 4, 0, /*
382 Set the value for range (START, END) to be VAL in TABLE.
383 */
384        (start, end, val, table))
385 {
386   EMACS_INT first, last;
387
388   CHECK_RANGE_TABLE (table);
389   CHECK_INT_COERCE_CHAR (start);
390   first = XINT (start);
391   CHECK_INT_COERCE_CHAR (end);
392   last = XINT (end);
393   if (first > last)
394     signal_simple_error_2 ("start must be <= end", start, end);
395
396   put_range_table (table, first, last, val);
397   verify_range_table (XRANGE_TABLE (table));
398   return Qnil;
399 }
400
401 DEFUN ("remove-range-table", Fremove_range_table, 3, 3, 0, /*
402 Remove the value for range (START, END) in TABLE.
403 */
404        (start, end, table))
405 {
406   return Fput_range_table (start, end, Qunbound, table);
407 }
408
409 DEFUN ("clear-range-table", Fclear_range_table, 1, 1, 0, /*
410 Flush TABLE.
411 */
412        (table))
413 {
414   CHECK_RANGE_TABLE (table);
415   Dynarr_reset (XRANGE_TABLE (table)->entries);
416   return Qnil;
417 }
418
419 DEFUN ("map-range-table", Fmap_range_table, 2, 2, 0, /*
420 Map FUNCTION over entries in TABLE, calling it with three args,
421 the beginning and end of the range and the corresponding value.
422 */
423        (function, table))
424 {
425   error ("not yet implemented");
426   return Qnil;
427 }
428
429 \f
430 /************************************************************************/
431 /*                         Range table read syntax                      */
432 /************************************************************************/
433
434 static int
435 rangetab_data_validate (Lisp_Object keyword, Lisp_Object value,
436                         Error_behavior errb)
437 {
438   Lisp_Object rest;
439
440   /* #### should deal with errb */
441   EXTERNAL_LIST_LOOP (rest, value)
442     {
443       Lisp_Object range = XCAR (rest);
444       rest = XCDR (rest);
445       if (!CONSP (rest))
446         signal_simple_error ("Invalid list format", value);
447       if (!INTP (range) && !CHARP (range)
448           && !(CONSP (range) && CONSP (XCDR (range))
449                && NILP (XCDR (XCDR (range)))
450                && (INTP (XCAR (range)) || CHARP (XCAR (range)))
451                && (INTP (XCAR (XCDR (range))) || CHARP (XCAR (XCDR (range))))))
452         signal_simple_error ("Invalid range format", range);
453     }
454
455   return 1;
456 }
457
458 static Lisp_Object
459 rangetab_instantiate (Lisp_Object data)
460 {
461   Lisp_Object rangetab = Fmake_range_table ();
462
463   if (!NILP (data))
464     {
465       data = Fcar (Fcdr (data)); /* skip over 'data keyword */
466       while (!NILP (data))
467         {
468           Lisp_Object range = Fcar (data);
469           Lisp_Object val = Fcar (Fcdr (data));
470
471           data = Fcdr (Fcdr (data));
472           if (CONSP (range))
473             Fput_range_table (Fcar (range), Fcar (Fcdr (range)), val,
474                               rangetab);
475           else
476             Fput_range_table (range, range, val, rangetab);
477         }
478     }
479
480   return rangetab;
481 }
482
483 \f
484 /************************************************************************/
485 /*                         Unified range tables                         */
486 /************************************************************************/
487
488 /* A "unified range table" is a format for storing range tables
489    as contiguous blocks of memory.  This is used by the regexp
490    code, which needs to use range tables to properly handle []
491    constructs in the presence of extended characters but wants to
492    store an entire compiled pattern as a contiguous block of memory.
493
494    Unified range tables are designed so that they can be placed
495    at an arbitrary (possibly mis-aligned) place in memory.
496    (Dealing with alignment is a pain in the ass.)
497
498    WARNING: No provisions for garbage collection are currently made.
499    This means that there must not be any Lisp objects in a unified
500    range table that need to be marked for garbage collection.
501    Good candidates for objects that can go into a range table are
502
503    -- numbers and characters (do not need to be marked)
504    -- nil, t (marked elsewhere)
505    -- charsets and coding systems (automatically marked because
506                                    they are in a marked list,
507                                    and can't be removed)
508
509    Good but slightly less so:
510
511    -- symbols (could be uninterned, but that is not likely)
512
513    Somewhat less good:
514
515    -- buffers, frames, devices (could get deleted)
516
517
518    It is expected that you work with range tables in the normal
519    format and then convert to unified format when you are done
520    making modifications.  As such, no functions are provided
521    for modifying a unified range table.  The only operations
522    you can do to unified range tables are
523
524    -- look up a value
525    -- retrieve all the ranges in an iterative fashion
526
527 */
528
529 /* The format of a unified range table is as follows:
530
531    -- The first byte contains the number of bytes to skip to find the
532       actual start of the table.  This deals with alignment constraints,
533       since the table might want to go at any arbitrary place in memory.
534    -- The next three bytes contain the number of bytes to skip (from the
535       *first* byte) to find the stuff after the table.  It's stored in
536       little-endian format because that's how God intended things.  We don't
537       necessarily start the stuff at the very end of the table because
538       we want to have at least ALIGNOF (EMACS_INT) extra space in case
539       we have to move the range table around. (It appears that some
540       architectures don't maintain alignment when reallocing.)
541    -- At the prescribed offset is a struct unified_range_table, containing
542       some number of `struct range_table_entry' entries. */
543
544 struct unified_range_table
545 {
546   int nentries;
547   struct range_table_entry first;
548 };
549
550 /* Return size in bytes needed to store the data in a range table. */
551
552 int
553 unified_range_table_bytes_needed (Lisp_Object rangetab)
554 {
555   return (sizeof (struct range_table_entry) *
556           (Dynarr_length (XRANGE_TABLE (rangetab)->entries) - 1) +
557           sizeof (struct unified_range_table) +
558           /* ALIGNOF a struct may be too big. */
559           /* We have four bytes for the size numbers, and an extra
560              four or eight bytes for making sure we get the alignment
561              OK. */
562           ALIGNOF (EMACS_INT) + 4);
563 }
564
565 /* Convert a range table into unified format and store in DEST,
566    which must be able to hold the number of bytes returned by
567    range_table_bytes_needed(). */
568
569 void
570 unified_range_table_copy_data (Lisp_Object rangetab, void *dest)
571 {
572   /* We cast to the above structure rather than just casting to
573      char * and adding sizeof(int), because that will lead to
574      mis-aligned data on the Alpha machines. */
575   struct unified_range_table *un;
576   range_table_entry_dynarr *rted = XRANGE_TABLE (rangetab)->entries;
577   int total_needed = unified_range_table_bytes_needed (rangetab);
578   void *new_dest = ALIGN_PTR ((char *) dest + 4, ALIGNOF (EMACS_INT));
579
580   * (char *) dest = (char) ((char *) new_dest - (char *) dest);
581   * ((unsigned char *) dest + 1) = total_needed & 0xFF;
582   total_needed >>= 8;
583   * ((unsigned char *) dest + 2) = total_needed & 0xFF;
584   total_needed >>= 8;
585   * ((unsigned char *) dest + 3) = total_needed & 0xFF;
586   un = (struct unified_range_table *) new_dest;
587   un->nentries = Dynarr_length (rted);
588   memcpy (&un->first, Dynarr_atp (rted, 0),
589           sizeof (struct range_table_entry) * Dynarr_length (rted));
590 }
591
592 /* Return number of bytes actually used by a unified range table. */
593
594 int
595 unified_range_table_bytes_used (void *unrangetab)
596 {
597   return ((* ((unsigned char *) unrangetab + 1))
598           + ((* ((unsigned char *) unrangetab + 2)) << 8)
599           + ((* ((unsigned char *) unrangetab + 3)) << 16));
600 }
601
602 /* Make sure the table is aligned, and move it around if it's not. */
603 static void
604 align_the_damn_table (void *unrangetab)
605 {
606   void *cur_dest = (char *) unrangetab + * (char *) unrangetab;
607 #if LONGBITS == 64
608   if ((((long) cur_dest) & 7) != 0)
609 #else
610   if ((((int) cur_dest) & 3) != 0)
611 #endif
612     {
613       int count = (unified_range_table_bytes_used (unrangetab) - 4
614                    - ALIGNOF (EMACS_INT));
615       /* Find the proper location, just like above. */
616       void *new_dest = ALIGN_PTR ((char *) unrangetab + 4,
617                                   ALIGNOF (EMACS_INT));
618       /* memmove() works in the presence of overlapping data. */
619       memmove (new_dest, cur_dest, count);
620       * (char *) unrangetab = (char) ((char *) new_dest - (char *) unrangetab);
621     }
622 }
623
624 /* Look up a value in a unified range table. */
625
626 Lisp_Object
627 unified_range_table_lookup (void *unrangetab, EMACS_INT pos,
628                             Lisp_Object default_)
629 {
630   void *new_dest;
631   struct unified_range_table *un;
632
633   align_the_damn_table (unrangetab);
634   new_dest = (char *) unrangetab + * (char *) unrangetab;
635   un = (struct unified_range_table *) new_dest;
636
637   return get_range_table (pos, un->nentries, &un->first, default_);
638 }
639
640 /* Return number of entries in a unified range table. */
641
642 int
643 unified_range_table_nentries (void *unrangetab)
644 {
645   void *new_dest;
646   struct unified_range_table *un;
647
648   align_the_damn_table (unrangetab);
649   new_dest = (char *) unrangetab + * (char *) unrangetab;
650   un = (struct unified_range_table *) new_dest;
651   return un->nentries;
652 }
653
654 /* Return the OFFSETth range (counting from 0) in UNRANGETAB. */
655 void
656 unified_range_table_get_range (void *unrangetab, int offset,
657                                EMACS_INT *min, EMACS_INT *max,
658                                Lisp_Object *val)
659 {
660   void *new_dest;
661   struct unified_range_table *un;
662   struct range_table_entry *tab;
663
664   align_the_damn_table (unrangetab);
665   new_dest = (char *) unrangetab + * (char *) unrangetab;
666   un = (struct unified_range_table *) new_dest;
667
668   assert (offset >= 0 && offset < un->nentries);
669   tab = (&un->first) + offset;
670   *min = tab->first;
671   *max = tab->last;
672   *val = tab->val;
673 }
674
675 \f
676 /************************************************************************/
677 /*                            Initialization                            */
678 /************************************************************************/
679
680 void
681 syms_of_rangetab (void)
682 {
683   defsymbol (&Qrange_tablep, "range-table-p");
684   defsymbol (&Qrange_table, "range-table");
685
686   DEFSUBR (Frange_table_p);
687   DEFSUBR (Fmake_range_table);
688   DEFSUBR (Fcopy_range_table);
689   DEFSUBR (Fget_range_table);
690   DEFSUBR (Fput_range_table);
691   DEFSUBR (Fremove_range_table);
692   DEFSUBR (Fclear_range_table);
693   DEFSUBR (Fmap_range_table);
694 }
695
696 void
697 structure_type_create_rangetab (void)
698 {
699   struct structure_type *st;
700
701   st = define_structure_type (Qrange_table, 0, rangetab_instantiate);
702
703   define_structure_type_keyword (st, Qdata, rangetab_data_validate);
704 }