Massive reorganization in progress.
[sod] / lib / sod.c
diff --git a/lib/sod.c b/lib/sod.c
new file mode 100644 (file)
index 0000000..bd600f9
--- /dev/null
+++ b/lib/sod.c
@@ -0,0 +1,123 @@
+/* -*-c-*-
+ *
+ * Runtime support for the Sensible Object Design
+ *
+ * (c) 2009 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Sensble Object Design, an object system for C.
+ *
+ * SOD is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * SOD is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SOD; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "sod.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @find_chain@ --- *
+ *
+ * Arguments:  @const SodClass *sub, *super@ = pointers to two classes
+ *
+ * Returns:    If @sub@ is a subclass of @super@, then a pointer to @sub@'s
+ *             chain entry describing @super@'s chain; otherwise null.
+ */
+
+static const struct sod_chain *find_chain(const SodClass *sub,
+                                         const SodClass *super)
+{
+  const SodClass *head = super->cls.head;
+  const struct sod_chain *chain, *limit;
+
+  /* Slightly fancy footwork.  Each class carries a table describing its
+   * constituent chains, and each chain has a vector of the classes in that
+   * chain, with the head (least specific) first.  Chains are always
+   * non-empty.
+   *
+   * Another useful bit of information in the class is its level, i.e., its
+   * index in its own chain vector.  This is invariant because the chains are
+   * linear.
+   *
+   * This suggests the following algorithm.  Search @sub@'s chains for one
+   * headed by @super@'s chain head.  If we find one, check that the chain's
+   * class vector is long enough, and look at the entry corresponding to
+   * @super@'s level.  If it matches @super@ then @sub@ is indeed a subclass
+   * and we're done.  Otherwise it isn't, and we lose.  We also lose if no
+   * matching chain is found.
+   */
+  for (chain = sub->cls.chains, limit = chain + sub->cls.n_chains;
+       chain < limit; chain++) {
+    if (chain->classes[0] != head)
+      continue;
+    if (super->cls.level < chain->n_classes &&
+       chain->classes[super->cls.level] == super)
+      return (chain);
+    break;
+  }
+  return (0);
+}
+
+/* --- @sod_subclassp@ --- *
+ *
+ * Arguments:  @const SodClass *sub, *super@ = pointers to two classes
+ *
+ * Returns:    Nonzero if @sub@ is a subclass of @super@.
+ */
+
+int sod_subclassp(const SodClass *sub, const SodClass *super)
+  { return (!!find_chain(sub, super)); }
+
+/* --- @sod_convert@ --- *
+ *
+ * Arguments:  @const SodClass *cls@ = desired class object
+ *             @const void *obj@ = pointer to instance
+ *
+ * Returns:    Pointer to appropriate ichain of object, or null if the
+ *             instance isn't of the specified class.
+ *
+ * Use:                General down/cross-casting function.
+ *
+ *             Upcasts can be performed efficiently using the automatically
+ *             generated macros.  In particular, upcasts with a chain are
+ *             trivial; cross-chain upcasts require information from vtables
+ *             but are fairly fast.  This function is rather slower, but is
+ *             much more general.
+ *
+ *             Suppose we have an instance of a class C, referred to by a
+ *             pointer to an instance of one of C's superclasses S.  If S'
+ *             is some other superclass of C then this function will return
+ *             a pointer to C suitable for use as an instance of S'.  If S'
+ *             is not a superclass of C, then the function returns null.
+ *             (If the pointer doesn't point to an instance of some class
+ *             then the behaviour is undefined.)  Note that you don't need
+ *             to know what C or S actually are.
+ */
+
+void *sod_convert(const SodClass *cls, void *p)
+{
+  const struct sod_instance *inst = p;
+  const struct sod_vtable *vt = inst->_vt;
+  const SodClass *realcls = vt->_class;
+  const struct sod_chain *chain = find_chain(realcls, cls);
+
+  if (!chain)
+    return (0);
+  return ((char *)p - vt->_base + chain->off_ichain);
+}
+
+/*----- That's all, folks -------------------------------------------------*/