1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-26 15:21:51 -08:00
emacs/src/chartab.c
Eli Zaretskii 14fe7b530d Backport revisions 2011-04-24T05:30:24Z!eggert@cs.ucla.edu..2011-04-25T19:40:22Z!eggert@cs.ucla.edu (inclusive) from trunk (bug#8623)
The next log entry shows the actual changes by Paul Eggert.

 Fix a problem with aliasing and vector headers.
 GCC 4.6.0 optimizes based on type-based alias analysis.  For
 example, if b is of type struct buffer * and v of type struct
 Lisp_Vector *, then gcc -O2 was incorrectly assuming that &b->size
 != &v->size, and therefore "v->size = 1; b->size = 2; return
 v->size;" must therefore return 1.  This assumption is incorrect
 for Emacs, since it type-puns struct Lisp_Vector * with many other
 types.  To fix this problem, this patch adds a new type struct
 vector_header that documents the constraints on layout of vectors
 and pseudovectors, and helps optimizing compilers not get fooled
 by Emacs's type punning.  It also adds the macros XSETTYPED_PVECTYPE
 XSETTYPED_PSEUDOVECTOR, TYPED_PSEUDOVECTORP, for similar reasons.
 src/lisp.h (XVECTOR_SIZE): New convenience macro.  All previous uses of
 XVECTOR (foo)->size replaced to use this macro, to avoid the hassle
 of writing XVECTOR (foo)->header.size.
 src/lisp.h: Say "vectorlike header" rather than "vector header.
 (struct vectorlike_header): Rename from struct vector_header.
 (XVECTORLIKE_HEADER_SIZE): Renamed from XVECTOR_HEADER_SIZE.
 All uses changed.
 (XVECTOR_HEADER_SIZE): New macro, for use in XSETPSEUDOVECTOR.
 (XSETTYPED_PVECTYPE): New macro, specifying the name of the size
 member.
 (XSETPVECTYPE): Rewrite in terms of new macro.
 (XSETPVECTYPESIZE): New macro, specifying both type and size.
 This is a bit clearer, and further avoids the possibility of
 undesirable aliasing.
 (XSETTYPED_PSEUDOVECTOR): New macro, specifying the size.
 (XSETPSEUDOVECTOR): Rewrite in terms of XSETTYPED_PSEUDOVECTOR
 and XVECTOR_HEADER_SIZE.
 (XSETSUBR): Rewrite in terms of XSETTYPED_PSEUDOVECTOR and XSIZE,
 since Lisp_Subr is a special case (no "next" field).
 (ASIZE): Rewrite in terms of XVECTOR_SIZE.
 (struct vector_header): New type.
 (TYPED_PSEUDOVECTORP): New macro, also specifying the C type of the
 object, to help avoid aliasing.
 (PSEUDOVECTORP): Rewrite in terms of TYPED_PSEUDOVECTORP.
 (SUBRP): Likewise, since Lisp_Subr is a special case.
 src/lisp.h (struct Lisp_Vector, struct Lisp_Char_Table):
 (struct Lisp_Sub_Char_Table, struct Lisp_Bool_Vector):
 (struct Lisp_Hash_Table): Combine first two members into a single
 struct vector_header member.  All uses of "size" and "next" members
 changed to be "header.size" and "header.next".
 src/buffer.h (struct buffer): Likewise.
 src/font.h (struct font_spec, struct font_entity, struct font): Likewise.
 src/frame.h (struct frame): Likewise.
 src/process.h (struct Lisp_Process): Likewise.
 src/termhooks.h (struct terminal): Likewise.
 src/window.c (struct save_window_data, struct saved_window): Likewise.
 src/window.h (struct window): Likewise.
 src/alloc.c (allocate_buffer, Fmake_bool_vector, allocate_pseudovector):
 Use XSETPVECTYPESIZE, not XSETPVECTYPE, to avoid aliasing problems.
 src/buffer.c (init_buffer_once): Likewise.
 src/lread.c (defsubr): Use XSETTYPED_PVECTYPE, since Lisp_Subr is a
 special case.
 src/process.c (Fformat_network_address): Use local var for size,
 for brevity.
 src/fns.c (vector): Remove; this old hack is no longer needed.
 src/bytecode.c (exec_byte_code): Don't use XVECTOR before CHECK_VECTOR.
2011-05-09 05:59:23 -04:00

1001 lines
27 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* chartab.c -- char-table support
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
National Institute of Advanced Industrial Science and Technology (AIST)
Registration Number H13PRO009
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <setjmp.h>
#include "lisp.h"
#include "character.h"
#include "charset.h"
#include "ccl.h"
/* 64/16/32/128 */
/* Number of elements in Nth level char-table. */
const int chartab_size[4] =
{ (1 << CHARTAB_SIZE_BITS_0),
(1 << CHARTAB_SIZE_BITS_1),
(1 << CHARTAB_SIZE_BITS_2),
(1 << CHARTAB_SIZE_BITS_3) };
/* Number of characters each element of Nth level char-table
covers. */
static const int chartab_chars[4] =
{ (1 << (CHARTAB_SIZE_BITS_1 + CHARTAB_SIZE_BITS_2 + CHARTAB_SIZE_BITS_3)),
(1 << (CHARTAB_SIZE_BITS_2 + CHARTAB_SIZE_BITS_3)),
(1 << CHARTAB_SIZE_BITS_3),
1 };
/* Number of characters (in bits) each element of Nth level char-table
covers. */
static const int chartab_bits[4] =
{ (CHARTAB_SIZE_BITS_1 + CHARTAB_SIZE_BITS_2 + CHARTAB_SIZE_BITS_3),
(CHARTAB_SIZE_BITS_2 + CHARTAB_SIZE_BITS_3),
CHARTAB_SIZE_BITS_3,
0 };
#define CHARTAB_IDX(c, depth, min_char) \
(((c) - (min_char)) >> chartab_bits[(depth)])
DEFUN ("make-char-table", Fmake_char_table, Smake_char_table, 1, 2, 0,
doc: /* Return a newly created char-table, with purpose PURPOSE.
Each element is initialized to INIT, which defaults to nil.
PURPOSE should be a symbol. If it has a `char-table-extra-slots'
property, the property's value should be an integer between 0 and 10
that specifies how many extra slots the char-table has. Otherwise,
the char-table has no extra slot. */)
(register Lisp_Object purpose, Lisp_Object init)
{
Lisp_Object vector;
Lisp_Object n;
int n_extras;
int size;
CHECK_SYMBOL (purpose);
n = Fget (purpose, Qchar_table_extra_slots);
if (NILP (n))
n_extras = 0;
else
{
CHECK_NATNUM (n);
n_extras = XINT (n);
if (n_extras > 10)
args_out_of_range (n, Qnil);
}
size = VECSIZE (struct Lisp_Char_Table) - 1 + n_extras;
vector = Fmake_vector (make_number (size), init);
XSETPVECTYPE (XVECTOR (vector), PVEC_CHAR_TABLE);
XCHAR_TABLE (vector)->parent = Qnil;
XCHAR_TABLE (vector)->purpose = purpose;
XSETCHAR_TABLE (vector, XCHAR_TABLE (vector));
return vector;
}
static Lisp_Object
make_sub_char_table (int depth, int min_char, Lisp_Object defalt)
{
Lisp_Object table;
int size = VECSIZE (struct Lisp_Sub_Char_Table) - 1 + chartab_size[depth];
table = Fmake_vector (make_number (size), defalt);
XSETPVECTYPE (XVECTOR (table), PVEC_SUB_CHAR_TABLE);
XSUB_CHAR_TABLE (table)->depth = make_number (depth);
XSUB_CHAR_TABLE (table)->min_char = make_number (min_char);
return table;
}
static Lisp_Object
char_table_ascii (Lisp_Object table)
{
Lisp_Object sub;
sub = XCHAR_TABLE (table)->contents[0];
if (! SUB_CHAR_TABLE_P (sub))
return sub;
sub = XSUB_CHAR_TABLE (sub)->contents[0];
if (! SUB_CHAR_TABLE_P (sub))
return sub;
return XSUB_CHAR_TABLE (sub)->contents[0];
}
static Lisp_Object
copy_sub_char_table (Lisp_Object table)
{
Lisp_Object copy;
int depth = XINT (XSUB_CHAR_TABLE (table)->depth);
int min_char = XINT (XSUB_CHAR_TABLE (table)->min_char);
Lisp_Object val;
int i;
copy = make_sub_char_table (depth, min_char, Qnil);
/* Recursively copy any sub char-tables. */
for (i = 0; i < chartab_size[depth]; i++)
{
val = XSUB_CHAR_TABLE (table)->contents[i];
if (SUB_CHAR_TABLE_P (val))
XSUB_CHAR_TABLE (copy)->contents[i] = copy_sub_char_table (val);
else
XSUB_CHAR_TABLE (copy)->contents[i] = val;
}
return copy;
}
Lisp_Object
copy_char_table (Lisp_Object table)
{
Lisp_Object copy;
int size = XCHAR_TABLE (table)->header.size & PSEUDOVECTOR_SIZE_MASK;
int i;
copy = Fmake_vector (make_number (size), Qnil);
XSETPVECTYPE (XVECTOR (copy), PVEC_CHAR_TABLE);
XCHAR_TABLE (copy)->defalt = XCHAR_TABLE (table)->defalt;
XCHAR_TABLE (copy)->parent = XCHAR_TABLE (table)->parent;
XCHAR_TABLE (copy)->purpose = XCHAR_TABLE (table)->purpose;
for (i = 0; i < chartab_size[0]; i++)
XCHAR_TABLE (copy)->contents[i]
= (SUB_CHAR_TABLE_P (XCHAR_TABLE (table)->contents[i])
? copy_sub_char_table (XCHAR_TABLE (table)->contents[i])
: XCHAR_TABLE (table)->contents[i]);
XCHAR_TABLE (copy)->ascii = char_table_ascii (copy);
size -= VECSIZE (struct Lisp_Char_Table) - 1;
for (i = 0; i < size; i++)
XCHAR_TABLE (copy)->extras[i] = XCHAR_TABLE (table)->extras[i];
XSETCHAR_TABLE (copy, XCHAR_TABLE (copy));
return copy;
}
static Lisp_Object
sub_char_table_ref (Lisp_Object table, int c)
{
struct Lisp_Sub_Char_Table *tbl = XSUB_CHAR_TABLE (table);
int depth = XINT (tbl->depth);
int min_char = XINT (tbl->min_char);
Lisp_Object val;
val = tbl->contents[CHARTAB_IDX (c, depth, min_char)];
if (SUB_CHAR_TABLE_P (val))
val = sub_char_table_ref (val, c);
return val;
}
Lisp_Object
char_table_ref (Lisp_Object table, int c)
{
struct Lisp_Char_Table *tbl = XCHAR_TABLE (table);
Lisp_Object val;
if (ASCII_CHAR_P (c))
{
val = tbl->ascii;
if (SUB_CHAR_TABLE_P (val))
val = XSUB_CHAR_TABLE (val)->contents[c];
}
else
{
val = tbl->contents[CHARTAB_IDX (c, 0, 0)];
if (SUB_CHAR_TABLE_P (val))
val = sub_char_table_ref (val, c);
}
if (NILP (val))
{
val = tbl->defalt;
if (NILP (val) && CHAR_TABLE_P (tbl->parent))
val = char_table_ref (tbl->parent, c);
}
return val;
}
static Lisp_Object
sub_char_table_ref_and_range (Lisp_Object table, int c, int *from, int *to, Lisp_Object defalt)
{
struct Lisp_Sub_Char_Table *tbl = XSUB_CHAR_TABLE (table);
int depth = XINT (tbl->depth);
int min_char = XINT (tbl->min_char);
int chartab_idx = CHARTAB_IDX (c, depth, min_char), idx;
Lisp_Object val;
val = tbl->contents[chartab_idx];
if (SUB_CHAR_TABLE_P (val))
val = sub_char_table_ref_and_range (val, c, from, to, defalt);
else if (NILP (val))
val = defalt;
idx = chartab_idx;
while (idx > 0 && *from < min_char + idx * chartab_chars[depth])
{
Lisp_Object this_val;
c = min_char + idx * chartab_chars[depth] - 1;
idx--;
this_val = tbl->contents[idx];
if (SUB_CHAR_TABLE_P (this_val))
this_val = sub_char_table_ref_and_range (this_val, c, from, to, defalt);
else if (NILP (this_val))
this_val = defalt;
if (! EQ (this_val, val))
{
*from = c + 1;
break;
}
}
while (((c = (chartab_idx + 1) * chartab_chars[depth])
< chartab_chars[depth - 1])
&& (c += min_char) <= *to)
{
Lisp_Object this_val;
chartab_idx++;
this_val = tbl->contents[chartab_idx];
if (SUB_CHAR_TABLE_P (this_val))
this_val = sub_char_table_ref_and_range (this_val, c, from, to, defalt);
else if (NILP (this_val))
this_val = defalt;
if (! EQ (this_val, val))
{
*to = c - 1;
break;
}
}
return val;
}
/* Return the value for C in char-table TABLE. Shrink the range *FROM
and *TO to cover characters (containing C) that have the same value
as C. It is not assured that the values of (*FROM - 1) and (*TO +
1) are different from that of C. */
Lisp_Object
char_table_ref_and_range (Lisp_Object table, int c, int *from, int *to)
{
struct Lisp_Char_Table *tbl = XCHAR_TABLE (table);
int chartab_idx = CHARTAB_IDX (c, 0, 0), idx;
Lisp_Object val;
val = tbl->contents[chartab_idx];
if (*from < 0)
*from = 0;
if (*to < 0)
*to = MAX_CHAR;
if (SUB_CHAR_TABLE_P (val))
val = sub_char_table_ref_and_range (val, c, from, to, tbl->defalt);
else if (NILP (val))
val = tbl->defalt;
idx = chartab_idx;
while (*from < idx * chartab_chars[0])
{
Lisp_Object this_val;
c = idx * chartab_chars[0] - 1;
idx--;
this_val = tbl->contents[idx];
if (SUB_CHAR_TABLE_P (this_val))
this_val = sub_char_table_ref_and_range (this_val, c, from, to,
tbl->defalt);
else if (NILP (this_val))
this_val = tbl->defalt;
if (! EQ (this_val, val))
{
*from = c + 1;
break;
}
}
while (*to >= (chartab_idx + 1) * chartab_chars[0])
{
Lisp_Object this_val;
chartab_idx++;
c = chartab_idx * chartab_chars[0];
this_val = tbl->contents[chartab_idx];
if (SUB_CHAR_TABLE_P (this_val))
this_val = sub_char_table_ref_and_range (this_val, c, from, to,
tbl->defalt);
else if (NILP (this_val))
this_val = tbl->defalt;
if (! EQ (this_val, val))
{
*to = c - 1;
break;
}
}
return val;
}
static void
sub_char_table_set (Lisp_Object table, int c, Lisp_Object val)
{
struct Lisp_Sub_Char_Table *tbl = XSUB_CHAR_TABLE (table);
int depth = XINT ((tbl)->depth);
int min_char = XINT ((tbl)->min_char);
int i = CHARTAB_IDX (c, depth, min_char);
Lisp_Object sub;
if (depth == 3)
tbl->contents[i] = val;
else
{
sub = tbl->contents[i];
if (! SUB_CHAR_TABLE_P (sub))
{
sub = make_sub_char_table (depth + 1,
min_char + i * chartab_chars[depth], sub);
tbl->contents[i] = sub;
}
sub_char_table_set (sub, c, val);
}
}
Lisp_Object
char_table_set (Lisp_Object table, int c, Lisp_Object val)
{
struct Lisp_Char_Table *tbl = XCHAR_TABLE (table);
if (ASCII_CHAR_P (c)
&& SUB_CHAR_TABLE_P (tbl->ascii))
{
XSUB_CHAR_TABLE (tbl->ascii)->contents[c] = val;
}
else
{
int i = CHARTAB_IDX (c, 0, 0);
Lisp_Object sub;
sub = tbl->contents[i];
if (! SUB_CHAR_TABLE_P (sub))
{
sub = make_sub_char_table (1, i * chartab_chars[0], sub);
tbl->contents[i] = sub;
}
sub_char_table_set (sub, c, val);
if (ASCII_CHAR_P (c))
tbl->ascii = char_table_ascii (table);
}
return val;
}
static void
sub_char_table_set_range (Lisp_Object *table, int depth, int min_char, int from, int to, Lisp_Object val)
{
int max_char = min_char + chartab_chars[depth] - 1;
if (depth == 3 || (from <= min_char && to >= max_char))
*table = val;
else
{
int i;
unsigned j;
depth++;
if (! SUB_CHAR_TABLE_P (*table))
*table = make_sub_char_table (depth, min_char, *table);
if (from < min_char)
from = min_char;
if (to > max_char)
to = max_char;
i = CHARTAB_IDX (from, depth, min_char);
j = CHARTAB_IDX (to, depth, min_char);
min_char += chartab_chars[depth] * i;
for (j++; i < j; i++, min_char += chartab_chars[depth])
sub_char_table_set_range (XSUB_CHAR_TABLE (*table)->contents + i,
depth, min_char, from, to, val);
}
}
Lisp_Object
char_table_set_range (Lisp_Object table, int from, int to, Lisp_Object val)
{
struct Lisp_Char_Table *tbl = XCHAR_TABLE (table);
Lisp_Object *contents = tbl->contents;
int i;
if (from == to)
char_table_set (table, from, val);
else
{
unsigned lim = to / chartab_chars[0] + 1;
for (i = CHARTAB_IDX (from, 0, 0); i < lim; i++)
sub_char_table_set_range (contents + i, 0, i * chartab_chars[0],
from, to, val);
if (ASCII_CHAR_P (from))
tbl->ascii = char_table_ascii (table);
}
return val;
}
DEFUN ("char-table-subtype", Fchar_table_subtype, Schar_table_subtype,
1, 1, 0,
doc: /*
Return the subtype of char-table CHAR-TABLE. The value is a symbol. */)
(Lisp_Object char_table)
{
CHECK_CHAR_TABLE (char_table);
return XCHAR_TABLE (char_table)->purpose;
}
DEFUN ("char-table-parent", Fchar_table_parent, Schar_table_parent,
1, 1, 0,
doc: /* Return the parent char-table of CHAR-TABLE.
The value is either nil or another char-table.
If CHAR-TABLE holds nil for a given character,
then the actual applicable value is inherited from the parent char-table
\(or from its parents, if necessary). */)
(Lisp_Object char_table)
{
CHECK_CHAR_TABLE (char_table);
return XCHAR_TABLE (char_table)->parent;
}
DEFUN ("set-char-table-parent", Fset_char_table_parent, Sset_char_table_parent,
2, 2, 0,
doc: /* Set the parent char-table of CHAR-TABLE to PARENT.
Return PARENT. PARENT must be either nil or another char-table. */)
(Lisp_Object char_table, Lisp_Object parent)
{
Lisp_Object temp;
CHECK_CHAR_TABLE (char_table);
if (!NILP (parent))
{
CHECK_CHAR_TABLE (parent);
for (temp = parent; !NILP (temp); temp = XCHAR_TABLE (temp)->parent)
if (EQ (temp, char_table))
error ("Attempt to make a chartable be its own parent");
}
XCHAR_TABLE (char_table)->parent = parent;
return parent;
}
DEFUN ("char-table-extra-slot", Fchar_table_extra_slot, Schar_table_extra_slot,
2, 2, 0,
doc: /* Return the value of CHAR-TABLE's extra-slot number N. */)
(Lisp_Object char_table, Lisp_Object n)
{
CHECK_CHAR_TABLE (char_table);
CHECK_NUMBER (n);
if (XINT (n) < 0
|| XINT (n) >= CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (char_table)))
args_out_of_range (char_table, n);
return XCHAR_TABLE (char_table)->extras[XINT (n)];
}
DEFUN ("set-char-table-extra-slot", Fset_char_table_extra_slot,
Sset_char_table_extra_slot,
3, 3, 0,
doc: /* Set CHAR-TABLE's extra-slot number N to VALUE. */)
(Lisp_Object char_table, Lisp_Object n, Lisp_Object value)
{
CHECK_CHAR_TABLE (char_table);
CHECK_NUMBER (n);
if (XINT (n) < 0
|| XINT (n) >= CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (char_table)))
args_out_of_range (char_table, n);
return XCHAR_TABLE (char_table)->extras[XINT (n)] = value;
}
DEFUN ("char-table-range", Fchar_table_range, Schar_table_range,
2, 2, 0,
doc: /* Return the value in CHAR-TABLE for a range of characters RANGE.
RANGE should be nil (for the default value),
a cons of character codes (for characters in the range), or a character code. */)
(Lisp_Object char_table, Lisp_Object range)
{
Lisp_Object val;
CHECK_CHAR_TABLE (char_table);
if (EQ (range, Qnil))
val = XCHAR_TABLE (char_table)->defalt;
else if (INTEGERP (range))
val = CHAR_TABLE_REF (char_table, XINT (range));
else if (CONSP (range))
{
int from, to;
CHECK_CHARACTER_CAR (range);
CHECK_CHARACTER_CDR (range);
val = char_table_ref_and_range (char_table, XINT (XCAR (range)),
&from, &to);
/* Not yet implemented. */
}
else
error ("Invalid RANGE argument to `char-table-range'");
return val;
}
DEFUN ("set-char-table-range", Fset_char_table_range, Sset_char_table_range,
3, 3, 0,
doc: /* Set the value in CHAR-TABLE for a range of characters RANGE to VALUE.
RANGE should be t (for all characters), nil (for the default value),
a cons of character codes (for characters in the range),
or a character code. Return VALUE. */)
(Lisp_Object char_table, Lisp_Object range, Lisp_Object value)
{
CHECK_CHAR_TABLE (char_table);
if (EQ (range, Qt))
{
int i;
XCHAR_TABLE (char_table)->ascii = value;
for (i = 0; i < chartab_size[0]; i++)
XCHAR_TABLE (char_table)->contents[i] = value;
}
else if (EQ (range, Qnil))
XCHAR_TABLE (char_table)->defalt = value;
else if (INTEGERP (range))
char_table_set (char_table, XINT (range), value);
else if (CONSP (range))
{
CHECK_CHARACTER_CAR (range);
CHECK_CHARACTER_CDR (range);
char_table_set_range (char_table,
XINT (XCAR (range)), XINT (XCDR (range)), value);
}
else
error ("Invalid RANGE argument to `set-char-table-range'");
return value;
}
DEFUN ("set-char-table-default", Fset_char_table_default,
Sset_char_table_default, 3, 3, 0,
doc: /*
This function is obsolete and has no effect. */)
(Lisp_Object char_table, Lisp_Object ch, Lisp_Object value)
{
return Qnil;
}
/* Look up the element in TABLE at index CH, and return it as an
integer. If the element is not a character, return CH itself. */
int
char_table_translate (Lisp_Object table, int ch)
{
Lisp_Object value;
value = Faref (table, make_number (ch));
if (! CHARACTERP (value))
return ch;
return XINT (value);
}
static Lisp_Object
optimize_sub_char_table (Lisp_Object table, Lisp_Object test)
{
struct Lisp_Sub_Char_Table *tbl = XSUB_CHAR_TABLE (table);
int depth = XINT (tbl->depth);
Lisp_Object elt, this;
int i, optimizable;
elt = XSUB_CHAR_TABLE (table)->contents[0];
if (SUB_CHAR_TABLE_P (elt))
elt = XSUB_CHAR_TABLE (table)->contents[0]
= optimize_sub_char_table (elt, test);
optimizable = SUB_CHAR_TABLE_P (elt) ? 0 : 1;
for (i = 1; i < chartab_size[depth]; i++)
{
this = XSUB_CHAR_TABLE (table)->contents[i];
if (SUB_CHAR_TABLE_P (this))
this = XSUB_CHAR_TABLE (table)->contents[i]
= optimize_sub_char_table (this, test);
if (optimizable
&& (NILP (test) ? NILP (Fequal (this, elt)) /* defaults to `equal'. */
: EQ (test, Qeq) ? !EQ (this, elt) /* Optimize `eq' case. */
: NILP (call2 (test, this, elt))))
optimizable = 0;
}
return (optimizable ? elt : table);
}
DEFUN ("optimize-char-table", Foptimize_char_table, Soptimize_char_table,
1, 2, 0,
doc: /* Optimize CHAR-TABLE.
TEST is the comparison function used to decide whether two entries are
equivalent and can be merged. It defaults to `equal'. */)
(Lisp_Object char_table, Lisp_Object test)
{
Lisp_Object elt;
int i;
CHECK_CHAR_TABLE (char_table);
for (i = 0; i < chartab_size[0]; i++)
{
elt = XCHAR_TABLE (char_table)->contents[i];
if (SUB_CHAR_TABLE_P (elt))
XCHAR_TABLE (char_table)->contents[i]
= optimize_sub_char_table (elt, test);
}
/* Reset the `ascii' cache, in case it got optimized away. */
XCHAR_TABLE (char_table)->ascii = char_table_ascii (char_table);
return Qnil;
}
/* Map C_FUNCTION or FUNCTION over TABLE (top or sub char-table),
calling it for each character or group of characters that share a
value. RANGE is a cons (FROM . TO) specifying the range of target
characters, VAL is a value of FROM in TABLE, DEFAULT_VAL is the
default value of the char-table, PARENT is the parent of the
char-table.
ARG is passed to C_FUNCTION when that is called.
It returns the value of last character covered by TABLE (not the
value inheritted from the parent), and by side-effect, the car part
of RANGE is updated to the minimum character C where C and all the
following characters in TABLE have the same value. */
static Lisp_Object
map_sub_char_table (void (*c_function) (Lisp_Object, Lisp_Object, Lisp_Object),
Lisp_Object function, Lisp_Object table, Lisp_Object arg, Lisp_Object val,
Lisp_Object range, Lisp_Object default_val, Lisp_Object parent)
{
/* Pointer to the elements of TABLE. */
Lisp_Object *contents;
/* Depth of TABLE. */
int depth;
/* Minimum and maxinum characters covered by TABLE. */
int min_char, max_char;
/* Number of characters covered by one element of TABLE. */
int chars_in_block;
int from = XINT (XCAR (range)), to = XINT (XCDR (range));
int i, c;
if (SUB_CHAR_TABLE_P (table))
{
struct Lisp_Sub_Char_Table *tbl = XSUB_CHAR_TABLE (table);
depth = XINT (tbl->depth);
contents = tbl->contents;
min_char = XINT (tbl->min_char);
max_char = min_char + chartab_chars[depth - 1] - 1;
}
else
{
depth = 0;
contents = XCHAR_TABLE (table)->contents;
min_char = 0;
max_char = MAX_CHAR;
}
chars_in_block = chartab_chars[depth];
if (to < max_char)
max_char = to;
/* Set I to the index of the first element to check. */
if (from <= min_char)
i = 0;
else
i = (from - min_char) / chars_in_block;
for (c = min_char + chars_in_block * i; c <= max_char;
i++, c += chars_in_block)
{
Lisp_Object this = contents[i];
int nextc = c + chars_in_block;
if (SUB_CHAR_TABLE_P (this))
{
if (to >= nextc)
XSETCDR (range, make_number (nextc - 1));
val = map_sub_char_table (c_function, function, this, arg,
val, range, default_val, parent);
}
else
{
if (NILP (this))
this = default_val;
if (!EQ (val, this))
{
int different_value = 1;
if (NILP (val))
{
if (! NILP (parent))
{
Lisp_Object temp = XCHAR_TABLE (parent)->parent;
/* This is to get a value of FROM in PARENT
without checking the parent of PARENT. */
XCHAR_TABLE (parent)->parent = Qnil;
val = CHAR_TABLE_REF (parent, from);
XCHAR_TABLE (parent)->parent = temp;
XSETCDR (range, make_number (c - 1));
val = map_sub_char_table (c_function, function,
parent, arg, val, range,
XCHAR_TABLE (parent)->defalt,
XCHAR_TABLE (parent)->parent);
if (EQ (val, this))
different_value = 0;
}
}
if (! NILP (val) && different_value)
{
XSETCDR (range, make_number (c - 1));
if (EQ (XCAR (range), XCDR (range)))
{
if (c_function)
(*c_function) (arg, XCAR (range), val);
else
call2 (function, XCAR (range), val);
}
else
{
if (c_function)
(*c_function) (arg, range, val);
else
call2 (function, range, val);
}
}
val = this;
from = c;
XSETCAR (range, make_number (c));
}
}
XSETCDR (range, make_number (to));
}
return val;
}
/* Map C_FUNCTION or FUNCTION over TABLE, calling it for each
character or group of characters that share a value.
ARG is passed to C_FUNCTION when that is called. */
void
map_char_table (void (*c_function) (Lisp_Object, Lisp_Object, Lisp_Object), Lisp_Object function, Lisp_Object table, Lisp_Object arg)
{
Lisp_Object range, val;
struct gcpro gcpro1, gcpro2, gcpro3;
range = Fcons (make_number (0), make_number (MAX_CHAR));
GCPRO3 (table, arg, range);
val = XCHAR_TABLE (table)->ascii;
if (SUB_CHAR_TABLE_P (val))
val = XSUB_CHAR_TABLE (val)->contents[0];
val = map_sub_char_table (c_function, function, table, arg, val, range,
XCHAR_TABLE (table)->defalt,
XCHAR_TABLE (table)->parent);
/* If VAL is nil and TABLE has a parent, we must consult the parent
recursively. */
while (NILP (val) && ! NILP (XCHAR_TABLE (table)->parent))
{
Lisp_Object parent = XCHAR_TABLE (table)->parent;
Lisp_Object temp = XCHAR_TABLE (parent)->parent;
int from = XINT (XCAR (range));
/* This is to get a value of FROM in PARENT without checking the
parent of PARENT. */
XCHAR_TABLE (parent)->parent = Qnil;
val = CHAR_TABLE_REF (parent, from);
XCHAR_TABLE (parent)->parent = temp;
val = map_sub_char_table (c_function, function, parent, arg, val, range,
XCHAR_TABLE (parent)->defalt,
XCHAR_TABLE (parent)->parent);
table = parent;
}
if (! NILP (val))
{
if (EQ (XCAR (range), XCDR (range)))
{
if (c_function)
(*c_function) (arg, XCAR (range), val);
else
call2 (function, XCAR (range), val);
}
else
{
if (c_function)
(*c_function) (arg, range, val);
else
call2 (function, range, val);
}
}
UNGCPRO;
}
DEFUN ("map-char-table", Fmap_char_table, Smap_char_table,
2, 2, 0,
doc: /*
Call FUNCTION for each character in CHAR-TABLE that has non-nil value.
FUNCTION is called with two arguments--a key and a value.
The key is a character code or a cons of character codes specifying a
range of characters that have the same value. */)
(Lisp_Object function, Lisp_Object char_table)
{
CHECK_CHAR_TABLE (char_table);
map_char_table (NULL, function, char_table, char_table);
return Qnil;
}
static void
map_sub_char_table_for_charset (void (*c_function) (Lisp_Object, Lisp_Object),
Lisp_Object function, Lisp_Object table, Lisp_Object arg,
Lisp_Object range, struct charset *charset,
unsigned from, unsigned to)
{
struct Lisp_Sub_Char_Table *tbl = XSUB_CHAR_TABLE (table);
int depth = XINT (tbl->depth);
int c, i;
if (depth < 3)
for (i = 0, c = XINT (tbl->min_char); i < chartab_size[depth];
i++, c += chartab_chars[depth])
{
Lisp_Object this;
this = tbl->contents[i];
if (SUB_CHAR_TABLE_P (this))
map_sub_char_table_for_charset (c_function, function, this, arg,
range, charset, from, to);
else
{
if (! NILP (XCAR (range)))
{
XSETCDR (range, make_number (c - 1));
if (c_function)
(*c_function) (arg, range);
else
call2 (function, range, arg);
}
XSETCAR (range, Qnil);
}
}
else
for (i = 0, c = XINT (tbl->min_char); i < chartab_size[depth]; i++, c ++)
{
Lisp_Object this;
unsigned code;
this = tbl->contents[i];
if (NILP (this)
|| (charset
&& (code = ENCODE_CHAR (charset, c),
(code < from || code > to))))
{
if (! NILP (XCAR (range)))
{
XSETCDR (range, make_number (c - 1));
if (c_function)
(*c_function) (arg, range);
else
call2 (function, range, arg);
XSETCAR (range, Qnil);
}
}
else
{
if (NILP (XCAR (range)))
XSETCAR (range, make_number (c));
}
}
}
/* Support function for `map-charset-chars'. Map C_FUNCTION or
FUNCTION over TABLE, calling it for each character or a group of
succeeding characters that have non-nil value in TABLE. TABLE is a
"mapping table" or a "deunifier table" of a certain charset.
If CHARSET is not NULL (this is the case that `map-charset-chars'
is called with non-nil FROM-CODE and TO-CODE), it is a charset who
owns TABLE, and the function is called only on a character in the
range FROM and TO. FROM and TO are not character codes, but code
points of a character in CHARSET.
This function is called in these two cases:
(1) A charset has a mapping file name in :map property.
(2) A charset has an upper code space in :offset property and a
mapping file name in :unify-map property. In this case, this
function is called only for characters in the Unicode code space.
Characters in upper code space are handled directly in
map_charset_chars. */
void
map_char_table_for_charset (void (*c_function) (Lisp_Object, Lisp_Object),
Lisp_Object function, Lisp_Object table, Lisp_Object arg,
struct charset *charset,
unsigned from, unsigned to)
{
Lisp_Object range;
int c, i;
struct gcpro gcpro1;
range = Fcons (Qnil, Qnil);
GCPRO1 (range);
for (i = 0, c = 0; i < chartab_size[0]; i++, c += chartab_chars[0])
{
Lisp_Object this;
this = XCHAR_TABLE (table)->contents[i];
if (SUB_CHAR_TABLE_P (this))
map_sub_char_table_for_charset (c_function, function, this, arg,
range, charset, from, to);
else
{
if (! NILP (XCAR (range)))
{
XSETCDR (range, make_number (c - 1));
if (c_function)
(*c_function) (arg, range);
else
call2 (function, range, arg);
}
XSETCAR (range, Qnil);
}
}
if (! NILP (XCAR (range)))
{
XSETCDR (range, make_number (c - 1));
if (c_function)
(*c_function) (arg, range);
else
call2 (function, range, arg);
}
UNGCPRO;
}
void
syms_of_chartab (void)
{
defsubr (&Smake_char_table);
defsubr (&Schar_table_parent);
defsubr (&Schar_table_subtype);
defsubr (&Sset_char_table_parent);
defsubr (&Schar_table_extra_slot);
defsubr (&Sset_char_table_extra_slot);
defsubr (&Schar_table_range);
defsubr (&Sset_char_table_range);
defsubr (&Sset_char_table_default);
defsubr (&Soptimize_char_table);
defsubr (&Smap_char_table);
}