a8556d2dec708db708bb372e8000a8130e8f8fc2
[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 Results are guaranteed to be correct (i.e. each entry processed
448 exactly once) if FUNCTION modifies or deletes the current entry
449 (i.e. passes the current range to `put-range-table' or
450 `remove-range-table'), but not otherwise.
451 */
452        (function, table))
453 {
454   Lisp_Range_Table *rt;
455   int i;
456
457   CHECK_RANGE_TABLE (table);
458   CHECK_FUNCTION (function);
459
460   rt = XRANGE_TABLE (table);
461
462   /* Do not "optimize" by pulling out the length computation below!
463      FUNCTION may have changed the table. */
464   for (i = 0; i < Dynarr_length (rt->entries); i++)
465     {
466       struct range_table_entry *entry = Dynarr_atp (rt->entries, i);
467       EMACS_INT first, last;
468       Lisp_Object args[4];
469       int oldlen;
470       
471     again:
472       first = entry->first;
473       last = entry->last;
474       oldlen = Dynarr_length (rt->entries);
475       args[0] = function;
476       args[1] = make_int (first);
477       args[2] = make_int (last);
478       args[3] = entry->val;
479       Ffuncall (countof (args), args);
480       /* Has FUNCTION removed the entry? */
481       if (oldlen > Dynarr_length (rt->entries)
482           && i < Dynarr_length (rt->entries)
483           && (first != entry->first || last != entry->last))
484         goto again;
485       }
486
487   return Qnil;
488 }
489
490 \f
491 /************************************************************************/
492 /*                         Range table read syntax                      */
493 /************************************************************************/
494
495 static int
496 rangetab_data_validate (Lisp_Object keyword, Lisp_Object value,
497                         Error_behavior errb)
498 {
499   Lisp_Object rest;
500
501   /* #### should deal with errb */
502   EXTERNAL_LIST_LOOP (rest, value)
503     {
504       Lisp_Object range = XCAR (rest);
505       rest = XCDR (rest);
506       if (!CONSP (rest))
507         signal_simple_error ("Invalid list format", value);
508       if (!INTP (range) && !CHARP (range)
509           && !(CONSP (range) && CONSP (XCDR (range))
510                && NILP (XCDR (XCDR (range)))
511                && (INTP (XCAR (range)) || CHARP (XCAR (range)))
512                && (INTP (XCAR (XCDR (range))) || CHARP (XCAR (XCDR (range))))))
513         signal_simple_error ("Invalid range format", range);
514     }
515
516   return 1;
517 }
518
519 static Lisp_Object
520 rangetab_instantiate (Lisp_Object data)
521 {
522   Lisp_Object rangetab = Fmake_range_table ();
523
524   if (!NILP (data))
525     {
526       data = Fcar (Fcdr (data)); /* skip over 'data keyword */
527       while (!NILP (data))
528         {
529           Lisp_Object range = Fcar (data);
530           Lisp_Object val = Fcar (Fcdr (data));
531
532           data = Fcdr (Fcdr (data));
533           if (CONSP (range))
534             Fput_range_table (Fcar (range), Fcar (Fcdr (range)), val,
535                               rangetab);
536           else
537             Fput_range_table (range, range, val, rangetab);
538         }
539     }
540
541   return rangetab;
542 }
543
544 \f
545 /************************************************************************/
546 /*                         Unified range tables                         */
547 /************************************************************************/
548
549 /* A "unified range table" is a format for storing range tables
550    as contiguous blocks of memory.  This is used by the regexp
551    code, which needs to use range tables to properly handle []
552    constructs in the presence of extended characters but wants to
553    store an entire compiled pattern as a contiguous block of memory.
554
555    Unified range tables are designed so that they can be placed
556    at an arbitrary (possibly mis-aligned) place in memory.
557    (Dealing with alignment is a pain in the ass.)
558
559    WARNING: No provisions for garbage collection are currently made.
560    This means that there must not be any Lisp objects in a unified
561    range table that need to be marked for garbage collection.
562    Good candidates for objects that can go into a range table are
563
564    -- numbers and characters (do not need to be marked)
565    -- nil, t (marked elsewhere)
566    -- charsets and coding systems (automatically marked because
567                                    they are in a marked list,
568                                    and can't be removed)
569
570    Good but slightly less so:
571
572    -- symbols (could be uninterned, but that is not likely)
573
574    Somewhat less good:
575
576    -- buffers, frames, devices (could get deleted)
577
578
579    It is expected that you work with range tables in the normal
580    format and then convert to unified format when you are done
581    making modifications.  As such, no functions are provided
582    for modifying a unified range table.  The only operations
583    you can do to unified range tables are
584
585    -- look up a value
586    -- retrieve all the ranges in an iterative fashion
587
588 */
589
590 /* The format of a unified range table is as follows:
591
592    -- The first byte contains the number of bytes to skip to find the
593       actual start of the table.  This deals with alignment constraints,
594       since the table might want to go at any arbitrary place in memory.
595    -- The next three bytes contain the number of bytes to skip (from the
596       *first* byte) to find the stuff after the table.  It's stored in
597       little-endian format because that's how God intended things.  We don't
598       necessarily start the stuff at the very end of the table because
599       we want to have at least ALIGNOF (EMACS_INT) extra space in case
600       we have to move the range table around. (It appears that some
601       architectures don't maintain alignment when reallocing.)
602    -- At the prescribed offset is a struct unified_range_table, containing
603       some number of `struct range_table_entry' entries. */
604
605 struct unified_range_table
606 {
607   int nentries;
608   struct range_table_entry first;
609 };
610
611 /* Return size in bytes needed to store the data in a range table. */
612
613 int
614 unified_range_table_bytes_needed (Lisp_Object rangetab)
615 {
616   return (sizeof (struct range_table_entry) *
617           (Dynarr_length (XRANGE_TABLE (rangetab)->entries) - 1) +
618           sizeof (struct unified_range_table) +
619           /* ALIGNOF a struct may be too big. */
620           /* We have four bytes for the size numbers, and an extra
621              four or eight bytes for making sure we get the alignment
622              OK. */
623           ALIGNOF (EMACS_INT) + 4);
624 }
625
626 /* Convert a range table into unified format and store in DEST,
627    which must be able to hold the number of bytes returned by
628    range_table_bytes_needed(). */
629
630 void
631 unified_range_table_copy_data (Lisp_Object rangetab, void *dest)
632 {
633   /* We cast to the above structure rather than just casting to
634      char * and adding sizeof(int), because that will lead to
635      mis-aligned data on the Alpha machines. */
636   struct unified_range_table *un;
637   range_table_entry_dynarr *rted = XRANGE_TABLE (rangetab)->entries;
638   int total_needed = unified_range_table_bytes_needed (rangetab);
639   void *new_dest = ALIGN_PTR ((char *) dest + 4, ALIGNOF (EMACS_INT));
640
641   * (char *) dest = (char) ((char *) new_dest - (char *) dest);
642   * ((unsigned char *) dest + 1) = total_needed & 0xFF;
643   total_needed >>= 8;
644   * ((unsigned char *) dest + 2) = total_needed & 0xFF;
645   total_needed >>= 8;
646   * ((unsigned char *) dest + 3) = total_needed & 0xFF;
647   un = (struct unified_range_table *) new_dest;
648   un->nentries = Dynarr_length (rted);
649   memcpy (&un->first, Dynarr_atp (rted, 0),
650           sizeof (struct range_table_entry) * Dynarr_length (rted));
651 }
652
653 /* Return number of bytes actually used by a unified range table. */
654
655 int
656 unified_range_table_bytes_used (void *unrangetab)
657 {
658   return ((* ((unsigned char *) unrangetab + 1))
659           + ((* ((unsigned char *) unrangetab + 2)) << 8)
660           + ((* ((unsigned char *) unrangetab + 3)) << 16));
661 }
662
663 /* Make sure the table is aligned, and move it around if it's not. */
664 static void
665 align_the_damn_table (void *unrangetab)
666 {
667   void *cur_dest = (char *) unrangetab + * (char *) unrangetab;
668 #if LONGBITS == 64
669   if ((((long) cur_dest) & 7) != 0)
670 #else
671   if ((((int) cur_dest) & 3) != 0)
672 #endif
673     {
674       int count = (unified_range_table_bytes_used (unrangetab) - 4
675                    - ALIGNOF (EMACS_INT));
676       /* Find the proper location, just like above. */
677       void *new_dest = ALIGN_PTR ((char *) unrangetab + 4,
678                                   ALIGNOF (EMACS_INT));
679       /* memmove() works in the presence of overlapping data. */
680       memmove (new_dest, cur_dest, count);
681       * (char *) unrangetab = (char) ((char *) new_dest - (char *) unrangetab);
682     }
683 }
684
685 /* Look up a value in a unified range table. */
686
687 Lisp_Object
688 unified_range_table_lookup (void *unrangetab, EMACS_INT pos,
689                             Lisp_Object default_)
690 {
691   void *new_dest;
692   struct unified_range_table *un;
693
694   align_the_damn_table (unrangetab);
695   new_dest = (char *) unrangetab + * (char *) unrangetab;
696   un = (struct unified_range_table *) new_dest;
697
698   return get_range_table (pos, un->nentries, &un->first, default_);
699 }
700
701 /* Return number of entries in a unified range table. */
702
703 int
704 unified_range_table_nentries (void *unrangetab)
705 {
706   void *new_dest;
707   struct unified_range_table *un;
708
709   align_the_damn_table (unrangetab);
710   new_dest = (char *) unrangetab + * (char *) unrangetab;
711   un = (struct unified_range_table *) new_dest;
712   return un->nentries;
713 }
714
715 /* Return the OFFSETth range (counting from 0) in UNRANGETAB. */
716 void
717 unified_range_table_get_range (void *unrangetab, int offset,
718                                EMACS_INT *min, EMACS_INT *max,
719                                Lisp_Object *val)
720 {
721   void *new_dest;
722   struct unified_range_table *un;
723   struct range_table_entry *tab;
724
725   align_the_damn_table (unrangetab);
726   new_dest = (char *) unrangetab + * (char *) unrangetab;
727   un = (struct unified_range_table *) new_dest;
728
729   assert (offset >= 0 && offset < un->nentries);
730   tab = (&un->first) + offset;
731   *min = tab->first;
732   *max = tab->last;
733   *val = tab->val;
734 }
735
736 \f
737 /************************************************************************/
738 /*                            Initialization                            */
739 /************************************************************************/
740
741 void
742 syms_of_rangetab (void)
743 {
744   INIT_LRECORD_IMPLEMENTATION (range_table);
745
746   defsymbol (&Qrange_tablep, "range-table-p");
747   defsymbol (&Qrange_table, "range-table");
748
749   DEFSUBR (Frange_table_p);
750   DEFSUBR (Fmake_range_table);
751   DEFSUBR (Fcopy_range_table);
752   DEFSUBR (Fget_range_table);
753   DEFSUBR (Fput_range_table);
754   DEFSUBR (Fremove_range_table);
755   DEFSUBR (Fclear_range_table);
756   DEFSUBR (Fmap_range_table);
757 }
758
759 void
760 structure_type_create_rangetab (void)
761 {
762   struct structure_type *st;
763
764   st = define_structure_type (Qrange_table, 0, rangetab_instantiate);
765
766   define_structure_type_keyword (st, Qdata, rangetab_data_validate);
767 }