/* fns3.c Copyright (C) 1989-95 Codemist Ltd */ /* * Basic functions part 3. * A concentration on hashtable, vector and array access code here. */ /* Signature: 0aca95f3 07-Mar-2000 */ #include <stdarg.h> #include <string.h> #include <ctype.h> #include "machine.h" #include "tags.h" #include "cslerror.h" #include "externs.h" #include "read.h" #include "entries.h" #include "arith.h" #ifdef COMMON #include "clsyms.h" #endif #ifdef TIMEOUT #include "timeout.h" #endif /* * Common Lisp and Standard Lisp disagree about vector sizes. Common * Lisp counts the number of elements in a vector (with make-simple-vector * and vector-bound) while Standard Lisp uses the value n, where the * vector concerned will accept index values from 0 to n (inclusive) * (mkvect and upbv). I provide the Standard Lisp versions always, so I * can use them even in Common Lisp mode. The vectors are exactly the * same - it is just a different way of talking about them. */ Lisp_Object Lmkvect(Lisp_Object nil, Lisp_Object n) { int32 n1; if (!is_fixnum(n)) return aerror1("mkvect", n); n1 = int_of_fixnum(n) << 2; n1 += 4; /* Oh! What an abomination! Standard Lisp allocated 0::n, */ /* Common allocates n items */ if (n1 < 0) return aerror1("mkvect", n); return onevalue(getvector_init(n1+4, nil)); } #ifdef COMMON Lisp_Object Lmksimplevec(Lisp_Object nil, Lisp_Object n) { int32 n1; if (!is_fixnum(n)) return aerror1("make-simple-vector", n); n1 = int_of_fixnum(n) << 2; if (n1 < 0) return aerror1("make-simple-vector", n); return onevalue(getvector_init(n1+4, nil)); } #endif /* * This one creates a "structure" tagged vector. */ Lisp_Object Lmkevect(Lisp_Object nil, Lisp_Object n) { int32 n1; if (!is_fixnum(n)) return aerror1("mkevect", n); n1 = int_of_fixnum(n) << 2; #ifndef COMMON n1 += 4; /* Oh! What an abomination! Standard Lisp allocated 0::n, */ /* Common allocates n items */ #endif if (n1 < 0) return aerror1("mkevect", n); n = getvector_init(n1+4, nil); errexit(); vechdr(n) ^= (TYPE_SIMPLE_VEC ^ TYPE_STRUCTURE); return onevalue(n); } /* * The following creates a sort of vector where the first 3 items are * lisp pointers, and the remainder may be filled with binary stuff (which * is not byte-flipped or anything on garbage collection, and so is possibly * fairly unsafe). It is intended for internal or experimental use only. */ Lisp_Object Lmkxvect(Lisp_Object nil, Lisp_Object n) { int32 n1; if (!is_fixnum(n)) return aerror1("mkxvect", n); n1 = int_of_fixnum(n) << 2; #ifndef COMMON n1 += 4; /* Oh! What an abomination! Standard Lisp allocated 0::n, */ /* Common allocates n items */ #endif if (n1 < 12) return aerror1("mkxvect", n); n = getvector_init(n1+4, nil); errexit(); vechdr(n) ^= (TYPE_SIMPLE_VEC ^ TYPE_MIXED1); return onevalue(n); } static int primep(int32 n) /* * Used to ensure that the body of a hash-table has a size that is prime. * Assumes odd number provided on entry, and that the value to be checked * is not especially large. Since it will have been handed in as a * fixnum it is at worst 2^28 or so, so brute-force should be OK. */ { int32 i; for (i=3; i*i<=n; i+=2) if (n%i == 0) return 0; return 1; } #define HASH_CHUNK_SIZE (((unsigned32)1) << (PAGE_BITS-3)) #define HASH_CHUNK_WORDS (HASH_CHUNK_SIZE/4) static Lisp_Object get_hash_vector(int32 n) { Lisp_Object v, nil = C_nil; /* * A major ugliness here is that I need to support hash tables that are * larger than the largest simple vector I can use (as limited by * CSL_PAGE_SIZE). To achieve this I will handle such huge tables using * a vector of vectors, with the higher level vector tagged as a STRUCT, * and the lower level vectors each sized at around 1/8 of a CSL page. The * modest chunk size is intended to limit the packing lossage I will see at * page boundaries. HASH_CHUNK_SIZE is the size (in bytes) used for data in * each such hash chunk. */ if (n > CSL_PAGE_SIZE/2) /* A fairly arbitrary cut-off */ { int32 chunks = (n + HASH_CHUNK_SIZE - 1)/HASH_CHUNK_SIZE; int32 i; v = getvector_init(12+4*chunks, nil); errexit(); /* The next line tags the top level vector as a struct */ vechdr(v) ^= (TYPE_SIMPLE_VEC ^ TYPE_STRUCTURE); elt(v, 1) = fixnum_of_int(n); for (i=0; i<chunks; i++) { Lisp_Object v1; push(v); /* * In general the last of these chunks will be larger that it really needs * to be, but keeping all chunks the same standard size seems a useful * simplification right at present! */ v1 = getvector_init(HASH_CHUNK_SIZE+4, SPID_HASH0); pop(v); errexit(); elt(v, i+2) = v1; } } else v = getvector_init(n, SPID_HASH0); return v; } Lisp_Object MS_CDECL Lmkhash(Lisp_Object nil, int nargs, ...) /* * size suggests how many items can be inserted before re-hashing * occurs. flavour is 0, 1, 2, 3 or 4 corresponding to hash tables * that use EQ, EQL, EQUAL, EQUALS or EQUALP. growth is a floating point * value suggesting how much to grow by when rehashing is needed. * * NB. Hash tables of type 0 or 1 (using EQ or EQL) will need special * treatment by the garbage collector - in particular since the garbage * collector can relocate values the entire contents of the tables will * need rearrangement. Tables of types 2, 3 and 4 use hash-codes that are * more expensive to compute, but which are insensitive to memory addresses * and the like, and so so NOT need special treatment. Tables that need * re-hashing on GC are kept on a special list, known to the GC. Even type * 2, 3 and 4 hash tables are rehashed when a core image is re-loaded, since * the hash function may be byte-order sensitive. * * If flavour is not a number it might be a dotted pair (hashfn . eqfn) * where hashfn is a user-provided function to compute hash values (which * will actually be permitted to be anything at all, since I will then * hash the output again as if hashing under EQL - but I expect that really * I expect numeric hash values), and eqfn is a function used to compare * items. [this facility may not be implemented at first] */ { va_list a; int32 size1, size2; Lisp_Object v, v1, size, flavour, growth; argcheck(nargs, 3, "mkhash"); va_start(a, nargs); size = va_arg(a, Lisp_Object); flavour = va_arg(a, Lisp_Object); growth = va_arg(a, Lisp_Object); va_end(a); if (!is_fixnum(size)) return aerror1("mkhash", size); size1 = int_of_fixnum(size); if (size1 <= 0) return aerror1("mkhash", size); if (!is_fixnum(flavour) && !consp(flavour)) return aerror1("mkhash", flavour); /* * I will start with a table with around 1.5 times as many slots as * were requested, and will ensure that the size is a prime. I also add * in a little more so that people who ask for VERY small tables get * given ones that are not mindlessly tiny. */ size2 = (size1 + (size1/2) + 4) | 1; while (!primep(size2)) size2 += 2; size2 = size2<<2; push(growth); /* * Huge hash tables will be stored (internally) in chunks. */ v = get_hash_vector(2*size2+8); errexitn(1); push(v); v1 = getvector_init(24, nil); pop2(v, growth); errexit(); push3(v, v1, growth); v = ncons(v); errexitn(3); /* * I keep a list of all hash tables in a weak list-head. The use of ncons * followed by a RPLACD is because I want xx_hash_tables to be the ONLY * possible pointer to that bit of list. Even if I garbage collect while * updating it. Note that I also re-hash every garbage collection if the * hash function is a user-provided one. This is a matter of security * since it will often not really be necessary, since it will be a bit hard * for user hash functions to depend on absolute memory addresses. But all * rehashing costs is some time, I hope. */ if (flavour == fixnum_of_int(0) || flavour == fixnum_of_int(1) || !is_fixnum(flavour)) { qcdr(v) = eq_hash_tables; eq_hash_tables = v; } else { qcdr(v) = equal_hash_tables; equal_hash_tables = v; } pop3(growth, v1, v); elt(v, 0) = elt(v1, 0) = flavour; elt(v1, 1) = fixnum_of_int(0); elt(v1, 2) = size; elt(v1, 3) = growth; elt(v1, 4) = v; vechdr(v1) ^= (TYPE_SIMPLE_VEC ^ TYPE_HASH); return onevalue(v1); } /* * I use the following while combining parts of a structure to compute a * hash value. It may not be totally wonderful (I would need to soak my mind * in pseudo-random numbers to do a really good job) but it will probably * serve for now. */ static unsigned32 update_hash(unsigned32 prev, unsigned32 data) { prev = prev ^ data; prev = prev ^ (prev >> 11); prev = prev ^ ((prev & 0xffffff) * 169); return prev & 0x7fffffff; } static unsigned32 hash_eql(Lisp_Object key) /* * Must return same code for two eql numbers. This is remarkably * painfull! I would like the value to be insensitive to fine details * of the machine I am running on. */ { if (is_bfloat(key)) { int32 h = type_of_header(flthdr(key)); /* * For floating point values I look at the binary representation of * the number. */ union nasty { double fp; unsigned32 i[2]; } nasty_union; nasty_union.i[0] = nasty_union.i[1] = 0; switch (h) { #ifdef COMMON case TYPE_SINGLE_FLOAT: nasty_union.fp = (double)single_float_val(key); break; #endif case TYPE_DOUBLE_FLOAT: nasty_union.fp = double_float_val(key); break; #ifdef COMMON case TYPE_LONG_FLOAT: nasty_union.fp = (double)long_float_val(key); break; #endif default: nasty_union.fp = 0.0; } /* * The following line is OK on any one computer, but will generate values * that are not portable across machines with different floating point * representation. This is not too important when the hash value is only * used with my built-in implementation of hash tables, since I arrange * to re-hash everything when an image file is re-loaded (as well as on * any garbage collection), so non-portable calculation here is corrected * for automatically. */ return update_hash(nasty_union.i[0], nasty_union.i[1]); } else if (is_numbers(key)) { Header h = numhdr(key); unsigned32 r; int n; switch (type_of_header(h)) { case TYPE_BIGNUM: n = length_of_header(h); n = (n>>2) - 2; /* last index into the data */ r = update_hash(1, (unsigned32)h); /* * This mat be overkill - for very long bignums it is possibly a waste to * walk over ALL the digits when computing a hash value - I could do well * enough just looking at a few. But I still feel safer using all of them. */ while (n >= 0) { r = update_hash(r, bignum_digits(key)[n]); n--; } return r; #ifdef COMMON case TYPE_RATNUM: case TYPE_COMPLEX_NUM: return update_hash(hash_eql(numerator(key)), hash_eql(denominator(key))); #endif default: return 0x12345678; /* unknown type of number? */ } } /* * For all things OTHER than messy numbers I just hand back the * representation of the object as a C pointer. Well, I scramble it a bit * because otherwise too often Lisp objects only differ in their low order * bits. */ else return update_hash(1, (unsigned32)key); } static unsigned32 hash_cl_equal(Lisp_Object key, CSLbool descend) /* * This function is the one used hashing things under EQUAL, and note * that Common Lisp expects that EQUAL will NOT descend vectors or * structures, so this code had better not. But it is supposed to * descend path-names and it must treat non-simple strings and bitvectors * as if they were like ordinary strings and bitvectors. If descend is * false this will not descend through lists. */ { unsigned32 r = 1, c; Lisp_Object nil, w; int32 len; #ifdef COMMON int32 bitoff; #endif unsigned char *data; Header ha; #ifdef CHECK_STACK if (check_stack(__FILE__,__LINE__)) { err_printf("Stack too deep in hash calculation\n"); my_exit(EXIT_FAILURE); } #endif for (;;) { switch (TAG_BITS & (int32)key) { case TAG_CONS: if (key == C_nil || !descend) return r; r = update_hash(r, hash_cl_equal(qcar(key), YES)); nil = C_nil; if (exception_pending()) return 0; key = qcdr(key); continue; case TAG_SYMBOL: if (key == C_nil) return r; key = get_pname(key); nil = C_nil; if (exception_pending()) return 0; r = update_hash(r, 1); /* makes name & string hash differently */ /* Drop through, because the pname is a string */ case TAG_VECTOR: { ha = vechdr(key); len = type_of_header(ha); /* * I need to treat strings and bitvectors here specially since in those * cases (and for pathnames) I must inspect the vector contents, while * in other cases I must not. */ if (len == TYPE_STRING) { len = length_of_header(ha) - 4; data = &ucelt(key, 0); goto hash_as_string; } #ifdef COMMON else if (header_of_bitvector(ha)) { len = length_of_header(ha); len = (len - 5)*8 + ((ha & 0x380) >> 7) + 1; bitoff = 0; data = &ucelt(key, 0); goto hash_as_bitvector; } #endif else if (len == TYPE_ARRAY) { /* * Arrays are fun here! I need to pick up the special case of character * vectors and bit vectors and make them compute the same hash value as the * simple case of the same thing. */ w = elt(key, 0); if (w == string_char_sym) ha = 0; #ifdef COMMON else if (w == bit_symbol) ha = 1; #endif else return update_hash(r, (unsigned32)key); w = elt(key, 1); /* List of dimensions */ if (!consp(w) || consp(qcdr(w))) /* 1 dim or more? */ return update_hash(r, (unsigned32)key); len = int_of_fixnum(qcar(w)); /* This is the length */ w = elt(key, 5); /* Fill pointer */ if (is_fixnum(w)) len = int_of_fixnum(w); w = elt(key, 3); /* displace adjustment */ key = elt(key, 2); /* vector holding the actual data */ data = &ucelt(key, 0); #ifdef COMMON if (ha) { bitoff = int_of_fixnum(w); goto hash_as_bitvector; } #endif data += int_of_fixnum(w); goto hash_as_string; } #ifdef COMMON /* * Common Lisp demands that pathname structures be compared and hashed in * a way that is expected to look at their contents. Here I just descend * all components of the pathname. */ else if (len == TYPE_STRUCTURE && elt(key, 0) == pathname_symbol && descend) { len = doubleword_align_up(length_of_header(ha)); while ((len -= 4) != 0) { Lisp_Object ea = *((Lisp_Object *)((char *)key + len - TAG_VECTOR)); r = update_hash(r, hash_cl_equal(ea, YES)); nil = C_nil; if (exception_pending()) return 0; } return r; } #endif else return update_hash(r, (unsigned32)key); } case TAG_ODDS: if (is_bps(key)) { data = (unsigned char *)data_of_bps(key); /* I treat bytecode things as strings here */ len = length_of_header(*(Header *)(data - 4)); goto hash_as_string; } else return update_hash(r, (unsigned32)key); case TAG_BOXFLOAT: /* * The "case TAG_BOXFLOAT:" above is not logically necessary, but at least * one release of a Silicon Graphics C compiler seems to miscompile this * function without it (when optimised). It is as if it seems the masking * with TAG_BITS in the switch() and therefore knows that there is just a * limited range of possibilities, so it omits the normal range-check one * would use before a table-branch. But it then leaves the branch table * that it generates NOT padded with the final case (TAG_BOXFLOAT) that is * needed, so when a floating point values does arise the code goes into the * yonder and usually crashes. */ default: return hash_eql(key); } hash_as_string: /* Here len is the length of the string data structure, excluding header */ while (len > 0) { c = data[--len]; r = update_hash(r, c); } return r; #ifdef COMMON hash_as_bitvector: /* here len is the number of bits to scan, and bitoff is a BIT offset */ len += bitoff; while (len > bitoff) { len--; c = data[len >> 3] & (1 << (len & 7)); if (c != 0) c = 1; r = update_hash(r, c); } return r; #endif } } static unsigned32 hash_equal(Lisp_Object key) /* * This function is the one used hashing things under the Standard Lisp * version of EQUAL, which descends vectors but is still sensitive to * case and which views different types of numbers as different. I will * make it view displaced or fill-pointered vectors as equivalent to the * corresponding simple vectors: I am pretty well obliged to do that for * strings and bitvectors so it seems polite to do the same for general * vectors (which are the only other ones I support!). */ { unsigned32 r = 1, c; Lisp_Object nil, w; int32 type, len, offset = 0; unsigned char *data; Header ha; #ifdef CHECK_STACK if (check_stack(__FILE__,__LINE__)) { err_printf("Stack too deep in hash calculation\n"); my_exit(EXIT_FAILURE); } #endif for (;;) { switch (TAG_BITS & (int32)key) { case TAG_CONS: if (key == C_nil) return r; r = update_hash(r, hash_equal(qcar(key))); nil = C_nil; if (exception_pending()) return 0; key = qcdr(key); continue; case TAG_SYMBOL: if (key == C_nil) return r; key = get_pname(key); nil = C_nil; if (exception_pending()) return 0; r = update_hash(r, 1); /* Drop through, because the pname is a string */ case TAG_VECTOR: { ha = vechdr(key); type = type_of_header(ha); len = length_of_header(ha) - 4; /* counts in bytes here */ /* * First I will separate off the two important cases of strings and bitvectors */ if (type == TYPE_STRING) { data = &ucelt(key, 0); goto hash_as_string; } #ifdef COMMON else if (header_of_bitvector(ha)) { len = (len - 1)*8 + ((ha & 0x380) >> 7) + 1; offset = 0; data = &ucelt(key, 0); goto hash_as_bitvector; } #endif #ifdef COMMON /* * Common Lisp demands that pathname structures be compared and hashed in * a way that is expected to look at their contents. Here I just descend * all components of the pathname. */ if (len == TYPE_STRUCTURE && elt(key, 0) != pathname_symbol) return update_hash(r, (unsigned32)key); #endif /* * Now I will look for an array that is in fact just a vector. */ if (type == TYPE_ARRAY) { w = elt(key, 0); if (w == string_char_sym) ha = 0; #ifdef COMMON else if (w == bit_symbol) ha = 1; #endif else ha = 2; w = elt(key, 1); /* List of dimensions */ if (consp(w) && !consp(qcdr(w))) /* 1 dim or not? */ { len = int_of_fixnum(qcar(w)); /* This is the length */ w = elt(key, 5); /* Fill pointer */ if (is_fixnum(w)) len = int_of_fixnum(w); w = elt(key, 3); /* displace adjustment */ key = elt(key, 2); /* vector holding the data */ switch (ha) { case 0: data = &ucelt(key, int_of_fixnum(w)); goto hash_as_string; #ifdef COMMON case 1: data = &ucelt(key, 0); offset = int_of_fixnum(w); goto hash_as_bitvector; #endif default: /* /* The code here can CRASH if asked to hash a general array that * has been represented in chunks because it has over 32K elements. */ ha = vechdr(key); offset = int_of_fixnum(w); break; } } } /* * Now in the case that I had a non-simple vector I have reset key to point * to the vector containing the true data, ha to the header of same and * len is the length that I want to use. offset is an offset into the vector. * For simple vectors all the same variables are set up (and offset will be * zero). All cases of strings and bitvectors should have been dealt with * so the only vectors containing binary are things like "file" structures, * and I do not expect them to hash portably. */ if (vector_holds_binary(ha)) return update_hash(r, (unsigned32)key); offset = 4*offset; if (is_mixed_header(ha)) { while (len > 16) { unsigned32 ea = *(unsigned32 *)((char *)key + offset + len - TAG_VECTOR - 4); len -= 4; r = update_hash(r, ea); } } while ((len -= 4) != 0) { Lisp_Object ea = *((Lisp_Object *)((char *)key + offset + len - TAG_VECTOR)); r = update_hash(r, hash_equal(ea)); nil = C_nil; if (exception_pending()) return 0; } return r; } case TAG_ODDS: if (is_bps(key)) { data = (unsigned char *)data_of_bps(key); /* I treat bytecode things as strings here */ len = length_of_header(*(Header *)(data - 4)); goto hash_as_string; } else return update_hash(r, (unsigned32)key); case TAG_BOXFLOAT: default:/* The default case here mainly covers numbers */ return hash_eql(key); } hash_as_string: /* Here len is the length of the string data structure, excluding header */ while (len > 0) { c = data[--len]; r = update_hash(r, c); } return r; #ifdef COMMON hash_as_bitvector: /* here len is the number of bits to scan, and offset is a BIT offset */ len += offset; while (len > offset) { len--; c = data[len >> 3] & (1 << (len & 7)); if (c != 0) c = 1; r = update_hash(r, c); } return r; #endif } } static unsigned32 hash_equalp(Lisp_Object key) /* * This function is the one used hashing things under the Common Lisp * version of EQUALP, which descends vectors but not structs (except * pathnames), which is case-insensitive and which views numbers of * different types but similar values (eg 1 and 1.0) as EQUALP). */ { unsigned32 r = 1, c; Lisp_Object nil, w; int32 type, len, offset = 0; unsigned char *data; Header ha; #ifdef CHECK_STACK if (check_stack(__FILE__,__LINE__)) { err_printf("Stack too deep in hash calculation\n"); my_exit(EXIT_FAILURE); } #endif for (;;) { switch (TAG_BITS & (int32)key) { case TAG_CONS: if (key == C_nil) return r; r = update_hash(r, hash_equalp(qcar(key))); nil = C_nil; if (exception_pending()) return 0; key = qcdr(key); continue; case TAG_SYMBOL: if (key == C_nil) return r; key = get_pname(key); nil = C_nil; if (exception_pending()) return 0; r = update_hash(r, 1); /* Drop through, because the pname is a string */ case TAG_VECTOR: { ha = vechdr(key); type = type_of_header(ha); len = length_of_header(ha) - 4; /* counts in bytes here */ /* * First I will separate off the two important cases of strings and bitvectors */ if (type == TYPE_STRING) { data = &ucelt(key, 0); goto hash_as_string; } #ifdef COMMON else if (header_of_bitvector(ha)) { len = (len - 1)*8 + ((ha & 0x380) >> 7) + 1; offset = 0; data = &ucelt(key, 0); goto hash_as_bitvector; } #endif #ifdef COMMON /* * Common Lisp demands that pathname structures be compared and hashed in * a way that is expected to look at their contents. Here I just descend * all components of the pathname. Other structs are not descended. */ if (len == TYPE_STRUCTURE && elt(key, 0) != pathname_symbol) return update_hash(r, (unsigned32)key); #endif /* * Now I will look for an array that is in fact just a vector. */ if (type == TYPE_ARRAY) { w = elt(key, 0); if (w == string_char_sym) ha = 0; #ifdef COMMON else if (w == bit_symbol) ha = 1; #endif else ha = 2; w = elt(key, 1); /* List of dimensions */ if (consp(w) && !consp(qcdr(w))) /* 1 dim or not? */ { len = int_of_fixnum(qcar(w)); /* This is the length */ w = elt(key, 5); /* Fill pointer */ if (is_fixnum(w)) len = int_of_fixnum(w); w = elt(key, 3); /* displace adjustment */ key = elt(key, 2); /* vector holding the data */ switch (ha) { case 0: data = &ucelt(key, int_of_fixnum(w)); goto hash_as_string; #ifdef COMMON case 1: data = &ucelt(key, 0); offset = int_of_fixnum(w); goto hash_as_bitvector; #endif default: /* /* Trouble if a general array with over 32K elements gets to here */ ha = vechdr(key); offset = int_of_fixnum(w); break; } } } /* * Now in the case that I had a non-simple vector I have reset key to point * to the vector containing the true data, ha to the header of same and * len is the length that I want to use. offset is an offset into the vector. * For simple vectors all the same variables are set up (and offset will be * zero). All cases of strings and bitvectors should have been dealt with * so the only vectors containing binary are things like "file" structures, * and I do not expect them to hash portably. */ if (vector_holds_binary(ha)) return update_hash(r, (unsigned32)key); offset = 4*offset; if (is_mixed_header(ha)) { while (len > 16) { unsigned32 ea = *(unsigned32 *)((char *)key + offset + len - TAG_VECTOR - 4); len -= 4; r = update_hash(r, ea); } } while ((len -= 4) != 0) { Lisp_Object ea = *((Lisp_Object *)((char *)key + offset + len - TAG_VECTOR)); r = update_hash(r, hash_equalp(ea)); nil = C_nil; if (exception_pending()) return 0; } return r; } case TAG_ODDS: if (is_bps(key)) { data = (unsigned char *)data_of_bps(key); /* I treat bytecode things as strings here */ len = length_of_header(*(Header *)(data - 4)); goto hash_as_string; } else if (is_char(key)) key = pack_char(0, 0, tolower(code_of_char(key))); return update_hash(r, (unsigned32)key); case TAG_BOXFLOAT: default:/* The default case here mainly covers numbers */ if (is_float(key)) { key = rational(key); /* painful expense */ nil = C_nil; if (exception_pending()) return 0; } #ifdef COMMON if (is_numbers(key)) { switch (type_of_header(numhdr(key))) { case TYPE_RATNUM: case TYPE_COMPLEX_NUM: return update_hash(hash_equalp(numerator(key)), hash_equalp(denominator(key))); default: break; } } #endif return hash_eql(key); } /* * Note that I scan the elements of a string or bitvector in the same order * that I would process a general vector of the same length, and I adjust the * vector contents to its generic representation before updating the hash * value. For strings I fold to lower case. */ hash_as_string: /* Here len is the length of the string data structure, excluding header */ while (len > 0) { c = tolower(data[--len]); r = update_hash(r, update_hash(1, pack_char(0, 0, c))); } return r; #ifdef COMMON hash_as_bitvector: /* here len is the number of bits to scan, and offset is a BIT offset */ len += offset; while (len > offset) { len--; c = data[len >> 3] & (1 << (len & 7)); if (c != 0) c = 1; r = update_hash(r, update_hash(1, fixnum_of_int(c))); } return r; #endif } } static unsigned32 hashcode; static int hashsize, hashoffset, hashgap; static CSLbool large_hash_table; #define words_in_hash_table(v) \ (((large_hash_table ? int_of_fixnum(elt(v, 1)) : \ length_of_header(vechdr(v))) - 8) >> 2) #define ht_elt(v, n) \ (*(large_hash_table ? \ &elt(elt((v), 2+(n)/HASH_CHUNK_WORDS), (n)%HASH_CHUNK_WORDS) : \ &elt((v), (n)))) Lisp_Object MS_CDECL Lget_hash(Lisp_Object nil, int nargs, ...) { int32 size, p, flavour = -1, hashstride, nprobes; va_list a; Lisp_Object v, key, tab, dflt; argcheck(nargs, 3, "gethash"); va_start(a, nargs); key = va_arg(a, Lisp_Object); tab = va_arg(a, Lisp_Object); dflt = va_arg(a, Lisp_Object); va_end(a); if (!is_vector(tab) || type_of_header(vechdr(tab)) != TYPE_HASH) return aerror1("gethash", tab); v = elt(tab, 0); /* /* The code here needs to allow for user-specified hash functions */ if (is_fixnum(v)) flavour = int_of_fixnum(v); switch (flavour) { default: return aerror1("gethash", cons(v, tab)); case 0: hashcode = update_hash(1, (unsigned32)key); break; case 1: hashcode = hash_eql(key); /* can never fail */ break; case 2: push3(key, tab, dflt); hashcode = hash_cl_equal(key, YES); pop3(dflt, tab, key); errexit(); break; case 3: push3(key, tab, dflt); hashcode = hash_equal(key); pop3(dflt, tab, key); errexit(); break; case 4: push3(key, tab, dflt); hashcode = hash_equalp(key); pop3(dflt, tab, key); errexit(); break; } v = elt(tab, 4); large_hash_table = type_of_header(vechdr(v)) == TYPE_STRUCTURE; hashsize = size = words_in_hash_table(v); p = (hashcode % (unsigned32)(size >> 1)) << 1; /* * I want to take my single 32-bit hash value and produce a secondary * hash value that is a stride for the search. I can just take the * remainder by 1 less than the hash table size (and add 1 so I get * a non-zero stride). */ hashstride = (1 + (hashcode % (unsigned32)((size >> 1)-1))) << 1; hashgap = -1; for (nprobes=0;nprobes<size;nprobes++) { Lisp_Object q = ht_elt(v, p+1); CSLbool cf; if (q == SPID_HASH0) { mv_2 = nil; work_0 = v; hashoffset = p; return nvalues(dflt, 2); } if (q == SPID_HASH1) { hashgap = p; cf = NO; /* vacated slot */ } /* /* again user-specified hash functions need insertion here */ else switch (flavour) { case 0: cf = (q == key); break; case 1: cf = eql(q, key); break; case 2: push4(key, tab, dflt, v); if (q == key) cf = YES; else cf = cl_equal(q, key); pop4(v, dflt, tab, key); errexit(); break; case 3: push4(key, tab, dflt, v); if (q == key) cf = YES; else cf = equal(q, key); pop4(v, dflt, tab, key); errexit(); break; case 4: push4(key, tab, dflt, v); if (q == key) cf = YES; else cf = equalp(q, key); pop4(v, dflt, tab, key); errexit(); break; } if (cf) { mv_2 = lisp_true; work_0 = v; hashoffset = p; return nvalues(ht_elt(v, p+2), 2); } p = p + hashstride; if (p >= size) p = p - size; } return aerror("too many probes in hash look-up"); } static void reinsert_hash(Lisp_Object v, int32 size, int32 flavour, Lisp_Object key, Lisp_Object val) { int32 p; unsigned32 hcode, hstride; Lisp_Object nil = C_nil; switch (flavour) { case 0: hcode = update_hash(1, (unsigned32)key); break; case 1: hcode = hash_eql(key); /* can never fail */ break; case 2: push3(key, v, val); hcode = hash_cl_equal(key, YES); pop3(val, v, key); errexitv(); break; case 3: push3(key, v, val); hcode = hash_equal(key); pop3(val, v, key); errexitv(); break; case 4: push3(key, v, val); hcode = hash_equalp(key); pop3(val, v, key); errexitv(); break; } p = (hcode % (unsigned32)(size >> 1)) << 1; hstride = (1 + (hcode % (unsigned32)((size >> 1)-1))) << 1; /* * When I re-insert the item into the table life is especially easy - * I know it is not there already and I know I will be able to find a * gap to put it in! So I just have to look for a gap - no comparisons * are needed. */ large_hash_table = type_of_header(vechdr(v)) == TYPE_STRUCTURE; for (;;) { Lisp_Object q = ht_elt(v, p+1); if (q == SPID_HASH0 || q == SPID_HASH1) { ht_elt(v, p+1) = key; ht_elt(v, p+2) = val; return; } p = p + hstride; if (p >= size) p = p - size; } } #define REHASH_CYCLES 2 #define REHASH_AT_ONE_GO 64 void rehash_this_table(Lisp_Object v) /* * Hash tables where the hash function depends on absolute memory addresses * will sometimes need rehashing - I do this by removing items from the * table one at a time and re-inserting them. This does not guarantee that * the table is left in a perfect state, but for modest loading will be * adequate. I reason that if I extract 64 (say) items at a time and * then re-insert them then (especially for smallish tables) I have a * better chance of things ending up in the ideal place. The problem is that * items that have not yet been moved may be sitting in places where a * re-hashed item ought to go. The effect will be that the newly re-inserted * item sees a clash and moves to a second-choice position. When the other * item is (later on) processed it will then vacate the place I would have * liked to use, leaving a "tombstone" marker behind. If at the end of all * re-hashing there are too many tombstones left around lookup performance * in the table will degrade. I attempt to counter this effect by performing * the whole re-hashing procedure several times. But I have neither analysed * nore measured what happens! I will do so if practical applications show * up serious trouble here. */ { int32 size, i, j, flavour, many; CSLbool old_large = large_hash_table; Lisp_Object pendkey[REHASH_AT_ONE_GO], pendval[REHASH_AT_ONE_GO]; flavour = int_of_fixnum(elt(v, 0)); /* Done this way always */ large_hash_table = type_of_header(vechdr(v)) == TYPE_STRUCTURE; size = words_in_hash_table(v); /* * The cycle count here is something I may want to experiment with. */ for (i=0; i<REHASH_CYCLES; i++) { /* * Change all slots in the table that are empty just because something has * been deleted to indicate that they are truly not in use. This makes some * items inaccessible by normal hash searches (because a void will be placed * earlier than them on a search trajectory) but this does not matter because * everything is about to be taken out of the table and reinserted properly. */ for (j=0; j<size; j+=2) if (ht_elt(v, j+1) == SPID_HASH1) ht_elt(v, j+1) = SPID_HASH0; many = 0; for (j=0; j<size; j+=2) { Lisp_Object key = ht_elt(v, j+1), val = ht_elt(v, j+2); if (key == SPID_HASH0 || key == SPID_HASH1) continue; pendkey[many] = key; pendval[many++] = val; ht_elt(v, j+1) = SPID_HASH1; ht_elt(v, j+2) = SPID_HASH0; if (many >= REHASH_AT_ONE_GO) { while (many > 0) { many--; reinsert_hash(v, size, flavour, pendkey[many], pendval[many]); } } } while (--many >= 0) reinsert_hash(v, size, flavour, pendkey[many], pendval[many]); } large_hash_table = old_large; } Lisp_Object Lmaphash(Lisp_Object nil, Lisp_Object fn, Lisp_Object tab) /* * There is a big worry here if the table is re-hashed because of * a garbage collection while I am in the middle of things. To * avoid utter shambles I will make a copy of the vector early * on and work from that. */ { int32 size, i; Lisp_Object v, v1; if (!is_vector(tab) || type_of_header(vechdr(tab)) != TYPE_HASH) return aerror1("maphash", tab); v = elt(tab, 4); large_hash_table = type_of_header(vechdr(v)) == TYPE_STRUCTURE; size = words_in_hash_table(v)*4+8; push2(fn, tab); v1 = get_hash_vector(size); pop2(tab, fn); v = elt(tab, 4); size = (size - 4) >> 2; for (i=0; i<size; i++) ht_elt(v1, i) = ht_elt(v, i); for (i=1; i<size; i+=2) { Lisp_Object key = ht_elt(v1, i), val = ht_elt(v1, i+1); if (key == SPID_HASH0 || key == SPID_HASH1) continue; push2(v1, fn); Lapply2(nil, 3, fn, key, val); pop2(fn, v1); errexit(); } return onevalue(nil); } Lisp_Object Lhashcontents(Lisp_Object nil, Lisp_Object tab) /* * There is a big worry here if the table is re-hashed because of * a garbage collection while I am in the middle of things. To * avoid utter shambles I will restart if a GC happens while I * am unfolding the hash table. And fail if that happens twice * in a row. */ { int32 size, i, ogcnum; int n_gc = 0; Lisp_Object v, r; if (!is_vector(tab) || type_of_header(vechdr(tab)) != TYPE_HASH) return aerror1("hashcontents", tab); v = elt(tab, 4); large_hash_table = type_of_header(vechdr(v)) == TYPE_STRUCTURE; size = words_in_hash_table(v)*4+8; size = (size - 4) >> 2; restart: r = nil; if (++n_gc > 2) return aerror("hashcontents"); ogcnum = gc_number; for (i=1; i<size; i+=2) { Lisp_Object k1 = ht_elt(v, i), v1 = ht_elt(v, i+1); if (k1 == SPID_HASH0 || k1 == SPID_HASH1) continue; push(v); r = acons(k1, v1, r); pop(v); errexit(); if (gc_number != ogcnum) goto restart; } return onevalue(r); } Lisp_Object Lget_hash_1(Lisp_Object nil, Lisp_Object key) { #ifdef COMMON return Lget_hash(nil, 3, key, sys_hash_table, nil); #else /* * The definition implemented here is as required by Reduce in * the file matrix.red... In the long term this is unsatisfactory. */ Lisp_Object r; push(key); r = Lget_hash(nil, 3, key, sys_hash_table, nil); pop(key); errexit(); if (mv_2 != nil) { r = cons(key, r); errexit(); } return onevalue(r); #endif } Lisp_Object Lget_hash_2(Lisp_Object nil, Lisp_Object key, Lisp_Object tab) { return Lget_hash(nil, 3, key, tab, nil); } Lisp_Object MS_CDECL Lput_hash(Lisp_Object nil, int nargs, ...) { va_list a; Lisp_Object key, tab, val; va_start(a, nargs); key = va_arg(a, Lisp_Object); tab = va_arg(a, Lisp_Object); val = va_arg(a, Lisp_Object); va_end(a); argcheck(nargs, 3, "puthash"); push3(key, tab, val); Lget_hash(nil, 3, key, tab, nil); pop3(val, tab, key); errexit(); if (mv_2 == nil) /* Not found, thus I point at an empty slot */ { if (hashgap >= 0) hashoffset = hashgap; ht_elt(work_0, hashoffset+1) = key; ht_elt(work_0, hashoffset+2) = val; elt(tab, 1) += 0x10; /* increment count of used entries */ if (elt(tab, 1) > elt(tab, 2)) { Lisp_Object size = elt(tab, 2), growth = elt(tab, 3), newhash, v; int32 isize = int_of_fixnum(size), i; push2(tab, val); if (is_fixnum(growth)) { int32 w1 = int_of_fixnum(growth); if (w1 > 0) isize = isize + w1; else isize = isize + (isize/2); } else if (is_float(growth)) { double w2 = float_of_number(growth); int32 newsize = isize; if (1.0 < w2 && w2 < 10.0) newsize = (int32)(w2 * (double)isize); if (newsize > isize) isize = newsize; else isize = isize + (isize/2); } else isize = isize + (isize/2); /* * NB - Lmkhash() does not disturb large_hash_table, so I can still * access the old table happily even after this call... */ newhash = Lmkhash(nil, 3, fixnum_of_int(isize), elt(tab, 0), growth); pop2(val, tab); errexit(); v = elt(tab, 4); for (i=0; i<=4; i++) elt(tab, i) = elt(newhash, i); large_hash_table = type_of_header(vechdr(v)) == TYPE_STRUCTURE; isize = words_in_hash_table(v); for (i=0; i<isize; i+=2) { Lisp_Object key1 = ht_elt(v, i+1), val1 = ht_elt(v, i+2); CSLbool large = large_hash_table; if (key1 == SPID_HASH0 || key1 == SPID_HASH1) continue; /* * NB the new hash table is big enough to hold all the data that was in the * old one, so inserting stuff into it can not cause a (recursive) * enlargement here.... */ push3(v, tab, val); Lput_hash(nil, 3, key1, tab, val1); pop3(val, tab, v); large_hash_table = large; /* Maybe scrabled by put_hash */ } } return onevalue(val); } else { ht_elt(work_0, hashoffset+2) = val; return onevalue(val); } } Lisp_Object Lput_hash_2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { return Lput_hash(nil, 3, a, sys_hash_table, b); } Lisp_Object Lrem_hash(Lisp_Object nil, Lisp_Object key, Lisp_Object tab) { push2(key, tab); Lget_hash(nil, 3, key, tab, nil); pop2(tab, key); errexit(); if (mv_2 == nil) return onevalue(nil); else { ht_elt(work_0, hashoffset+1) = SPID_HASH1; ht_elt(work_0, hashoffset+2) = SPID_HASH0; elt(tab, 1) -= 0x10; /* * Some folk would believe that if the table shrank too much I should * shrink it, or at the very least re-hash it. */ return onevalue(lisp_true); } } Lisp_Object Lrem_hash_1(Lisp_Object nil, Lisp_Object a) { return Lrem_hash(nil, a, sys_hash_table); } Lisp_Object Lclr_hash(Lisp_Object nil, Lisp_Object tab) { Lisp_Object v; int32 size, i; CSL_IGNORE(nil); if (!is_vector(tab) || type_of_header(vechdr(tab)) != TYPE_HASH) return aerror1("clrhash", tab); elt(tab, 1) = fixnum_of_int(0); v = elt(tab, 4); large_hash_table = type_of_header(vechdr(v)) == TYPE_STRUCTURE; size = words_in_hash_table(v); for (i=1; i<size; i++) ht_elt(v, i) = SPID_HASH0; return tab; } Lisp_Object MS_CDECL Lclr_hash_0(Lisp_Object nil, int nargs, ...) { argcheck(nargs, 0, "clrhash"); return Lclr_hash(nil, sys_hash_table); } Lisp_Object Lsxhash(Lisp_Object nil, Lisp_Object key) { unsigned32 h = hash_cl_equal(key, YES); errexit(); h = (h ^ (h >> 16)) & 0x03ffffff; /* ensure it will be a positive fixnum */ return onevalue(fixnum_of_int(h)); } Lisp_Object Leqlhash(Lisp_Object nil, Lisp_Object key) { unsigned32 h = hash_cl_equal(key, NO); errexit(); h = (h ^ (h >> 16)) & 0x03ffffff; /* ensure it will be a positive fixnum */ return onevalue(fixnum_of_int(h)); } #ifdef COMMON Lisp_Object Lhash_flavour(Lisp_Object nil, Lisp_Object tab) { Lisp_Object v,flavour = fixnum_of_int(-1); if (!is_vector(tab) || type_of_header(vechdr(tab)) != TYPE_HASH) return aerror1("hash_flavour", tab); v = elt(tab, 0); /* The code here needs to allow for user-specified hash functions */ if (is_fixnum(v)) flavour = v; return onevalue(flavour); } #endif Lisp_Object MS_CDECL Lputv(Lisp_Object nil, int nargs, ...) { Header h; va_list a; int32 n1, hl; Lisp_Object v, n, x; argcheck(nargs, 3, "putv"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); va_end(a); CSL_IGNORE(nil); if (!is_vector(v) || vector_holds_binary(h = vechdr(v))) return aerror1("putv", v); else if (!is_fixnum(n)) return aerror1("putv offset not fixnum", n); hl = (length_of_header(h) - 4) >> 2; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("putv index range", n); elt(v, n1) = x; return onevalue(x); } Lisp_Object Lgetv(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int32 n1, hl; CSL_IGNORE(nil); if (!is_vector(v) || vector_holds_binary(h = vechdr(v))) return aerror1("getv", v); else if (!is_fixnum(n)) return aerror1("getv offset not fixnum", n); hl = (length_of_header(h) - 4) >> 2; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("getv index range", n); else return onevalue(elt(v, n1)); } /* * Here I make a (simple) string. */ Lisp_Object Lsmkvect(Lisp_Object nil, Lisp_Object n) { Lisp_Object w; int32 nn; if (!is_fixnum(n) || (int32)n<0) return aerror1("make-simple-string", n); nn = int_of_fixnum(n); w = getvector(TAG_VECTOR, TYPE_STRING, nn+4); errexit(); nn = (int32)doubleword_align_up(nn+4); while (nn > 4) { nn -= 4; *(int32 *)((char *)w - TAG_VECTOR + nn) = 0; } return onevalue(w); } /* * Here I make a vector capable of holding 8-bit binary integers. */ Lisp_Object Lmkvect8(Lisp_Object nil, Lisp_Object n) { Lisp_Object w; int32 nn; if (!is_fixnum(n) || (int32)n<0) return aerror1("mkvect8", n); nn = int_of_fixnum(n); w = getvector(TAG_VECTOR, TYPE_VEC8, nn+4); errexit(); nn = (int32)doubleword_align_up(nn+4); while (nn > 4) { nn -= 4; *(int32 *)((char *)w - TAG_VECTOR + nn) = 0; } return onevalue(w); } /* * Here I make a vector capable of holding 16-bit binary integers. */ Lisp_Object Lmkvect16(Lisp_Object nil, Lisp_Object n) { Lisp_Object w; int32 nn; if (!is_fixnum(n) || (int32)n<0) return aerror1("mkvect16", n); nn = 2*int_of_fixnum(n); w = getvector(TAG_VECTOR, TYPE_VEC16, nn+4); errexit(); nn = (int32)doubleword_align_up(nn+4); while (nn > 4) { nn -= 4; *(int32 *)((char *)w - TAG_VECTOR + nn) = 0; } return onevalue(w); } /* * Here I make a vector capable of holding 32-bit binary integers. */ Lisp_Object Lmkvect32(Lisp_Object nil, Lisp_Object n) { Lisp_Object w; int32 nn; if (!is_fixnum(n) || (int32)n<0) return aerror1("mkvect32", n); nn = 4*int_of_fixnum(n); w = getvector(TAG_VECTOR, TYPE_VEC32, nn+4); errexit(); nn = (int32)doubleword_align_up(nn+4); while (nn > 4) { nn -= 4; *(int32 *)((char *)w - TAG_VECTOR + nn) = 0; } return onevalue(w); } /* * Here I make a vector capable of holding 32-bit floats. */ Lisp_Object Lmkfvect32(Lisp_Object nil, Lisp_Object n) { Lisp_Object w; int32 nn; if (!is_fixnum(n) || (int32)n<0) return aerror1("mkfvect32", n); nn = 4*int_of_fixnum(n); w = getvector(TAG_VECTOR, TYPE_FLOAT32, nn+4); errexit(); nn = (int32)doubleword_align_up(nn+4); while (nn > 4) { nn -= 4; *(float *)((char *)w - TAG_VECTOR + nn) = (float)0.0; } return onevalue(w); } /* * Here I make a vector capable of holding 64-bit floats. */ Lisp_Object Lmkfvect64(Lisp_Object nil, Lisp_Object n) { Lisp_Object w; int32 nn; if (!is_fixnum(n) || (int32)n<0) return aerror1("mkfvect64", n); nn = 4 + 8*int_of_fixnum(n); w = getvector(TAG_VECTOR, TYPE_FLOAT64, nn+4); errexit(); nn = (int32)doubleword_align_up(nn+4); while (nn > 8) { nn -= 8; *(double *)((char *)w - TAG_VECTOR + nn) = 0.0; } return onevalue(w); } Lisp_Object simplify_string(Lisp_Object s) /* * s is supposed to be a string of some sort - return a simple string * with the same contents. This is horrid and messy, and relies on * a load of stuff coded elsewhere in Lisp: is is coded here in C * despite that because despite the breaches of modularity that are involved * doing so seems to make bootstrapping easier. */ { Header h; Lisp_Object w, nil = C_nil, h1; int32 i, n = 0; if (!is_vector(s)) return aerror("simplify-string"); h = vechdr(s); if (type_of_header(h) == TYPE_STRING) return onevalue(s); /* Already simple */ if (type_of_header(h) != TYPE_ARRAY) return aerror("simplify-string"); h1 = elt(s, 0); if (h1 != string_char_sym) return aerror("simplify-string"); h1 = elt(s, 1); /* Dimension list */ if (!consp(h1)) return aerror("simplify-string"); n = int_of_fixnum(qcar(h1)); /* Look at size involved */ h1 = elt(s, 5); /* Fill pointer */ if (is_fixnum(h1)) n = int_of_fixnum(h1); stackcheck1(0, s); nil = C_nil; push(s); w = getvector(TAG_VECTOR, TYPE_STRING, n+4); pop(s); errexit(); i = (int32)doubleword_align_up(n+4); while (i > 4) /* pre-fill target vector with zero */ { i -= 4; *(int32 *)((char *)w - TAG_VECTOR + i) = 0; } h1 = elt(s, 3); h = int_of_fixnum(h1); /* Displace adjustment */ s = elt(s, 2); for (i=0; i<n; i++) celt(w, i) = celt(s, i+h); return onevalue(w); } Lisp_Object MS_CDECL Lsputv(Lisp_Object nil, int nargs, ...) { Header h; va_list a; int32 vx, n1, hl; Lisp_Object v, n, x; argcheck(nargs, 3, "sputv"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); va_end(a); CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_STRING) return aerror1("putv-char", v); else if (!is_fixnum(n)) return aerror1("putv-char", n); else if (is_fixnum(x)) vx = int_of_fixnum(x); else if (is_char(x)) vx = code_of_char(x); else return aerror1("putv-char contents", x); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("putv-char", n); #ifdef Kanji if (iswchar((int)vx) { if (n1 == hl-1) return aerror1("putv-char", n); celt(v, n1) = vx >> 8; celt(v, n1+1) = vx; } else celt(v, n1) = vx; #else celt(v, n1) = vx; #endif return onevalue(x); } Lisp_Object Lbpsupbv(Lisp_Object nil, Lisp_Object v) { Header h; int32 n; CSL_IGNORE(nil); if (!(is_bps(v))) return aerror1("bps-upbv", v); h = *(Header *)((char *)data_of_bps(v) - 4); n = length_of_header(h) - 4; return onevalue(fixnum_of_int(n-1)); } Lisp_Object MS_CDECL Lbpsputv(Lisp_Object nil, int nargs, ...) { Header h; va_list a; int32 n1, hl; Lisp_Object v, n, x; argcheck(nargs, 3, "bpsputv"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); va_end(a); CSL_IGNORE(nil); if (!is_bps(v)) return aerror1("bpsputv", v); else if (!is_fixnum(n)) return aerror1("bps-putv", n); else if (!is_fixnum(x)) return aerror1("bps-putv contents", x); h = *(Header *)((char *)data_of_bps(v) - 4); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("bps-putv", n); *((char *)data_of_bps(v) + n1) = (int)int_of_fixnum(x); return onevalue(x); } /* * To make this function Standard Lisp Friendly it will return as its * value a SYMBOL. This is because unadorned character objects are not * really part of Standard Lisp. For cases where you want to character * code I have introduced a function scharn which is almost exactly the * same except that it returns an integer character code not a symbol. */ Lisp_Object Lsgetv(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int w; int32 n1, hl; CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_STRING) return aerror1("schar", v); else if (!is_fixnum(n)) return aerror1("schar", n); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("schar", n); w = celt(v, n1); #ifdef Kanji if (n1 < hl-1 && is2byte(w)) w = (w << 8) + celt(v, n+1); #endif #ifdef COMMON return onevalue(pack_char(0, 0, w)); /* NB 16-bite chars OK here */ #else #ifdef Kanji if (w & 0xff00) { celt(boffo, 0) = w >> 8; celt(boffo, 1) = w; /* * If it is an extended character I will look up a symbol for it each time. * this will make processing extended characters distinctly more expensive * than working with the basic ASCII ones, but I hope it will still be * acceptable. */ n = iintern(boffo, 2, lisp_package, 0); errexit(); return onevalue(n); } #endif /* * For 8-bit characters I keep a table of ready-interned Lisp symbols. */ n = elt(charvec, w & 0xff); if (n == nil) { celt(boffo, 0) = w; n = iintern(boffo, 1, lisp_package, 0); errexit(); elt(charvec, w & 0xff) = n; } return onevalue(n); #endif } Lisp_Object Lsgetvn(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int w; int32 n1, hl; CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_STRING) return aerror1("scharn", v); else if (!is_fixnum(n)) return aerror1("scharn", n); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("scharn", n); w = celt(v, n1); #ifdef Kanji if (n1 < hl-1 && is2byte(w)) w = (w << 8) + celt(v, n+1); #endif return onevalue(fixnum_of_int(w)); } Lisp_Object Lbytegetv(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int w; int32 n1, hl; CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_STRING) return aerror1("byte-getv", v); else if (!is_fixnum(n)) return aerror1("byte-getv", n); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("byte-getv", n); w = ucelt(v, n1); return onevalue(fixnum_of_int(w)); } Lisp_Object Lbpsgetv(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int32 n1, hl; CSL_IGNORE(nil); if (!is_bps(v)) return aerror1("bps-getv", v); else if (!is_fixnum(n)) return aerror1("bps-getv", n); h = *(Header *)((char *)data_of_bps(v) - 4); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("bps-getv", n); n1 = *((char *)data_of_bps(v) + n1); return onevalue(fixnum_of_int(n1 & 0xff)); } /* * native-putv and native-getv have an optional trailing argument that * should have the value 1, 2 or 4 to indicate the number of bytes to be * transferred. */ Lisp_Object MS_CDECL Lnativeputv(Lisp_Object nil, int nargs, ...) { va_list a; int32 p, o, v32, width; Lisp_Object v, n, x, w; if (nargs != 4) { argcheck(nargs, 3, "native-putv"); } va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); if (nargs == 4) w = va_arg(a, Lisp_Object); else w = fixnum_of_int(1); va_end(a); CSL_IGNORE(nil); if (!consp(v) || !is_fixnum(qcar(v)) || !is_fixnum(qcdr(v)) || (p = int_of_fixnum(qcar(v))) < 0 || p > native_pages_count) return aerror1("native-putv", v); else if (!is_fixnum(n)) return aerror1("native-putv", n); else if (!is_fixnum(x) && (!is_numbers(x) || !is_bignum(x))) return aerror1("native-putv contents", x); else if (!is_fixnum(w)) return aerror1("native-putv width", w); width = int_of_fixnum(w); o = int_of_fixnum(qcdr(v)) + int_of_fixnum(n); if (o < 0 || o >= CSL_PAGE_SIZE) return aerror1("native-putv", n); p = (int32)native_pages[p]; p = doubleword_align_up(p); v32 = thirty_two_bits(x); switch (width) { default: return aerror1("native-putv width", w); case 1: *((char *)p + o) = (int)int_of_fixnum(x); break; #ifdef ADDRESS_64 case 2: /* * NOTE that I access the memory here as an array of 16-bit or 32-bit * values and I do not do anything to adjust for the order of bytes in * the word. Thus the effect of mixtures of 1, 2 and 4 byte operations on * native code space will be system dependent. But my intent at present is * that native code is always to be generated on ths machine on which it * will run and that it will never be touched on other machines so this * lack of portability is not really an issue! */ /* * This seems to be one of a very small number of places where I use int16. * In the case of a machine with try 64-bit addresses I will disble it. */ *(int16 *)((char *)p + o) = (int)int_of_fixnum(x); break; #endif case 4: *(int32 *)((char *)p + o) = (int)int_of_fixnum(x); break; } native_pages_changed = 1; return onevalue(x); } Lisp_Object Lnativegetv(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { int32 p, o; CSL_IGNORE(nil); if (!consp(v) || !is_fixnum(qcar(v)) || !is_fixnum(qcdr(v)) || (p = int_of_fixnum(qcar(v))) < 0 || p > native_pages_count) return aerror1("native-getv", v); else if (!is_fixnum(n)) return aerror1("native-getv", n); o = int_of_fixnum(qcdr(v)) + int_of_fixnum(n); if (o < 0 || o >= CSL_PAGE_SIZE) return aerror1("native-getv", o); p = (int32)native_pages[p]; p = doubleword_align_up(p); o = *((char *)p + o); return onevalue(fixnum_of_int(o & 0xff)); } Lisp_Object MS_CDECL Lnativegetvn(Lisp_Object nil, int nargs, ...) { Lisp_Object v, n, w; int32 p, o; va_list a; argcheck(nargs, 3, "native-getv"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); w = va_arg(a, Lisp_Object); va_end(a); CSL_IGNORE(nil); if (!consp(v) || !is_fixnum(qcar(v)) || !is_fixnum(qcdr(v)) || (p = int_of_fixnum(qcar(v))) < 0 || p > native_pages_count) return aerror1("native-getv", v); else if (!is_fixnum(n)) return aerror1("native-getv", n); else if (!is_fixnum(w)) return aerror1("native-getv width", w); o = int_of_fixnum(qcdr(v)) + int_of_fixnum(n); if (o < 0 || o >= CSL_PAGE_SIZE) return aerror1("native-getv", o); p = (int32)native_pages[p]; p = doubleword_align_up(p); switch (int_of_fixnum(w)) { default: return aerror1("native-getv width", w); case 1: o = *((char *)p + o); return onevalue(fixnum_of_int(o & 0xff)); #ifndef ADDRESS_64 case 2: o = *(int16 *)((char *)p + o); return onevalue(fixnum_of_int(o & 0xffff)); #endif case 4: o = *(int32 *)((char *)p + o); p = o & fix_mask; if (p==0 || p==fix_mask) return onevalue(fixnum_of_int(o & 0xff)); else if ((o & 0x80000000) == 0) { w = make_one_word_bignum(o); errexit(); return onevalue(w); } else { w = make_two_word_bignum(1, o & 0x7fffffff); errexit(); return onevalue(w); } } } Lisp_Object MS_CDECL Lnative_type(Lisp_Object nil, int nargs, ...) { return onevalue(fixnum_of_int(NATIVE_CODE_TAG)); } /* * (native-address fn nargs) fetches the value from the relevent function cell * of the function and returns it represented as an integer. This gives * the current real absolute address of the code involved and is intended * to be useful while testing a native-mode compiler. */ Lisp_Object Lnative_address(Lisp_Object nil, Lisp_Object fn, Lisp_Object nargs) { int32 n, n1; CSL_IGNORE(nil); if (!symbolp(fn)) return aerror1("native-address", fn); if (!is_fixnum(nargs)) return aerror1("native-address", nargs); n = int_of_fixnum(nargs); switch (n) { case 1: n = ifn1(fn); break; case 2: n = ifn2(fn); break; default:n = ifnn(fn); break; } n1 = n & fix_mask; if (n1 == 0 || n1 == fix_mask) return onevalue(fixnum_of_int(n)); fn = make_one_word_bignum(n); errexit(); return onevalue(fn); } /* * (native-address n) with one integer argument will return an integer that * is the current memory address of a CSL/CCL internal variable identified * by that integer. The association between integers and variables is as * per the file "externs.h" and the switch statement here. The case 0 gives * the address of NIL, while 1 gives the address of "stack". * An invalid or unrecognised integer leads to a result * of zero. This is intended solely for the use of a native-code compiler. * It may not then be necessary to provide access to ALL of these variables, * but at least to start with it seems easiest to be comprehensive. * Negative integers use values in the following table, which are functions * in CSL that might usefully be called directly. If the one argument is a * cons then it is expected to be a native code handle and the associated * real address is returned. */ void *useful_functions[] = { (void *)cons, /* -1, 0 */ (void *)ncons, /* -2, 1 */ (void *)list2, /* -3, 2 */ (void *)list2star, /* -4, 3 */ (void *)acons, /* -5, 4 */ (void *)list3, /* -6, 5 */ (void *)plus2, /* -7, 6 */ (void *)difference2, /* -8, 7 */ (void *)add1, /* -9, 8 */ (void *)sub1, /* -10, 9 */ (void *)get, /* -11, 10 */ (void *)lognot, /* -12, 11 */ (void *)ash, /* -13, 12 */ (void *)quot2, /* -14, 13 */ (void *)Cremainder, /* -15, 14 */ (void *)times2, /* -16, 15 */ (void *)negate, /* -17, 16 */ (void *)rational, /* -18, 17 */ (void *)lessp2, /* -19, 18 */ (void *)lesseq2, /* -20, 19 */ (void *)greaterp2, /* -21, 20 */ (void *)geq2, /* -22, 21 */ (void *)zerop, /* -23, 22 */ (void *)reclaim, /* -24, 23 */ (void *)error, /* -25, 24 */ (void *)equal_fn, /* -26, 25 */ (void *)cl_equal_fn, /* -27, 26 */ (void *)aerror, /* -28, 27 */ (void *)integerp, /* -29, 28 */ (void *)apply /* -30, 29 */ }; char *address_of_var(int n) { char *p = NULL; Lisp_Object nil = C_nil; if (n == 0) p = (char *)nil; else if (n == 1) p = (char *)&stack; else #ifdef NILSEG_EXTERNS switch (n) { default: p = 0; break; case 12: p = (char *)&byteflip; break; case 13: p = (char *)&codefringe; break; case 14: p = (char *)&codelimit; break; #ifdef COMMON case 16: p = (char *)&stacklimit; break; #else case 15: p = (char *)&stacklimit; break; #endif case 18: p = (char *)&fringe; break; case 19: p = (char *)&heaplimit; break; case 20: p = (char *)&vheaplimit; break; case 21: p = (char *)&vfringe; break; case 22: p = (char *)&miscflags; break; case 24: p = (char *)&nwork; break; case 25: p = (char *)&exit_reason; break; case 26: p = (char *)&exit_count; break; case 27: p = (char *)&gensym_ser; break; case 28: p = (char *)&print_precision; break; case 29: p = (char *)¤t_modulus; break; case 30: p = (char *)&fastget_size; break; case 31: p = (char *)&package_bits; break; case 52: p = (char *)¤t_package; break; case 53: p = (char *)&B_reg; break; case 54: p = (char *)&codevec; break; case 55: p = (char *)&litvec; break; case 56: p = (char *)&exit_tag; break; case 57: p = (char *)&exit_value; break; case 58: p = (char *)&catch_tags; break; case 59: p = (char *)&lisp_package; break; case 60: p = (char *)&boffo; break; case 61: p = (char *)&charvec; break; case 62: p = (char *)&sys_hash_table; break; case 63: p = (char *)&help_index; break; case 64: p = (char *)&gensym_base; break; case 65: p = (char *)&err_table; break; case 66: p = (char *)&supervisor; break; case 67: p = (char *)&startfn; break; case 68: p = (char *)&faslvec; break; case 69: p = (char *)&tracedfn; break; case 70: p = (char *)&prompt_thing; break; case 71: p = (char *)&faslgensyms; break; case 72: p = (char *)&cl_symbols; break; case 73: p = (char *)&active_stream; break; case 80: p = (char *)&append_symbol; break; case 81: p = (char *)&applyhook; break; case 82: p = (char *)&cfunarg; break; case 83: p = (char *)&comma_at_symbol; break; case 84: p = (char *)&comma_symbol; break; case 85: p = (char *)&compiler_symbol; break; case 86: p = (char *)&comp_symbol; break; case 87: p = (char *)&cons_symbol; break; case 88: p = (char *)&echo_symbol; break; case 89: p = (char *)&emsg_star; break; case 90: p = (char *)&evalhook; break; case 91: p = (char *)&eval_symbol; break; case 92: p = (char *)&expr_symbol; break; case 93: p = (char *)&features_symbol; break; case 94: p = (char *)&fexpr_symbol; break; case 95: p = (char *)&funarg; break; case 96: p = (char *)&function_symbol; break; case 97: p = (char *)λ break; case 98: p = (char *)&lisp_true; break; case 99: p = (char *)&lower_symbol; break; case 100: p = (char *)¯oexpand_hook; break; case 101: p = (char *)¯o_symbol; break; case 102: p = (char *)&opt_key; break; case 103: p = (char *)&prinl_symbol; break; case 104: p = (char *)&progn_symbol; break; case 105: p = (char *)"e_symbol; break; case 106: p = (char *)&raise_symbol; break; case 107: p = (char *)&redef_msg; break; case 108: p = (char *)&rest_key; break; case 109: p = (char *)&savedef; break; case 110: p = (char *)&string_char_sym; break; case 111: p = (char *)&unset_var; break; case 112: p = (char *)&work_symbol; break; case 113: p = (char *)&lex_words; break; case 114: p = (char *)&get_counts; break; case 115: p = (char *)&fastget_names; break; case 116: p = (char *)&input_libraries; break; case 117: p = (char *)&output_library; break; case 118: p = (char *)¤t_file; break; case 119: p = (char *)&break_function; break; case 120: p = (char *)&lisp_work_stream; break; case 121: p = (char *)&lisp_standard_output; break; case 122: p = (char *)&lisp_standard_input; break; case 123: p = (char *)&lisp_debug_io; break; case 124: p = (char *)&lisp_error_output; break; case 125: p = (char *)&lisp_query_io; break; case 126: p = (char *)&lisp_terminal_io; break; case 127: p = (char *)&lisp_trace_output; break; case 128: p = (char *)&standard_output; break; case 129: p = (char *)&standard_input; break; case 130: p = (char *)&debug_io; break; case 131: p = (char *)&error_output; break; case 132: p = (char *)&query_io; break; case 133: p = (char *)&terminal_io; break; case 134: p = (char *)&trace_output; break; case 135: p = (char *)&fasl_stream; break; case 136: p = (char *)&native_code; break; #ifdef COMMON case 140: p = (char *)&keyword_package; break; case 141: p = (char *)&all_packages; break; case 142: p = (char *)&package_symbol; break; case 143: p = (char *)&internal_symbol; break; case 144: p = (char *)&external_symbol; break; case 145: p = (char *)&inherited_symbol; break; case 146: p = (char *)&key_key; break; case 147: p = (char *)&allow_other_keys; break; case 148: p = (char *)&aux_key; break; case 149: p = (char *)&format_symbol; break; case 150: p = (char *)&expand_def_symbol; break; case 151: p = (char *)&allow_key_key; break; case 152: p = (char *)&declare_symbol; break; case 153: p = (char *)&special_symbol; break; #endif } #else /* NILSEG_EXTERNS */ if (n >= 160) switch (n) { default: p = 0; break; case 160: p = (char *)&user_base_0; break; case 161: p = (char *)&user_base_1; break; case 162: p = (char *)&user_base_2; break; case 163: p = (char *)&user_base_3; break; case 164: p = (char *)&user_base_4; break; case 165: p = (char *)&user_base_5; break; case 166: p = (char *)&user_base_6; break; case 167: p = (char *)&user_base_7; break; case 168: p = (char *)&user_base_8; break; case 169: p = (char *)&user_base_9; break; } else p = (char *)&(((int32 *)nil)[n]); #endif /* NILSEG_EXTERNS */ return p; } Lisp_Object Lnative_address1(Lisp_Object nil, Lisp_Object x) { int32 n, n1, p; if (consp(x)) { if (!is_fixnum(qcar(x)) || !is_fixnum(qcdr(x)) || (p = int_of_fixnum(qcar(x))) < 0 || p > native_pages_count) return aerror1("native-address", x); n = int_of_fixnum(qcdr(x)); if (n < 0 || n >= CSL_PAGE_SIZE) return aerror1("native-address", x); p = (int32)native_pages[p]; p = doubleword_align_up(p); p = (int32)((char *)p + n); } else { if (!is_fixnum(x)) return aerror1("native-address", x); n = int_of_fixnum(x); if (n < 0) { n = (-n) - 1; if (n >= sizeof(useful_functions)/sizeof(void *)) return aerror1("native-address", x); else p = (int32)useful_functions[n]; } else p = (int32)address_of_var(n); } n1 = p & fix_mask; if (n1 == 0 || n1 == fix_mask) return onevalue(fixnum_of_int(p)); x = make_one_word_bignum(p); errexit(); return onevalue(x); } /* * Access functions for specialised (binary-contents) vectors. NOT integrated * in with the greater generality of vector structures. */ Lisp_Object MS_CDECL Lputv8(Lisp_Object nil, int nargs, ...) { Header h; va_list a; int32 n1, hl; Lisp_Object v, n, x; argcheck(nargs, 3, "putv8"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); va_end(a); CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_VEC8) return aerror1("putv8", v); else if (!is_fixnum(n)) return aerror1("putv8 offset not fixnum", n); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("putv8 index range", n); scelt(v, n1) = int_of_fixnum(x); return onevalue(x); } Lisp_Object Lgetv8(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int32 n1, hl; CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_VEC8) return aerror1("getv8", v); else if (!is_fixnum(n)) return aerror1("getv8 offset not fixnum", n); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("getv8 index range", n); else return onevalue(fixnum_of_int(scelt(v, n1))); } Lisp_Object MS_CDECL Lputv16(Lisp_Object nil, int nargs, ...) { Header h; va_list a; int32 n1, hl; Lisp_Object v, n, x; argcheck(nargs, 3, "putv16"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); va_end(a); CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_VEC16) return aerror1("putv16", v); else if (!is_fixnum(n)) return aerror1("putv16 offset not fixnum", n); hl = (length_of_header(h) - 4) >> 1; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("putv16 index range", n); sethelt(v, n1, int_of_fixnum(x)); return onevalue(x); } Lisp_Object Lgetv16(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int32 n1, hl; CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_VEC16) return aerror1("getv16", v); else if (!is_fixnum(n)) return aerror1("getv16 offset not fixnum", n); hl = (length_of_header(h) - 4) >> 1; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("getv16 index range", n); n1 = helt(v, n1); return onevalue(fixnum_of_int(n1)); } Lisp_Object MS_CDECL Lputv32(Lisp_Object nil, int nargs, ...) { Header h; va_list a; int32 n1, hl; Lisp_Object v, n, x; argcheck(nargs, 3, "putv32"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); va_end(a); CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_VEC32) return aerror1("putv32", v); else if (!is_fixnum(n)) return aerror1("putv32 offset not fixnum", n); hl = (length_of_header(h) - 4) >> 2; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("putv32 index range", n); ielt(v, n1) = thirty_two_bits(x); return onevalue(x); } Lisp_Object Lgetv32(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int32 n1, hl; CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_VEC32) return aerror1("getv32", v); else if (!is_fixnum(n)) return aerror1("getv32 offset not fixnum", n); hl = (length_of_header(h) - 4) >> 2; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("getv32 index range", n); n1 = ielt(v, n1); hl = n1 & fix_mask; if (hl == 0 || hl == fix_mask) return fixnum_of_int(n1); n = make_one_word_bignum(n1); errexit(); return onevalue(n); } Lisp_Object MS_CDECL Lfputv32(Lisp_Object nil, int nargs, ...) { Header h; va_list a; int32 n1, hl; Lisp_Object v, n, x; double d; argcheck(nargs, 3, "fputv32"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); d = float_of_number(x); va_end(a); CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_FLOAT32) return aerror1("fputv32", v); else if (!is_fixnum(n)) return aerror1("fputv32 offset not fixnum", n); hl = (length_of_header(h) - 4) >> 2; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("fputv32 index range", n); felt(v, n1) = (float)d; return onevalue(x); } Lisp_Object Lfgetv32(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int32 n1, hl; CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_FLOAT32) return aerror1("fgetv32", v); else if (!is_fixnum(n)) return aerror1("fgetv32 offset not fixnum", n); hl = (length_of_header(h) - 4) >> 2; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("fgetv32 index range", n); #ifdef COMMON v = make_boxfloat((double)felt(v, n1), TYPE_SINGLE_FLOAT); #else v = make_boxfloat((double)felt(v, n1), TYPE_DOUBLE_FLOAT); #endif errexit(); return onevalue(v); } Lisp_Object MS_CDECL Lfputv64(Lisp_Object nil, int nargs, ...) { Header h; va_list a; int32 n1, hl; Lisp_Object v, n, x; double d; argcheck(nargs, 3, "fputv64"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); d = float_of_number(x); va_end(a); CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_FLOAT64) return aerror1("fputv64", v); else if (!is_fixnum(n)) return aerror1("fputv64 offset not fixnum", n); hl = (length_of_header(h) - 8) >> 3; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("fputv64 index range", n); delt(v, n1) = d; return onevalue(x); } Lisp_Object Lfgetv64(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int32 n1, hl; CSL_IGNORE(nil); if (!is_vector(v) || type_of_header(h = vechdr(v)) != TYPE_FLOAT64) return aerror1("fgetv64", v); else if (!is_fixnum(n)) return aerror1("fgetv64 offset not fixnum", n); hl = (length_of_header(h) - 8) >> 3; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("fgetv64 index range", n); v = make_boxfloat(delt(v, n1), TYPE_DOUBLE_FLOAT); errexit(); return onevalue(v); } #ifdef COMMON /* * (defun putvec (v n x) * (cond * ((simple-string-p v) (putv-char v n x)) * ((simple-bit-vector-p v) (putv-bit v n x)) * (t (putv v n x)))) */ static Lisp_Object MS_CDECL Lputvec(Lisp_Object nil, int nargs, ...) { Header h; va_list a; int32 vx, n1, hl; Lisp_Object v, n, x; CSL_IGNORE(nil); argcheck(nargs, 3, "putvec"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); va_end(a); /* * Oh joy - here I have to dispatch based on what sort of vector I have. */ if (!is_vector(v)) return aerror1("putvec", v); else if (!is_fixnum(n)) return aerror1("putvec", n); h = vechdr(v); if (type_of_header(h) == TYPE_STRING) { if (is_fixnum(x)) vx = int_of_fixnum(x); else if (is_char(x)) vx = code_of_char(x); else return aerror1("putvec on string, contents", x); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("putvec", n); celt(v, n1) = (int)vx; return onevalue(x); } if (header_of_bitvector(h)) { int b; if (!is_fixnum(x)) return aerror1("putvec on bitvec, contents", x); x = int_of_fixnum(x) & 1; h = length_of_header(h) - 4; n1 = int_of_fixnum(n); b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ /* * I am just a bit shoddy here - I only complain if an attempt is made to * access beyond the last active byte of a bitvector - I do not * do bound checking accurate to bit positions. */ if (n1 < 0 || n1 >= (int32)h) return aerror1("putv-bit", n); if (x == 0) celt(v, n1) &= ~b; else celt(v, n1) |= b; return onevalue(fixnum_of_int(x)); } if (vector_holds_binary(h)) return aerror1("putvec", v); hl = (length_of_header(h) - 4) >> 2; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("putvec index range", n); elt(v, n1) = x; return onevalue(x); } /* * (defun aref (v n1 &rest r) * (if (null r) * (cond * ((simple-vector-p v) (getv v n1)) * ((simple-string-p v) (schar v n1)) * ((simple-bit-vector-p v) (getv-bit v n1)) * ((structp v) (getv v n1)) * (t (general-aref v n1 r))) * (general-aref v n1 r))) * * (defun general-aref (v n1 r) * (when (not (arrayp v)) (error "aref ~s ~s" v (cons n1 r))) * (do ((dd (cdr (getv v 1)) (cdr dd))) * ((null r)) * (setq n1 (+ (* n1 (car dd)) (pop r)))) ***** plus special magic to deal with segmented representations... * (aref (getv v 2) (+ (getv v 3) n1))) */ Lisp_Object MS_CDECL Laref(Lisp_Object nil, int nargs, ...) { Header h; Lisp_Object v, n, w; int32 hl, n1, b; va_list a; if (nargs == 0) return aerror("aref"); va_start(a, nargs); v = va_arg(a, Lisp_Object); if (!is_vector(v)) { va_end(a); return aerror1("aref", v); } h = vechdr(v); if (nargs == 1) n = 0; /* Funny case (aref v) legal if no dimensions! */ else { n = va_arg(a, Lisp_Object); /* First subscript */ if (!is_fixnum(n)) { va_end(a); return aerror1("aref", n); } if (nargs == 2) { if (type_of_header(h) == TYPE_SIMPLE_VEC || type_of_header(h) == TYPE_STRUCTURE) { va_end(a); hl = (length_of_header(h) - 4) >> 2; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("aref index range", n); else return onevalue(elt(v, n1)); } else if (type_of_header(h) == TYPE_STRING) { va_end(a); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("aref index range", n); return onevalue(pack_char(0, 0, celt(v, n1))); } else if (header_of_bitvector(h)) { va_end(a); h = length_of_header(h) - 4; n1 = int_of_fixnum(n); b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ if (n1 < 0 || n1 >= (int32)h) return aerror1("aref index range", n); if ((celt(v, n1) & b) == 0) return onevalue(fixnum_of_int(0)); else return onevalue(fixnum_of_int(1)); } } } if (type_of_header(h) != TYPE_ARRAY) { va_end(a); return aerror1("aref", v); } /* * Here I had better have a general array, and I will need to calculate the * real index location within it. */ w = elt(v, 1); /* The list of dimensions */ if (w == nil && nargs == 1) { va_end(a); return onevalue(elt(v, 2)); } n1 = int_of_fixnum(n); w = qcdr(w); while (nargs > 2 && w != nil) { n = va_arg(a, Lisp_Object); if (!is_fixnum(n)) { va_end(a); return aerror1("aref", n); } n1 = n1*int_of_fixnum(qcar(w)) + int_of_fixnum(n); nargs--; w = qcdr(w); } va_end(a); if (nargs > 2 || w != nil) return aerror("aref, wrong number of subscripts"); n1 += int_of_fixnum(elt(v, 3)); /* displaced-index-offset */ v = elt(v, 2); /* * Now I have got the vector that this array is displaced to or * represented by. If it is in fact a structure (not a simple vector) * then it is a row of 8K sub-vectors, and at element zero it has the * nominal size of the big vector (as a Lisp integer) */ h = vechdr(v); if (type_of_header(h) == TYPE_SIMPLE_VEC) { hl = (length_of_header(h) - 4) >> 2; if (n1 < 0 || n1 >= hl) return aerror("aref index range"); else return onevalue(elt(v, n1)); } else if (type_of_header(h) == TYPE_STRUCTURE) { int32 n2; hl = int_of_fixnum(elt(v, 0)); if (n1 < 0 || n1 >= hl) return aerror("aref index range"); n2 = n1 % 8192; n1 = n1 / 8192; return onevalue(elt(elt(v, n1+1), n2)); } else if (type_of_header(h) == TYPE_STRING) { hl = length_of_header(h) - 4; if (n1 < 0 || n1 >= hl) return aerror("aref index range"); return onevalue(pack_char(0, 0, celt(v, n1))); } else if (header_of_bitvector(h)) { h = length_of_header(h) - 4; b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ if (n1 < 0 || n1 >= (int32)h) return aerror("aref index range"); if ((celt(v, n1) & b) == 0) return onevalue(fixnum_of_int(0)); else return onevalue(fixnum_of_int(1)); } return aerror("aref unknown type for vector representation"); } static Lisp_Object Laref1(Lisp_Object nil, Lisp_Object a) { return Laref(nil, 1, a); } Lisp_Object Laref2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { return Laref(nil, 2, a, b); } Lisp_Object Lelt(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; Lisp_Object w; int32 hl, n1, b; if (!is_fixnum(n) || ((int32)n) < 0) return aerror1("elt", n); n1 = int_of_fixnum(n); if (!is_vector(v)) { w = v; while (consp(w) && n1>0) { n1--; w = qcdr(w); } if (!consp(w)) return aerror1("elt", v); return onevalue(qcar(w)); } h = vechdr(v); if (type_of_header(h) == TYPE_SIMPLE_VEC || type_of_header(h) == TYPE_STRUCTURE) { hl = (length_of_header(h) - 4) >> 2; if (n1 >= hl) return aerror1("elt index range", n); else return onevalue(elt(v, n1)); } else if (type_of_header(h) == TYPE_STRING) { hl = length_of_header(h) - 4; if (n1 >= hl) return aerror1("elt index range", n); return onevalue(pack_char(0, 0, celt(v, n1))); } else if (header_of_bitvector(h)) { h = length_of_header(h) - 4; b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ if (n1 < 0 || n1 >= (int32)h) return aerror1("elt index range", n); if ((celt(v, n1) & b) == 0) return onevalue(fixnum_of_int(0)); else return onevalue(fixnum_of_int(1)); } if (type_of_header(h) != TYPE_ARRAY) return aerror1("elt", v); w = elt(v, 1); /* The list of dimensions - must be 1 dim here */ w = qcdr(w); if (w != nil) return aerror1("elt", v); n1 += int_of_fixnum(elt(v, 3)); /* displaced-index-offset */ v = elt(v, 2); h = vechdr(v); if (type_of_header(h) == TYPE_SIMPLE_VEC) { hl = (length_of_header(h) - 4) >> 2; if (n1 >= hl) return aerror("elt index range"); else return onevalue(elt(v, n1)); } else if (type_of_header(h) == TYPE_STRUCTURE) { int32 n2; hl = int_of_fixnum(elt(v, 0)); if (n1 >= hl) return aerror("elt index range"); n2 = n1 % 8192; n1 = n1 / 8192; return onevalue(elt(elt(v, n1+1), n2)); } else if (type_of_header(h) == TYPE_STRING) { hl = length_of_header(h) - 4; if (n1 >= hl) return aerror("elt index range"); return onevalue(pack_char(0, 0, celt(v, n1))); } else if (header_of_bitvector(h)) { h = length_of_header(h) - 4; b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ if (n1 >= (int32)h) return aerror("elt index range"); if ((celt(v, n1) & b) == 0) return onevalue(fixnum_of_int(0)); else return onevalue(fixnum_of_int(1)); } return aerror("elt unknown type for vector representation"); } /* * (defun aset (v n1 x &rest r) * (if (null r) * (cond * ((simple-vector-p v) (putv v n1 x)) * ((simple-string-p v) (putv-char v n1 x)) * ((simple-bit-vector-p v) (putv-bit v n1 x)) * ((structp v) (putv v n1 x)) * (t (general-aset v n1 x r))) * (general-aset v n1 x r))) * * (defun general-aset (v n1 x r) * (when (not (arrayp v)) (error "aref ~s ~s" v * (reverse (cdr (reverse (cons n1 (cons x r))))))) * (setq r (cons x r)) * (do ((dd (cdr (getv v 1)) (cdr dd))) * ((null (cdr r))) * (setq n1 (+ (* n1 (car dd)) (pop r)))) ***** plus special magic to deal with segmented representations... * (aset (getv v 2) (+ (getv v 3) n1) (car r))) */ /* * Note that the code for ASET is really a mildly modified copy of that * for AREF. */ Lisp_Object MS_CDECL Laset(Lisp_Object nil, int nargs, ...) { Header h; Lisp_Object v, n, w, x; int32 hl, n1, b; va_list a; if (nargs < 2) return aerror("aset"); va_start(a, nargs); v = va_arg(a, Lisp_Object); if (!is_vector(v)) { va_end(a); return aerror1("aset", v); } h = vechdr(v); if (nargs == 2) n = 0; /* Funny case (aset v w) legal if no dimensions! */ else { n = va_arg(a, Lisp_Object); /* First subscript */ if (!is_fixnum(n)) { va_end(a); return aerror1("aset", n); } if (nargs == 3) { if (type_of_header(h) == TYPE_SIMPLE_VEC || type_of_header(h) == TYPE_STRUCTURE) { x = va_arg(a, Lisp_Object); va_end(a); hl = (length_of_header(h) - 4) >> 2; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("aset index range", n); elt(v, n1) = x; return onevalue(x); } else if (type_of_header(h) == TYPE_STRING) { x = va_arg(a, Lisp_Object); va_end(a); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("aset index range", n); if (is_fixnum(x)) b = int_of_fixnum(x); else if (is_char(x)) b = code_of_char(x); else return aerror1("aset needs char", x); celt(v, n1) = b; return onevalue(x); } else if (header_of_bitvector(h)) { x = va_arg(a, Lisp_Object); va_end(a); h = length_of_header(h) - 4; n1 = int_of_fixnum(n); b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ if (n1 < 0 || n1 >= (int32)h) return aerror1("aset index range", n); if (!is_fixnum(x)) return aerror1("aset needs bit", x); if (int_of_fixnum(x) & 1) ucelt(v, n1) |= b; else ucelt(v, n1) &= ~b; return onevalue(x); } } } if (type_of_header(h) != TYPE_ARRAY) { va_end(a); return aerror1("aset", v); } /* * Here I had better have a general array, and I will need to calculate the * real index location within it. */ w = elt(v, 1); /* The list of dimensions */ if (w == nil && nargs == 2) { x = va_arg(a, Lisp_Object); va_end(a); elt(v, 2) = x; return onevalue(x); } n1 = int_of_fixnum(n); w = qcdr(w); while (nargs > 3 && w != nil) { n = va_arg(a, Lisp_Object); if (!is_fixnum(n)) { va_end(a); return aerror1("aset", n); } n1 = n1*int_of_fixnum(qcar(w)) + int_of_fixnum(n); nargs--; w = qcdr(w); } x = va_arg(a, Lisp_Object); va_end(a); if (nargs > 3 || w != nil) return aerror("aset, wrong number of subscripts"); n1 += int_of_fixnum(elt(v, 3)); /* displaced-index-offset */ v = elt(v, 2); h = vechdr(v); if (type_of_header(h) == TYPE_SIMPLE_VEC) { hl = (length_of_header(h) - 4) >> 2; if (n1 < 0 || n1 >= hl) return aerror("aset index range"); elt(v, n1) = x; return onevalue(x); } if (type_of_header(h) == TYPE_STRUCTURE) { int32 n2; hl = int_of_fixnum(elt(v, 0)); if (n1 < 0 || n1 >= hl) return aerror("aset index range"); n2 = n1 % 8192; n1 = n1 / 8192; elt(elt(v, n1+1), n2) = x; return onevalue(x); } else if (type_of_header(h) == TYPE_STRING) { hl = length_of_header(h) - 4; if (n1 < 0 || n1 >= hl) return aerror("aset index range"); if (is_fixnum(x)) b = int_of_fixnum(x); else if (is_char(x)) b = code_of_char(x); else return aerror1("aset needs char", x); celt(v, n1) = b; return onevalue(x); } else if (header_of_bitvector(h)) { h = length_of_header(h) - 4; b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ if (n1 < 0 || n1 >= (int32)h) return aerror("aset index range"); if (!is_fixnum(x)) return aerror1("aset needs bit", x); if (int_of_fixnum(x) & 1) ucelt(v, n1) |= b; else ucelt(v, n1) &= ~b; return onevalue(x); } return aerror("aset unknown type for vector representation"); } static Lisp_Object Laset1(Lisp_Object nil, Lisp_Object a) { return aerror("aset"); } static Lisp_Object Laset2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { return Laset(nil, 2, a, b); } static Lisp_Object MS_CDECL Lsetelt(Lisp_Object nil, int nargs, ...) { Lisp_Object v, n, x; Header h; Lisp_Object w; int32 hl, n1, b; va_list a; argcheck(nargs, 3, "setelt"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); va_end(a); if (!is_fixnum(n) || ((int32)n) < 0) return aerror1("setelt", n); n1 = int_of_fixnum(n); if (!is_vector(v)) { w = v; while (consp(w) && n1>0) { n1--; w = qcdr(w); } if (!consp(w)) return aerror1("setelt", v); qcar(w) = x; return onevalue(x); } h = vechdr(v); if (type_of_header(h) == TYPE_SIMPLE_VEC || type_of_header(h) == TYPE_STRUCTURE) { hl = (length_of_header(h) - 4) >> 2; if (n1 >= hl) return aerror1("setelt index range", n); elt(v, n1) = x; return onevalue(x); } else if (type_of_header(h) == TYPE_STRING) { int vx; hl = length_of_header(h) - 4; if (n1 >= hl) return aerror1("setelt index range", n); if (is_fixnum(x)) vx = int_of_fixnum(x); else if (is_char(x)) vx = code_of_char(x); else return aerror1("setelt contents", x); celt(v, n1) = vx; return onevalue(x); } else if (header_of_bitvector(h)) { if (!is_fixnum(x)) return aerror1("setelt contents", x); x = int_of_fixnum(x) & 1; h = length_of_header(h) - 4; b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ if (n1 >= (int32)h) return aerror1("setelt index range", n); if (x == 0) celt(v, n1) &= ~b; else celt(v, n1) |= b; return onevalue(fixnum_of_int(x)); } if (type_of_header(h) != TYPE_ARRAY) return aerror1("setelt", v); w = elt(v, 1); /* The list of dimensions - must be 1 dim here */ w = qcdr(w); if (w != nil) return aerror1("setelt", v); n1 += int_of_fixnum(elt(v, 3)); /* displaced-index-offset */ v = elt(v, 2); h = vechdr(v); if (type_of_header(h) == TYPE_SIMPLE_VEC) { hl = (length_of_header(h) - 4) >> 2; if (n1 >= hl) return aerror("setelt index range"); elt(v, n1) = x; return onevalue(x); } else if (type_of_header(h) == TYPE_STRUCTURE) { int32 n2; hl = int_of_fixnum(elt(v, 0)); if (n1 >= hl) return aerror("setelt index range"); n2 = n1 % 8192; n1 = n1 / 8192; elt(elt(v, n1+1), n2) = x; return onevalue(x); } else if (type_of_header(h) == TYPE_STRING) { int vx; hl = length_of_header(h) - 4; if (is_fixnum(x)) vx = int_of_fixnum(x); else if (is_char(x)) vx = code_of_char(x); else return aerror1("setelt contents", x); if (n1 >= hl) return aerror("setelt index range"); celt(v, n1) = vx; return onevalue(x); } else if (header_of_bitvector(h)) { if (!is_fixnum(x)) return aerror1("setelt contents", x); x = int_of_fixnum(x) & 1; h = length_of_header(h) - 4; b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ if (n1 >= (int32)h) return aerror("setelt index range"); if (x == 0) celt(v, n1) &= ~b; else celt(v, n1) |= b; return onevalue(fixnum_of_int(x)); } return aerror("setelt unknown type for vector representation"); } /* * (defun vectorp (x) * (or (simple-vector-p x) * (simple-string-p x) * (simple-bit-vector-p x) * (and (arrayp x) (length-one-p (svref x 1))))) */ Lisp_Object Lvectorp(Lisp_Object nil, Lisp_Object a) { Header h; int32 tt; if (!is_vector(a)) return onevalue(nil); h = vechdr(a); tt = type_of_header(h); if (tt == TYPE_SIMPLE_VEC || tt == TYPE_STRING || header_of_bitvector(h)) return onevalue(lisp_true); if (tt == TYPE_ARRAY) { a = elt(a, 1); /* List of dimensions */ if (consp(a) && !consp(qcdr(a))) return onevalue(lisp_true); } return onevalue(nil); } /* * (defun char (s n) * (cond * ((simple-string-p s) (schar s n)) * (t (aref s n)))) */ static Lisp_Object Lchar(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; if (!is_vector(v)) return aerror("char"); h = vechdr(v); if (type_of_header(h) == TYPE_STRING) { int32 hl, n1; if (!is_fixnum(n)) return aerror1("char", n); hl = length_of_header(h) - 4; n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("schar", n); return onevalue(pack_char(0, 0, celt(v, n1))); } return Laref(nil, 2, v, n); } /* * (defun charset (s n c) * (cond * ((simple-string-p s) (putv-char s n c)) * (t (aset s n c)))) */ static Lisp_Object MS_CDECL Lcharset(Lisp_Object nil, int nargs, ...) { Lisp_Object v, n, c; Header h; va_list a; argcheck(nargs, 3, "charset"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); c = va_arg(a, Lisp_Object); va_end(a); if (!is_vector(v)) return aerror1("charset", v); h = vechdr(v); if (!is_fixnum(n)) return aerror1("charset", n); if (type_of_header(h) == TYPE_STRING) { int32 hl, n1, vx; if (!is_fixnum(n)) return aerror1("charset", n); hl = length_of_header(h) - 4; if (is_fixnum(c)) vx = int_of_fixnum(c); else if (is_char(c)) vx = code_of_char(c); else return aerror1("charset contents", c); n1 = int_of_fixnum(n); if (n1 < 0 || n1 >= hl) return aerror1("charset", n); celt(v, n1) = (int)vx; return onevalue(c); } return Laset(nil, 3, v, n, c); } /* * (defun make-string (len &key (initial-element #\ )) * (let ((s (make-simple-string len))) * (dotimes (i len) (charset s i initial-element)) * s)) */ static Lisp_Object MS_CDECL Lmake_string(Lisp_Object nil, int nargs, ...) { va_list a; Lisp_Object w, n, key, init; int32 nn, z, blanks; argcheck(nargs, 3, "make-string"); va_start(a, nargs); n = va_arg(a, Lisp_Object); key = va_arg(a, Lisp_Object); init = va_arg(a, Lisp_Object); va_end(a); if (!is_fixnum(n) || (int32)n<0) return aerror1("make-string", n); if (!is_char(init) && !is_fixnum(init)) return aerror1("make-string", init); if (key != initial_element) return aerror1("make-string", key); nn = int_of_fixnum(n); w = getvector(TAG_VECTOR, TYPE_STRING, nn+4); errexit(); z = (int32)doubleword_align_up(nn+4); if (is_char(init)) blanks = code_of_char(init); else blanks = int_of_fixnum(init); blanks = (blanks << 8) | blanks; blanks = (blanks << 16) | blanks; while (z > 4) { z -= 4; *(int32 *)((char *)w - TAG_VECTOR + z) = blanks; } nn = nn + 4; while ((nn & 7) != 0) { *((char *)w - TAG_VECTOR + nn) = 0; nn++; } return onevalue(w); } static Lisp_Object Lmake_string1(Lisp_Object nil, Lisp_Object n) { Lisp_Object w; int32 nn, z, blanks; if (!is_fixnum(n) || (int32)n<0) return aerror1("make-string", n); nn = int_of_fixnum(n); w = getvector(TAG_VECTOR, TYPE_STRING, nn+4); errexit(); z = (int32)doubleword_align_up(nn+4); blanks = (' ' << 24) | (' ' << 16) | (' ' << 8) | ' '; while (z > 4) { z -= 4; *(int32 *)((char *)w - TAG_VECTOR + z) = blanks; } nn = nn + 4; while ((nn & 7) != 0) { *((char *)w - TAG_VECTOR + nn) = 0; nn++; } return onevalue(w); } static Lisp_Object Lmake_string2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { return Lmake_string(nil, 2, a, b); } /* * (defun string (x) * (cond * ((stringp x) x) * ((symbolp x) (symbol-name x)) * ((string-char-p x) (make-string 1 :initial-element x)) * (t (error "String expected, but found ~S" x)))) */ static Lisp_Object Lstring(Lisp_Object nil, Lisp_Object a) { Header h; Lisp_Object w; if (!is_vector(a)) { char dd[4]; if (symbolp(a)) return onevalue(qpname(a)); if (!is_char(a)) return aerror1("string", a); dd[0] = 'x'; /* Done this way in case character arg has code 0 */ dd[1] = 0; w = make_string(dd); errexit(); celt(w, 0) = code_of_char(a); return onevalue(w); } h = vechdr(a); if (type_of_header(h) == TYPE_STRING) return onevalue(a); else if (type_of_header(h) != TYPE_ARRAY) return aerror1("string", a); /* * Beware abolition of 'string-char */ else if (elt(a, 0) != string_char_sym) return aerror1("string", a); w = elt(a, 1); if (!consp(w) || consp(qcdr(w))) return aerror1("string", a); else return onevalue(a); } /* * (defun list-to-vector (old) * (let* ((len (length old)) * (new (make-simple-vector len))) * (dotimes (i len new) (putv new i (car old)) (setq old (cdr old))))) */ static Lisp_Object Llist_to_vector(Lisp_Object nil, Lisp_Object a) { Lisp_Object v; int32 n = 4; /* * The general LENGTH function deals with vectors as well as lists, and * returns a Lisp integer result. So here I just write out a simple in-line * version. */ for (v=a; consp(v); v = qcdr(v)) n += 4; push(a); v = getvector(TAG_VECTOR, TYPE_SIMPLE_VEC, n); pop(a); errexit(); for(n=0; consp(a); a = qcdr(a), n++) elt(v, n) = qcar(a); if ((n & 1) == 0) elt(v, n) = nil; /* Padder word */ return onevalue(v); } /* * (defun copy-vector (old) * ;; At present this only copies general vectors... * (let* ((len (vector-bound old)) * (new (make-simple-vector len))) * (dotimes (i len new) (putv new i (svref old i))))) */ static Lisp_Object Lcopy_vector(Lisp_Object nil, Lisp_Object a) { return onevalue(nil); } /* * (defun vector (&rest args) * ;; Note that a vector made this way can have at most 50 elements... * (let* ((l (length args)) * (g (make-simple-vector l))) * (dotimes (i l g) * (putv g i (car args)) * (setq args (cdr args))))) */ static Lisp_Object MS_CDECL Lvector(Lisp_Object nil, int nargs, ...) { Lisp_Object r = nil, w; va_list a; va_start(a, nargs); push_args(a, nargs); r = getvector(TAG_VECTOR, TYPE_SIMPLE_VEC, 4*nargs+4); errexitn(nargs); /* * The next line allows for the fact that vectors MUST pad to an even * number of words. */ if ((nargs & 1) == 0) elt(r, nargs) = nil; while (nargs > 0) { pop(w); elt(r, --nargs) = w; } return onevalue(r); } static Lisp_Object Lvector1(Lisp_Object nil, Lisp_Object a) { return Lvector(nil, 1, a); } static Lisp_Object Lvector2(Lisp_Object nil, Lisp_Object a, Lisp_Object b) { return Lvector(nil, 2, a, b); } static Lisp_Object Lshrink_vector(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { int32 n1, n2; if (!is_vector(v)) return aerror1("shrink-vector", v); if (!is_fixnum(n)) return aerror1("shrink-vector", n); n1 = length_of_header(vechdr(v)); n2 = 4*int_of_fixnum(n)+4; if (n2 >= n1) return onevalue(v); /* Not shrunk at all */ if (n1==n2+4 && (n2&4)==0) /* No space to free */ *(Lisp_Object *)((char *)v-TAG_VECTOR+n2) = nil; else { int32 n2a = doubleword_align_up(n2); n1 = doubleword_align_up(n1); *(Lisp_Object *)((char *)v-TAG_VECTOR+n1) = TAG_ODDS+TYPE_STRING+((n1-n2a)<<10); } vechdr(v) = TAG_ODDS+type_of_header(vechdr(v))+(n2<<10); return onevalue(v); } static Lisp_Object Lmake_simple_bitvector(Lisp_Object nil, Lisp_Object n) { int32 bytes; Lisp_Object w; int32 n1; if (!is_fixnum(n) || (int32)n<0) return aerror1("make-simple-bitvector", n); n1 = int_of_fixnum(n); bytes = 4+(n1+7)/8; #define bitvechdr_(n) (TYPE_BITVEC1 + ((((n)+7)&7)<<7)) w = getvector(TAG_VECTOR, bitvechdr_(n1), bytes); errexit(); n1 = doubleword_align_up(bytes); while (n1 > 4) { n1 -= 4; *(int32 *)((char *)w - TAG_VECTOR + n1) = 0; } return onevalue(w); } static Lisp_Object MS_CDECL Lbputv(Lisp_Object nil, int nargs, ...) { Header h; va_list a; int b; int32 n1; Lisp_Object v, n, x; argcheck(nargs, 3, "bputv"); va_start(a, nargs); v = va_arg(a, Lisp_Object); n = va_arg(a, Lisp_Object); x = va_arg(a, Lisp_Object); va_end(a); CSL_IGNORE(nil); /* * This code is WRONG at present in that unexpectedly it is supposed to * support bit-arrays of arbitrary rank, and not just simple vectors. */ if (!(is_vector(v)) || !header_of_bitvector(h = vechdr(v))) return aerror1("putv-bit", v); if (!is_fixnum(n)) return aerror1("putv-bit", n); if (!is_fixnum(x)) return aerror1("putv-bit contents", x); x = int_of_fixnum(x) & 1; h = length_of_header(h) - 4; n1 = int_of_fixnum(n); b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ /* * I am just a bit shoddy here - I only complain if an attempt is made to * access beyond the last active byte of a bitvector - I do not * do bound checking accurate to bit positions. */ if (n1 < 0 || n1 >= (int32)h) return aerror1("putv-bit", n); if (x == 0) ucelt(v, n1) &= ~b; else ucelt(v, n1) |= b; return onevalue(fixnum_of_int(x)); } static Lisp_Object Lbgetv(Lisp_Object nil, Lisp_Object v, Lisp_Object n) { Header h; int b; int32 n1; CSL_IGNORE(nil); /* * This code is WRONG at present in that unexpectedly it is supposed to * support bit-arrays of arbitrary rank, and not just simple vectors. */ if (!(is_vector(v)) || !header_of_bitvector(h = vechdr(v))) return aerror1("getv-bit", v); if (!is_fixnum(n)) return aerror1("getv-bit", n); h = length_of_header(h) - 4; n1 = int_of_fixnum(n); b = 1 << (n1 & 7); /* Bit selector */ n1 = n1 >> 3; /* Byte selector */ if (n1 < 0 || n1 >= (int32)h) return aerror1("getv-bit", n); if ((ucelt(v, n1) & b) == 0) return onevalue(fixnum_of_int(0)); else return onevalue(fixnum_of_int(1)); } #endif /* COMMON */ Lisp_Object Lupbv(Lisp_Object nil, Lisp_Object v) { Header h; int32 n; CSL_IGNORE(nil); /* * in non segmented mode this will support BPS, but really * you ought not to rely on that. */ if (!(is_vector(v))) return onevalue(nil); /* Standard Lisp demands.. */ h = vechdr(v); n = length_of_header(h) - 4; #ifdef COMMON if (header_of_bitvector(h)) { n = (n - 1)*8; n += ((h & 0x380) >> 7) + 1; } else #endif switch (type_of_header(h)) { case TYPE_STRING: case TYPE_VEC8: break; case TYPE_VEC16: n = n >> 1; break; case TYPE_FLOAT64: n = (n - 4) >> 3; break; default: n = n >> 2; break; } n--; /* c.f. mkvect */ return onevalue(fixnum_of_int(n)); } #ifdef COMMON Lisp_Object Lvecbnd(Lisp_Object nil, Lisp_Object v) { Header h; int32 n; CSL_IGNORE(nil); /* * in non segmented mode this will support BPS, but really * you ought not to rely on that. */ if (!(is_vector(v))) return aerror1("vector-bound", v); h = vechdr(v); n = length_of_header(h) - 4; if (header_of_bitvector(h)) { n = (n - 1)*8; n += ((h & 0x380) >> 7) + 1; } else switch (type_of_header(h)) { case TYPE_STRING: case TYPE_VEC8: break; case TYPE_VEC16: n = n >> 1; break; case TYPE_FLOAT64: n = (n - 4) >> 3; break; default: n = n >> 2; break; } return onevalue(fixnum_of_int(n)); } #endif #ifdef COMMON /* * The following were added for efficiency reasons, MCD 14/8/96 */ Lisp_Object list_subseq(Lisp_Object sequence, int32 start, int32 end) { Lisp_Object nil=C_nil, copy, last, new, seq=sequence; int32 i, seq_length, pntr = start; seq_length = end - start; /* Find start of subsequence */ while (consp(seq) && pntr > 0) { pntr--; seq = qcdr(seq); } if (!consp(seq)) return aerror1("subseq",sequence); copy = nil; /* Store the values */ push(sequence); while (consp(seq) && pntr < seq_length) { push3(seq,copy,last); new = Lcons(nil,qcar(seq),nil); pop3(last,copy,seq); if (pntr == 0) copy = new; else qcdr(last) = new; last = new; seq = qcdr(seq); pntr++; } pop(sequence); errexit(); if (pntr != seq_length) return aerror1("subseq",sequence); return onevalue(copy); } Lisp_Object vector_subseq(Lisp_Object sequence, int32 start, int32 end) { Lisp_Object nil=C_nil, copy; Header h; int32 hl, seq_length, i; if (is_cons(sequence)) return list_subseq(sequence,start,end); else if (!is_vector(sequence)) return aerror1("vector-subseq*",sequence); seq_length = end - start; h = vechdr(sequence); if (type_of_header(h) == TYPE_SIMPLE_VEC ) { hl = (length_of_header(h) - 4) >> 2; if (hl < end) return aerror0("vector-subseq* out of range"); /* * Since we are dealing with a simple vector the following shift is * guarenteed to work. The extra 4 bytes are for the header. */ copy = getvector_init(4+(seq_length << 2),nil); for (i=start; i < end; ++i) elt(copy,i-start) = elt(sequence,i); return onevalue(copy); } else if (type_of_header(h) == TYPE_STRING) { char *s; int32 k; hl = length_of_header(h) - 4; if (hl < end) return aerror0("vector-subseq* out of range"); /* Get a new string of the right size */ push(sequence); copy = getvector(TAG_VECTOR, TYPE_STRING, 4+seq_length); pop(sequence); /* This code plagiarised from copy_string ... */ s = (char *)copy - TAG_VECTOR; k = (seq_length + 3) & ~(int32)7; errexit(); *(int32 *)(s + k + 4) = 0; if (k != 0) *(int32 *)(s + k) = 0; memcpy(s + 4, (char *)sequence+(4L-TAG_VECTOR)+start, (size_t)seq_length); return onevalue(copy); } else if (header_of_bitvector(h)) { hl = length_of_header(h) - 4; if (hl < (end >> 3)) return aerror0("vector-subseq* out of range"); /* Grab a bit-vector of the right size */ push(sequence); copy = Lmake_simple_bitvector(nil,fixnum_of_int(seq_length)); pop(sequence); errexit(); /* * This is not terribly efficient since the calls to Lbputv and Lbgetv * ought to be coded inline, but on the other hand its no worse than the * original Lisp-coded version. */ for (i=start; i<end; ++i) { push2(sequence,copy); Lbputv(nil,3,copy,fixnum_of_int(i-start), Lbgetv(nil,sequence,fixnum_of_int(i))); pop2(copy,sequence); errexit(); } return onevalue(copy); } else if (type_of_header(h) == TYPE_ARRAY) { /* elt(sequence, 1) is the list of dimensions - only handle 1-d case */ if (qcdr(elt(sequence, 1)) != nil) return aerror1("vector-subseq*",sequence); i = int_of_fixnum(elt(sequence, 3)); /* displaced-index-offset */ return vector_subseq(elt(sequence,2),start+i,end+i); } else return aerror1("vector-subseq*",sequence); } Lisp_Object Llist_subseq1(Lisp_Object nil, Lisp_Object seq, Lisp_Object start) { Lisp_Object len; int32 first, last; first = int_of_fixnum(start); push(seq); len = Llength(nil,seq); pop(seq); errexit(); last = int_of_fixnum(len); if (first > last) return aerror1("list-subseq* out of range",seq); return list_subseq(seq, first, last); } Lisp_Object MS_CDECL Llist_subseq2(Lisp_Object nil, int32 nargs, ...) { va_list args; int32 first, last; Lisp_Object seq, start, end; argcheck(nargs, 3, "list-subseq*"); va_start(args, nargs); seq = va_arg(args, Lisp_Object); start = va_arg(args, Lisp_Object); end = va_arg(args, Lisp_Object); va_end(args); first = int_of_fixnum(start); last = int_of_fixnum(end); if (first > last) return aerror1("list-subseq* out of range",seq); return list_subseq(seq, first, last); } Lisp_Object Lvector_subseq1(Lisp_Object nil, Lisp_Object seq, Lisp_Object start) { Lisp_Object len; int32 first, last; first = int_of_fixnum(start); push(seq); len = Llength(nil,seq); pop(seq); errexit(); last = int_of_fixnum(len); if (first > last) return aerror1("vector-subseq* out of range",seq); return vector_subseq(seq, first, last); } Lisp_Object MS_CDECL Lvector_subseq2(Lisp_Object nil, int32 nargs, ...) { va_list args; int32 first, last; Lisp_Object seq, start, end; argcheck(nargs, 3, "vector-subseq*"); va_start(args, nargs); seq = va_arg(args, Lisp_Object); start = va_arg(args, Lisp_Object); end = va_arg(args, Lisp_Object); va_end(args); first = int_of_fixnum(start); last = int_of_fixnum(end); if (first > last) return aerror1("vector-subseq* out of range",seq); return vector_subseq(seq, first, last); } #endif setup_type const funcs3_setup[] = { {"getv", too_few_2, Lgetv, wrong_no_2}, {"putv", wrong_no_3a, wrong_no_3b, Lputv}, {"getv8", too_few_2, Lgetv8, wrong_no_2}, {"putv8", wrong_no_3a, wrong_no_3b, Lputv8}, {"getv16", too_few_2, Lgetv16, wrong_no_2}, {"putv16", wrong_no_3a, wrong_no_3b, Lputv16}, {"getv32", too_few_2, Lgetv32, wrong_no_2}, {"putv32", wrong_no_3a, wrong_no_3b, Lputv32}, {"fgetv32", too_few_2, Lfgetv32, wrong_no_2}, {"fputv32", wrong_no_3a, wrong_no_3b, Lfputv32}, {"fgetv64", too_few_2, Lfgetv64, wrong_no_2}, {"fputv64", wrong_no_3a, wrong_no_3b, Lfputv64}, {"qgetv", too_few_2, Lgetv, wrong_no_2}, {"egetv", too_few_2, Lgetv, wrong_no_2}, {"qputv", wrong_no_3a, wrong_no_3b, Lputv}, {"eputv", wrong_no_3a, wrong_no_3b, Lputv}, {"make-simple-string", Lsmkvect, too_many_1, wrong_no_1}, {"putv-char", wrong_no_3a, wrong_no_3b, Lsputv}, {"bps-putv", wrong_no_3a, wrong_no_3b, Lbpsputv}, {"bps-getv", too_few_2, Lbpsgetv, wrong_no_2}, {"bps-upbv", Lbpsupbv, too_many_1, wrong_no_1}, {"native-type", wrong_no_na, wrong_no_nb, Lnative_type}, {"native-putv", wrong_no_3a, wrong_no_3b, Lnativeputv}, {"native-getv", too_few_2, Lnativegetv, Lnativegetvn}, {"native-address", Lnative_address1, Lnative_address, wrong_no_2}, {"eupbv", Lupbv, too_many_1, wrong_no_1}, {"schar", too_few_2, Lsgetv, wrong_no_2}, {"scharn", too_few_2, Lsgetvn, wrong_no_2}, {"byte-getv", too_few_2, Lbytegetv, wrong_no_2}, {"mkvect", Lmkvect, too_many_1, wrong_no_1}, {"mkevect", Lmkevect, too_many_1, wrong_no_1}, {"mkxvect", Lmkxvect, too_many_1, wrong_no_1}, {"mkvect8", Lmkvect8, too_many_1, wrong_no_1}, {"mkvect16", Lmkvect16, too_many_1, wrong_no_1}, {"mkvect32", Lmkvect32, too_many_1, wrong_no_1}, {"mkfvect32", Lmkfvect32, too_many_1, wrong_no_1}, {"mkfvect64", Lmkfvect64, too_many_1, wrong_no_1}, {"mkhash", wrong_no_3a, wrong_no_3b, Lmkhash}, {"gethash", Lget_hash_1, Lget_hash_2, Lget_hash}, {"puthash", wrong_no_3a, Lput_hash_2, Lput_hash}, {"remhash", Lrem_hash_1, Lrem_hash, wrong_no_2}, {"clrhash", Lclr_hash, too_many_1, Lclr_hash_0}, {"sxhash", Lsxhash, too_many_1, wrong_no_1}, {"eqlhash", Leqlhash, too_many_1, wrong_no_1}, {"maphash", too_few_2, Lmaphash, wrong_no_2}, {"hashcontents", Lhashcontents, too_many_1, wrong_no_1}, {"upbv", Lupbv, too_many_1, wrong_no_1}, #ifdef COMMON {"hashtable-flavour", Lhash_flavour, too_many_1, wrong_no_1}, {"getv-bit", too_few_2, Lbgetv, wrong_no_2}, {"sbit", too_few_2, Lbgetv, wrong_no_2}, {"make-simple-bitvector", Lmake_simple_bitvector, too_many_1, wrong_no_1}, {"make-simple-vector", Lmksimplevec, too_many_1, wrong_no_1}, {"putv-bit", wrong_no_3a, wrong_no_3b, Lbputv}, {"sbitset", wrong_no_3a, wrong_no_3b, Lbputv}, {"svref", too_few_2, Lgetv, wrong_no_2}, {"vector-bound", Lvecbnd, too_many_1, wrong_no_1}, {"putvec", wrong_no_3a, wrong_no_3b, Lputvec}, {"aref", Laref1, Laref2, Laref}, {"aset", Laset1, Laset2, Laset}, {"elt", too_few_2, Lelt, wrong_no_2}, {"setelt", wrong_no_3a, wrong_no_3b, Lsetelt}, {"vectorp", Lvectorp, too_many_1, wrong_no_1}, {"char", too_few_2, Lchar, wrong_no_2}, {"charset", wrong_no_3a, wrong_no_3b, Lcharset}, {"make-string", Lmake_string1, Lmake_string2, Lmake_string}, {"list-to-vector", Llist_to_vector, too_many_1, wrong_no_1}, {"vector", Lvector1, Lvector2, Lvector}, {"shrink-vector", too_few_2, Lshrink_vector, wrong_no_2}, {"string", Lstring, too_many_1, wrong_no_1}, #ifdef COMMON {"vector-subseq*", wrong_no_3a, Lvector_subseq1, Lvector_subseq2}, {"list-subseq*", wrong_no_3a, Llist_subseq1, Llist_subseq2}, {"subseq", wrong_no_3a, Lvector_subseq1, Lvector_subseq2}, #endif /* The "x" is temporary while I debug */ {"xcopy-vector", Lcopy_vector, too_many_1, wrong_no_1}, #endif {NULL, 0, 0, 0} }; /* end of fns3.c */