# Steak---performance patches for MOO 1.8.0p5.

Jay Carlson (nop@kagoona.mitre.org)
Tue, 31 Dec 1996 13:35:11 PST

- Messages sorted by:[ date ][ thread ][ subject ][ author ]

    Steak---performance patches for MOO 1.8.0p5.

    So, about a month ago, Uther_Locksley on LambdaMOO proposed on *core a
    bunch of minor changes to some command verbs that made them slightly
    more tick-efficient at the cost of some readability.  In the ensuing
    discussion, I realized I had no idea how expensive some common
    operations were, so I started playing around with statements like:

    argstr = "foo";
    n = 0;
    s = seconds_left();
    while (s == seconds_left())
       "waste time to get to an integral number of seconds";
       #1:name();
    endwhile
    s = seconds_left() - 4;
    while (s <= seconds_left())
      player.location:say();
      n = n + 1;
    endwhile
    return n;

    which counts the number of times you can call room:say() in five CPU
    seconds.

    One of the most striking things from this was exactly how expensive it
    was to call a verb on an object, if the verb was defined on a distant
    parent.  For example, in JHcore, :name is defined on #1 and whenever
    you called :name on a $wiz, the server linearly searched through 578
    verb definitions to find it.  When I defined :name on the object being
    tested itself, it was much faster.

    The first thing I did was start to tweak gcc options to see if there was
    some cheap way of improving the speed of the server.  There was some
    performance increase without source modification, but there was a
    bigger win from allowing the compiler to inline some functions from
    utils.c---in particular, verbcasecmp, which was showing up near the
    top of the profile results.

    In an attempt to eliminate the calls to verbcasecmp, I built a simple
    per-object callable verb cache that sped up the :name test by at least
    450%.  On real-world tests like say and emote, the increase was more
    like 15-25%.

    The next victim in the profiler was builtin property protection.  Not
    many people use the 1.8.0 feature to protect, say, .location or .name,
    and it added about 5-15% cost to all the tests.  I made it a
    compile-time option.

    That's where things stand now.  The next obvious target is built-in
    function protection---it should be possible to store protection
    information in the builtin function structure itself to avoid an
    expensive property lookup on every builtin call.  Also, the cache
    residency is, uh, suboptimal and I plan on playing with the strategies
    a little more.

    At some point I'm going to go after list manipulation and other bigger
    game, but I figured that doing a safe set of optimizations now that
    don't change semantics significantly would be of immediate use.

    Again, without Uther, I wouldn't have been prodded into it.  Have fun,
    and let me know if I've done anything stupid or you have ideas.

    Jay Carlson   nop@kagoona.mitre.org    nop@nop.com
    The MITRE Corporation, Bedford MA

    Flat text is just *never* what you want.       ---stephen p spackman

    --------------------------
    Message 2900 on *Core-DB-Issues (#8175):
    Date:    Sun Nov 17 18:06:32 1996 PST
    From:    Uther_Locksley (#93141)
    To:      *Core-DB-Issues (#8175)

    @create is hardly ever run? @quota is hardly ever run? I'm talking
    about streamlining the entire core to improve the over all efficiency
    of the entire MOO and/or of any MOO that uses the next core.
    --------------------------

    I did the benchmarking on a Pentium 75 with 32M of RAM and a 256k
    async cache under Debian GNU/Linux 1.1.  I was running the latest
    version of JHcore, with 10 $things in the first room with Wizard.
    Here are the primary evals I used.

    ;;argstr = "foo"; n = 0; s = seconds_left(); while (s == seconds_left()) #1:name(); endwhile s = seconds_left() - 4; while (s <= seconds_left()) n = n + 1; endwhile return n

    ;;argstr = "foo"; n = 0; s = seconds_left(); while (s == seconds_left()) #1:name(); endwhile s = seconds_left() - 4; while (s <= seconds_left()) player:name(); n = n + 1; endwhile return n

    ;;argstr = "foo"; n = 0; s = seconds_left(); while (s == seconds_left()) #1:name(); endwhile s = seconds_left() - 4; while (s <= seconds_left()) player.location:say(); n = n + 1; endwhile return n

    ;;argstr = "foo"; n = 0; s = seconds_left(); while (s == seconds_left()) #1:name(); endwhile s = seconds_left() - 4; while (s <= seconds_left()) player.location:emote(); n = n + 1; endwhile return n

    ;;argstr = "foo"; n = 0; s = seconds_left(); while (s == seconds_left()) #1:name(); endwhile s = seconds_left() - 4; while (s <= seconds_left()) $string_utils:pronoun_sub("%N %<waves>."); n = n + 1; endwhile return n

    --------------------------

    diff -x *.orig -cr MOO-1.8.0p5/Makefile.in MOO-1.8.0p5steak/Makefile.in
    *** MOO-1.8.0p5/Makefile.in	Thu Apr 18 21:28:40 1996
    --- MOO-1.8.0p5steak/Makefile.in	Tue Dec 31 16:25:54 1996
    ***************
    *** 18,23 ****
    --- 18,26 ----
      YACC = @YACC@

      CFLAGS = -O
    + # If you're using GCC, you may prefer:
    + # CFLAGS = -O2 -finline-functions
    +
      YFLAGS = -d
      COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) -c

    ***************
    *** 46,52 ****
      	options.h parse_cmd.h parser.h pattern.h program.h quota.h random.h \
      	ref_count.h regexpr.h server.h storage.h streams.h structures.h \
      	sym_table.h tasks.h timers.h tokens.h unparse.h utils.h verbs.h \
    ! 	version.h

      SYSHDRS = my-ctype.h my-fcntl.h my-in.h my-inet.h my-ioctl.h my-math.h \
      	my-poll.h my-signal.h my-socket.h my-stat.h my-stdarg.h my-stdio.h \
    --- 49,55 ----
      	options.h parse_cmd.h parser.h pattern.h program.h quota.h random.h \
      	ref_count.h regexpr.h server.h storage.h streams.h structures.h \
      	sym_table.h tasks.h timers.h tokens.h unparse.h utils.h verbs.h \
    ! 	version.h inline.h

      SYSHDRS = my-ctype.h my-fcntl.h my-in.h my-inet.h my-ioctl.h my-math.h \
      	my-poll.h my-signal.h my-socket.h my-stat.h my-stdarg.h my-stdio.h \
    ***************
    *** 137,143 ****
      	echo '$$r makedep' >>eddep
      	echo 'w' >>eddep
      	cp Makefile.in Makefile.in.bak
    ! 	ex - Makefile.in < eddep
      	rm -f eddep makedep

      ###############################################################################
    --- 140,146 ----
      	echo '$$r makedep' >>eddep
      	echo 'w' >>eddep
      	cp Makefile.in Makefile.in.bak
    ! 	ed - Makefile.in < eddep
      	rm -f eddep makedep

      ###############################################################################
    ***************
    *** 256,262 ****
      # Revision 1.3  1992/07/27  18:30:21  pjames
      # Update what vector.o and vector.po depend on.
      ###############################################################################
    !
      # Have to do this one manually, since 'make depend' can't hack yacc files.
      parser.o:	my-ctype.h my-math.h my-stdlib.h my-string.h \
      		ast.h code_gen.h config.h functions.h \
    --- 259,265 ----
      # Revision 1.3  1992/07/27  18:30:21  pjames
      # Update what vector.o and vector.po depend on.
      ###############################################################################
    !
      # Have to do this one manually, since 'make depend' can't hack yacc files.
      parser.o:	my-ctype.h my-math.h my-stdlib.h my-string.h \
      		ast.h code_gen.h config.h functions.h \
    diff -x *.orig -cr MOO-1.8.0p5/db_objects.c MOO-1.8.0p5steak/db_objects.c
    *** MOO-1.8.0p5/db_objects.c	Sun Apr  7 20:42:11 1996
    --- MOO-1.8.0p5steak/db_objects.c	Tue Dec 31 16:25:55 1996
    ***************
    *** 89,100 ****
    --- 89,107 ----
      dbpriv_new_object(void)
      {
          Object     *o;
    +     int i;

          ensure_new_object();
          o = objects[num_objects] = mymalloc(sizeof(Object), M_OBJECT);
          o->id = num_objects;
          num_objects++;

    + #ifdef DO_VERBCACHE
    +     o->verbcache.generation = 0;
    +     for (i = 0; i < VERBCACHE_ENTRIES; i++)
    +       o->verbcache.e[i].searched_name = NULL;
    + #endif
    +
          return o;
      }

    ***************
    *** 137,142 ****
    --- 144,151 ----
          Verbdef    *v, *w;
          int 	i;

    +     db_priv_affected_callable_verb_lookup();
    +
          if (!o)
      	panic("DB_DESTROY_OBJECT: Invalid object!");

    ***************
    *** 174,179 ****
    --- 183,197 ----
      	myfree(v, M_VERBDEF);
          }

    + #ifdef DO_VERBCACHE
    +     for (i = 0; i < VERBCACHE_ENTRIES; i++) {
    +       if (o->verbcache.e[i].searched_name) {
    +         free_str(o->verbcache.e[i].searched_name);
    +         o->verbcache.e[i].searched_name = NULL;
    +       }
    +     }
    + #endif
    +
          myfree(objects[oid], M_OBJECT);
          objects[oid] = 0;
      }
    ***************
    *** 184,189 ****
    --- 202,209 ----
          Objid	new;
          Object     *o;

    +     db_priv_affected_callable_verb_lookup();
    +
          for (new = 0; new < old; new++) {
      	if (objects[new] == 0) {
      	    /* Change the identity of the object. */
    ***************
    *** 406,411 ****
    --- 426,433 ----
      db_change_parent(Objid oid, Objid parent)
      {
          Objid	old_parent;
    +
    +     db_priv_affected_callable_verb_lookup();

          if (!dbpriv_check_properties_for_chparent(oid, parent))
      	return 0;
    diff -x *.orig -cr MOO-1.8.0p5/db_private.h MOO-1.8.0p5steak/db_private.h
    *** MOO-1.8.0p5/db_private.h	Thu Feb  8 01:27:29 1996
    --- MOO-1.8.0p5steak/db_private.h	Tue Dec 31 16:25:55 1996
    ***************
    *** 21,26 ****
    --- 21,27 ----

      #include "config.h"
      #include "exceptions.h"
    + #include "options.h"
      #include "program.h"
      #include "structures.h"

    ***************
    *** 55,60 ****
    --- 56,117 ----
          short	perms;
      } Pval;

    + #ifdef DO_VERBCACHE
    +
    + /* Verb lookup cache.
    +  *
    +  * The cache is intentionally very simple.  Due to the problematic
    +  * verb match rules the MOO language maintains, we can't just use a
    +  * straight hash table for verb names.  I didn't want to get into the
    +  * morass of command-line matched verbs either.  All the current code
    +  * does is remember what Verbdef we found when searching for a
    +  * callable verb with a given string.
    +  *
    +  * Cache entries
    +  *
    +  * If the searched_name field of an entry is non-NULL, the entry
    +  * itself is valid.  When the entry is invalidated, we free the
    +  * searched_name string and NULL it out atomically.
    +  *
    +  * Invalidation
    +  *
    +  * We keep a global generation counter that's incremented whenever any
    +  * action could affect the result of a callable verb lookup.  It's currently
    +  * very conservative---it errs on the side of more generations for safety.
    +  *
    +  * Each object's cache has its own generation count.  When we look up
    +  * a verb on an object with an mismatch, we invalidate all the old
    +  * cache entries.  (If you're worried about the invalid searched_name
    +  * strings hanging around your process, just call any verb on every
    +  * object.)
    +  *
    +  */
    +
    + typedef struct Verbcache_entry {
    +   unsigned searched_hash;
    +   const char *searched_name;
    +   Objid definer;
    +   Verbdef *verbdef;
    + } Verbcache_entry;
    +
    + typedef struct Verbcache {
    +   unsigned int generation;
    +   Verbcache_entry e[VERBCACHE_ENTRIES];
    + } Verbcache;
    +
    + /* Every time we modify anything that could influence callable verb
    +  * lookup we increment this variable.  */
    +
    + extern unsigned int db_verb_generation;
    +
    + #define db_priv_affected_callable_verb_lookup() (db_verb_generation++)  /* The choice of a new generation. */
    +
    + #else /* no cache */
    +
    + #define db_priv_affected_callable_verb_lookup()
    +
    + #endif
    +
      typedef struct Object {
          Objid	id;
          Objid	owner;
    ***************
    *** 73,79 ****
    --- 130,141 ----
          Verbdef    *verbdefs;
          Proplist	propdefs;
          Pval       *propval;
    +
    + #ifdef DO_VERBCACHE
    +   Verbcache verbcache;
    + #endif
      } Object;
    +

      /*********** Objects ***********/

    diff -x *.orig -cr MOO-1.8.0p5/db_verbs.c MOO-1.8.0p5steak/db_verbs.c
    *** MOO-1.8.0p5/db_verbs.c	Sun May 12 17:32:24 1996
    --- MOO-1.8.0p5steak/db_verbs.c	Tue Dec 31 16:25:55 1996
    ***************
    *** 191,196 ****
    --- 191,198 ----
      #define OBJMASK    0x3
      #define PERMMASK   0xF

    + unsigned int db_verb_generation = 0;
    +
      void
      db_add_verb(Objid oid, const char *vnames, Objid owner, unsigned flags,
      	    db_arg_spec dobj, db_prep_spec prep, db_arg_spec iobj)
    ***************
    *** 198,203 ****
    --- 200,207 ----
          Object     *o = dbpriv_find_object(oid);
          Verbdef    *v, *newv;

    +     db_priv_affected_callable_verb_lookup();
    +
          newv = mymalloc(sizeof(Verbdef), M_VERBDEF);
          newv->name = vnames;
          newv->owner = owner;
    ***************
    *** 268,273 ****
    --- 272,279 ----
          Object     *o = dbpriv_find_object(oid);
          Verbdef    *vv;

    +     db_priv_affected_callable_verb_lookup();
    +
          vv = o->verbdefs;
          if (vv == v)
      	o->verbdefs = v->next;
    ***************
    *** 315,334 ****
          return vh;
      }

      db_verb_handle
      db_find_callable_verb(Objid oid, const char *verb)
      {
    !     Object	       *o;
          Verbdef	       *v;
          static handle	h;
          db_verb_handle	vh;

          for (o = dbpriv_find_object(oid); o; o = dbpriv_find_object(o->parent))
      	if ((v = find_verbdef_by_name(o, verb, 1)) != 0) {
      	    h.definer = o->id;
      	    h.verbdef = v;
    - 	    vh.ptr = &h;

      	    return vh;
      	}

    --- 321,395 ----
          return vh;
      }

    + #ifdef DO_VERBCACHE
    + /* If you're running the server in GDB, these variables might be useful. */
    +
    + int verbcache_generation_miss = 0;
    + int verbcache_hit = 0;
    + int verbcache_cache_miss = 0;
    + #endif
    +
      db_verb_handle
      db_find_callable_verb(Objid oid, const char *verb)
      {
    !     Object	       *o, *top;
          Verbdef	       *v;
          static handle	h;
          db_verb_handle	vh;

    + #ifdef DO_VERBCACHE
    +     unsigned hash = str_hash(verb);
    +     unsigned potential_entry = hash % VERBCACHE_ENTRIES;
    +     int i;
    +
    +     top = dbpriv_find_object(oid);
    +     if (top) {
    +       if (top->verbcache.generation != db_verb_generation) {
    +         verbcache_generation_miss++;
    +
    +         for (i = 0; i < VERBCACHE_ENTRIES; i++) {
    +           if (top->verbcache.e[i].searched_name) {
    +             free_str(top->verbcache.e[i].searched_name);
    +             top->verbcache.e[i].searched_name = NULL;
    +           }
    +         }
    +         top->verbcache.generation = db_verb_generation;
    +       } else {
    +         if (top->verbcache.e[potential_entry].searched_name &&
    +             top->verbcache.e[potential_entry].searched_hash == hash &&
    +             !mystrcasecmp(verb, top->verbcache.e[potential_entry].searched_name)) {
    +           verbcache_hit++;
    +
    +           h.verbdef = top->verbcache.e[potential_entry].verbdef;
    +           h.definer = top->verbcache.e[potential_entry].definer;
    +           vh.ptr = &h;
    +           return vh;
    +         } else {
    +           verbcache_cache_miss++;
    +         }
    +       }
    +     }
    + #endif
    +
          for (o = dbpriv_find_object(oid); o; o = dbpriv_find_object(o->parent))
      	if ((v = find_verbdef_by_name(o, verb, 1)) != 0) {
      	    h.definer = o->id;
      	    h.verbdef = v;

    + #ifdef DO_VERBCACHE
    +             if (top->verbcache.e[potential_entry].searched_name) {
    +               free_str(top->verbcache.e[potential_entry].searched_name);
    +             }
    +
    +             /* If I understood more about the server, I'd feel better about
    +              * changing the following str_dup call to str_ref.
    +              */
    +             top->verbcache.e[potential_entry].searched_name = str_dup(verb);
    +             top->verbcache.e[potential_entry].searched_hash = hash;
    +             top->verbcache.e[potential_entry].definer = o->id;
    +             top->verbcache.e[potential_entry].verbdef = v;
    + #endif
    +             	    vh.ptr = &h;
      	    return vh;
      	}

    ***************
    *** 421,426 ****
    --- 482,489 ----
      {
          handle     *h = (handle *) vh.ptr;

    +     db_priv_affected_callable_verb_lookup();
    +
          if (h) {
      	if (h->verbdef->name)
      	    free_str(h->verbdef->name);
    ***************
    *** 469,474 ****
    --- 532,539 ----
      {
          handle     *h = (handle *) vh.ptr;

    +     db_priv_affected_callable_verb_lookup();
    +
          if (h) {
      	h->verbdef->perms &= ~PERMMASK;
      	h->verbdef->perms |= flags;
    ***************
    *** 496,501 ****
    --- 561,568 ----
      {
          handle     *h = (handle *) vh.ptr;

    +     db_priv_affected_callable_verb_lookup();
    +
          if (h) {
      	if (h->verbdef->program)
      	    free_program(h->verbdef->program);
    ***************
    *** 510,515 ****
    --- 577,584 ----
      {
          handle     *h = (handle *) vh.ptr;

    +     db_priv_affected_callable_verb_lookup();
    +
          if (h) {
      	*dobj = (h->verbdef->perms >> DOBJSHIFT) & OBJMASK;
      	*prep = h->verbdef->prep;
    ***************
    *** 523,528 ****
    --- 592,599 ----
      		      db_arg_spec dobj, db_prep_spec prep, db_arg_spec iobj)
      {
          handle     *h = (handle *) vh.ptr;
    +
    +     db_priv_affected_callable_verb_lookup();

          if (h) {
      	h->verbdef->perms = ((h->verbdef->perms & PERMMASK)
    diff -x *.orig -cr MOO-1.8.0p5/execute.c MOO-1.8.0p5steak/execute.c
    *** MOO-1.8.0p5/execute.c	Thu Apr 18 21:24:40 1996
    --- MOO-1.8.0p5steak/execute.c	Tue Dec 31 16:25:55 1996
    ***************
    *** 592,597 ****
    --- 592,600 ----
          return 0;
      }

    + #ifdef IGNORE_PROP_PROTECTED
    + #define bi_prop_protected(prop, progr) (0)
    + #else
      static int
      bi_prop_protected(enum bi_prop prop, Objid progr)
      {
    ***************
    *** 616,621 ****
    --- 619,626 ----

          return server_flag_option(pname);
      }
    + #endif
    +

      /**
        the main interpreter -- run()
    diff -x *.orig -cr MOO-1.8.0p5/inline.h MOO-1.8.0p5steak/inline.h
    *** MOO-1.8.0p5/inline.h	Tue Dec 31 16:27:49 1996
    --- MOO-1.8.0p5steak/inline.h	Tue Dec 31 16:26:30 1996
    ***************
    *** 0 ****
    --- 1,18 ----
    + #ifndef Inline_h
    + #define Inline_h 1
    +
    + /* #define DO_INLINES */
    +
    + #ifdef DO_INLINES
    +
    + #define EXTERN_INLINE extern inline
    + #define INLINE inline
    +
    + #else /* not doing inlines */
    +
    + #define EXTERN_INLINE extern
    + #define INLINE
    +
    + #endif /* DO_INLINES */
    +
    + #endif /* Inline_h */
    diff -x *.orig -cr MOO-1.8.0p5/options.h MOO-1.8.0p5steak/options.h
    *** MOO-1.8.0p5/options.h	Tue Mar 19 02:13:36 1996
    --- MOO-1.8.0p5steak/options.h	Tue Dec 31 16:25:55 1996
    ***************
    *** 80,87 ****

      #define DEFAULT_MAX_STACK_DEPTH	50

    ! #define DEFAULT_FG_TICKS	30000
    ! #define DEFAULT_BG_TICKS	15000

      #define DEFAULT_FG_SECONDS	5
      #define DEFAULT_BG_SECONDS	3
    --- 80,87 ----

      #define DEFAULT_MAX_STACK_DEPTH	50

    ! #define DEFAULT_FG_TICKS	300000
    ! #define DEFAULT_BG_TICKS	150000

      #define DEFAULT_FG_SECONDS	5
      #define DEFAULT_BG_SECONDS	3
    ***************
    *** 191,196 ****
    --- 191,250 ----
       */

      #define PATTERN_CACHE_SIZE	20
    +
    + /******************************************************************************
    +  * You may wish to experiment with inlining large numbers of functions
    +  * if your compiler supports the inline keyword and makes intelligent
    +  * choices about when it increases performance.  Right now, this only
    +  * is implemented for utils.c, and at least on a P75 with 256k of
    +  * async cache under gcc 2.7.2.1, it's a lose.
    +  ******************************************************************************
    +  */
    +
    + /* #define DO_INLINES */
    +
    + /******************************************************************************
    +  * GCC has a nice feature that allows function bodies declared "extern
    +  * inline" to be treated close to macros---if such a function can be
    +  * inlined, it is; otherwise a normal function call is compiled.  This
    +  * is currently used for verbcasecmp, which, according to some profile
    +  * results, is one of the most expensive verbs in the server.  Define
    +  * this only if your compiler supports "extern inline".
    +  ******************************************************************************
    +  */
    +
    + #define DO_EXTERN_INLINE
    +
    +
    + /******************************************************************************
    +  * If you don't plan on using protecting built-in properties (like
    +  * .name and .location), define IGNORE_PROP_PROTECTED.  The extra
    +  * property lookups on every reference to a built-in property are
    +  * expensive.
    +  ******************************************************************************
    +  */
    +
    + #define IGNORE_PROP_PROTECTED
    +
    + /******************************************************************************
    +  * Profiling the server revealed a lot of time spent looking up verbs.
    +  * A per-object verb cache substantially improves overall performance
    +  * at the cost of additional memory used for each object.  Define
    +  * DO_VERBCACHE to enable it.
    +  *
    +  * The larger the number of verb cache entries, the better.  On a
    +  * typical 32-bit machine, each entry costs 16 bytes, plus the length
    +  * of the verb names that haven't been invalidated.  A 7-entry cache
    +  * appears to be too small; a 17-entry cache performed much better in
    +  * some benchmarks.
    +  *
    +  * See remarks in db_private.h for further information.
    +
    +  ******************************************************************************
    +  */
    +
    + #define DO_VERBCACHE
    + #define VERBCACHE_ENTRIES 17

      /******************************************************************************
       * This package comes with a copy of the implementation of malloc() from GNU
    diff -x *.orig -cr MOO-1.8.0p5/server.c MOO-1.8.0p5steak/server.c
    *** MOO-1.8.0p5/server.c	Sun May 12 17:26:11 1996
    --- MOO-1.8.0p5steak/server.c	Tue Dec 31 16:25:55 1996
    ***************
    *** 906,912 ****
          while (*line && p < e)
      	*p++ = *line++;
          while (p < e)
    ! 	*p++ = ' ';		/* Pad with blanks, not nulls; on SunOS and
      				 * maybe other systems, nulls would confuse
      				 * `ps'.  (*sigh*)
      				 */
    --- 906,912 ----
          while (*line && p < e)
      	*p++ = *line++;
          while (p < e)
    !  	*p++ = ' ';		/* Pad with blanks, not nulls; on SunOS and
      				 * maybe other systems, nulls would confuse
      				 * `ps'.  (*sigh*)
      				 */
    diff -x *.orig -cr MOO-1.8.0p5/utils.c MOO-1.8.0p5steak/utils.c
    *** MOO-1.8.0p5/utils.c	Sun Apr  7 20:43:09 1996
    --- MOO-1.8.0p5steak/utils.c	Tue Dec 31 16:25:55 1996
    ***************
    *** 15,20 ****
    --- 15,22 ----
          Pavel@Xerox.Com
       *****************************************************************************/

    + #define Utils_c 1
    +
      #include "my-ctype.h"
      #include "my-stdio.h"
      #include "my-string.h"
    ***************
    *** 39,45 ****
       * We implement them here because neither one is in the ANSI standard.
       */

    ! static const char cmap[] =
        "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
        "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
        "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
    --- 41,48 ----
       * We implement them here because neither one is in the ANSI standard.
       */

    !
    ! const char strcase_cmap[] =
        "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
        "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
        "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
    ***************
    *** 57,62 ****
    --- 60,68 ----
        "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
        "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377";

    + #define cmap strcase_cmap
    +
    + INLINE
      int
      mystrcasecmp(const char *ss, const char *tt)
      {
    ***************
    *** 70,75 ****
    --- 76,82 ----
          return(cmap[*s] - cmap[*--t]);
      }

    + INLINE
      int
      mystrncasecmp(const char *ss, const char *tt, int n)
      {
    ***************
    *** 85,90 ****
    --- 92,98 ----
          return(cmap[*s] - cmap[*--t]);
      }

    + INLINE
      int
      verbcasecmp(const char *verb, const char *word)
      {
    ***************
    *** 116,121 ****
    --- 124,130 ----
          return 0;
      }

    + INLINE
      unsigned
      str_hash(const char *s)
      {
    ***************
    *** 130,135 ****
    --- 139,145 ----
          return ans;
      }

    + INLINE
      void
      free_var(Var v)
      {
    ***************
    *** 165,170 ****
    --- 175,181 ----
          }
      }

    + INLINE
      Var
      var_ref(Var v)
      {
    ***************
    *** 192,197 ****
    --- 203,209 ----
          return v;
      }

    + INLINE
      Var
      var_dup(Var v)
      {
    ***************
    *** 225,230 ****
    --- 237,243 ----
          return v;
      }

    + INLINE
      int
      is_true(Var v)
      {
    ***************
    *** 234,239 ****
    --- 247,253 ----
      	    || (v.type == TYPE_LIST   &&  v.v.list[0].v.num != 0));
      }

    + INLINE
      int
      equality(Var lhs, Var rhs, int case_matters)
      {
    ***************
    *** 273,278 ****
    --- 287,293 ----
          return 0;
      }

    + INLINE
      char *
      strsub(const char *source, const char *what, const char *with, int case_counts)
      {
    ***************
    *** 294,299 ****
    --- 309,315 ----
          return reset_stream(str);
      }

    + INLINE
      int
      strindex(const char *source, const char *what, int case_counts)
      {
    ***************
    *** 309,314 ****
    --- 325,331 ----
          return 0;
      }

    + INLINE
      int
      strrindex(const char *source, const char *what, int case_counts)
      {
    ***************
    *** 324,329 ****
    --- 341,347 ----
          return 0;
      }

    + INLINE
      Var
      get_system_property(const char *name)
      {
    ***************
    *** 344,349 ****
    --- 362,368 ----
          return value;
      }

    + INLINE
      Objid
      get_system_object(const char *name)
      {
    ***************
    *** 357,362 ****
    --- 376,382 ----
      	return value.v.obj;
      }

    + INLINE
      int
      value_bytes(Var v)
      {
    ***************
    *** 382,387 ****
    --- 402,408 ----
          return size;
      }

    + INLINE
      const char *
      raw_bytes_to_binary(const char *buffer, int buflen)
      {
    ***************
    *** 403,408 ****
    --- 424,430 ----
          return reset_stream(s);
      }

    + INLINE
      const char *
      binary_to_raw_bytes(const char *binary, int *buflen)
      {
    ***************
    *** 439,445 ****
    --- 461,469 ----
          return reset_stream(s);
      }

    + #ifndef INLINING_Utils_c
      char rcsid_utils[] = "$Id: utils.c,v 2.8 1996/04/08 00:43:09 pavel Exp $";
    + #endif

      /* $Log: utils.c,v $
       * Revision 2.8  1996/04/08  00:43:09  pavel
    diff -x *.orig -cr MOO-1.8.0p5/utils.h MOO-1.8.0p5steak/utils.h
    *** MOO-1.8.0p5/utils.h	Thu Feb  8 01:08:07 1996
    --- MOO-1.8.0p5steak/utils.h	Tue Dec 31 16:25:55 1996
    ***************
    *** 23,55 ****
      #include "config.h"
      #include "execute.h"

    ! #define Arraysize(x) (sizeof(x) / sizeof(*x))
    !
    ! extern int		mystrcasecmp(const char *, const char *);
    ! extern int		mystrncasecmp(const char *, const char *, int);
    !
    ! extern int		verbcasecmp(const char *verb, const char *word);

    ! extern unsigned		str_hash(const char *);
    !
    ! extern void		free_var(Var);
    ! extern Var		var_dup(Var);
    ! extern Var      	var_ref(Var);
    !
    ! extern int		equality(Var lhs, Var rhs, int case_matters);
    ! extern int 		is_true(Var v);

    ! extern char    	       *strsub(const char *, const char *, const char *, int);
    ! extern int		strindex(const char *, const char *, int);
    ! extern int		strrindex(const char *, const char *, int);

    - extern Var		get_system_property(const char *);
    - extern Objid		get_system_object(const char *);

    ! extern int		value_bytes(Var);

    ! extern const char      *raw_bytes_to_binary(const char *buffer, int buflen);
    ! extern const char      *binary_to_raw_bytes(const char *binary, int *rawlen);

      #endif

    --- 23,103 ----
      #include "config.h"
      #include "execute.h"

    ! #include "inline.h"

    ! #define Arraysize(x) (sizeof(x) / sizeof(*x))

    ! EXTERN_INLINE int		mystrcasecmp(const char *, const char *);
    ! EXTERN_INLINE int		mystrncasecmp(const char *, const char *, int);

    ! /* The single most expensive function there is */

    ! #ifdef DO_EXTERN_INLINE
    ! extern inline
    ! int
    ! verbcasecmp(const char *verb, const char *word)
    ! {
    !   extern const char strcase_cmap[];
    !
    !     const unsigned char        *w;
    !     const unsigned char        *v = (const unsigned char *) verb;
    !     enum { none, inner, end }	star;
    !
    !     while (*v) {
    ! 	w = (const unsigned char *) word;
    ! 	star = none;
    ! 	while (1) {
    ! 	    while (*v == '*') {
    ! 		v++;
    ! 		star = (!*v || *v == ' ') ? end : inner;
    ! 	    }
    ! 	    if (!*v || *v == ' ' || !*w || strcase_cmap[*w] != strcase_cmap[*v])
    ! 		break;
    ! 	    w++;
    ! 	    v++;
    ! 	}
    ! 	if (!*w ? (star != none || !*v || *v == ' ')
    ! 	        : (star == end))
    ! 	    return 1;
    ! 	while (*v && *v != ' ')
    ! 	    v++;
    ! 	while (*v == ' ')
    ! 	    v++;
    !     }
    !     return 0;
    ! }
    ! #else
    ! EXTERN_INLINE int		verbcasecmp(const char *verb, const char *word);
    ! #endif
    !
    ! EXTERN_INLINE unsigned		str_hash(const char *);
    !
    ! EXTERN_INLINE void		free_var(Var);
    ! EXTERN_INLINE Var		var_dup(Var);
    ! EXTERN_INLINE Var      	var_ref(Var);
    !
    ! EXTERN_INLINE int		equality(Var lhs, Var rhs, int case_matters);
    ! EXTERN_INLINE int 		is_true(Var v);
    !
    ! EXTERN_INLINE char    	       *strsub(const char *, const char *, const char *, int);
    ! EXTERN_INLINE int		strindex(const char *, const char *, int);
    ! EXTERN_INLINE int		strrindex(const char *, const char *, int);
    !
    ! EXTERN_INLINE Var		get_system_property(const char *);
    ! EXTERN_INLINE Objid		get_system_object(const char *);
    !
    ! EXTERN_INLINE int		value_bytes(Var);
    !
    ! EXTERN_INLINE const char      *raw_bytes_to_binary(const char *buffer, int buflen);
    ! EXTERN_INLINE const char      *binary_to_raw_bytes(const char *binary, int *rawlen);
    !
    ! #ifdef DO_INLINES
    ! #ifndef Utils_c
    ! #define INLINING_Utils_c
    ! #include "utils.c"
    ! #endif
    ! #endif

      #endif