From 4f257df8ed7f467178c5a23f00048d72246d1b8c Mon Sep 17 00:00:00 2001
From: Miller Puckette <msp@ucsd.edu>
Date: Thu, 13 Dec 2007 10:41:16 -0800
Subject: [PATCH] tighter message stack allocation

---
 src/m_binbuf.c | 88 ++++++++++++++++++++++++++++++++++++++------------
 src/notes.txt  |  3 +-
 2 files changed, 69 insertions(+), 22 deletions(-)

diff --git a/src/m_binbuf.c b/src/m_binbuf.c
index 69580741a..3d909a75b 100644
--- a/src/m_binbuf.c
+++ b/src/m_binbuf.c
@@ -387,9 +387,6 @@ void binbuf_restore(t_binbuf *x, int argc, t_atom *argv)
     x->b_n = newsize;
 }
 
-
-#define MSTACKSIZE 10000 /* FIXME -- make this grow as needed */
-
 void binbuf_print(t_binbuf *x)
 {
     int i, startedpost = 0, newline = 1;
@@ -531,13 +528,70 @@ done:
     return (gensym(buf2));
 }
 
+#define SMALLMSG 5
+#define HUGEMSG 1000
+#ifdef MSW
+#include <malloc.h>
+#else
+#include <alloca.h>
+#endif
+#if HAVE_ALLOCA
+#define ATOMS_ALLOCA(x, n) ((x) = (t_atom *)((n) < HUGEMSG ?  \
+        alloca((n) * sizeof(t_atom)) : getbytes((n) * sizeof(t_atom))))
+#define ATOMS_FREEA(x, n) ( \
+    ((n) < HUGEMSG || (freebytes((x), (n) * sizeof(t_atom)), 0)))
+#else
+#define ATOMS_ALLOCA(x, n) ((x) = (t_atom *)getbytes((n) * sizeof(t_atom)))
+#define ATOMS_FREEA(x, n) (freebytes((x), (n) * sizeof(t_atom)))
+#endif
+
 void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv)
 {
-    static t_atom mstack[MSTACKSIZE], *msp = mstack, *ems = mstack+MSTACKSIZE;
-    t_atom *stackwas = msp;
+    t_atom smallstack[SMALLMSG], *mstack, *msp;
     t_atom *at = x->b_vec;
     int ac = x->b_n;
-    int nargs;
+    int nargs, maxnargs = 0;
+    if (ac <= SMALLMSG)
+        mstack = smallstack;
+    else
+    {
+#if 1
+            /* count number of args in biggest message.  The wierd
+            treatment of "pd_objectmaker" is because when the message
+            goes out to objectmaker, commas and semis are passed
+            on as regular args (see below).  We're tacitly assuming here
+            that the pd_objectmaker target can't come up via a named
+            destination in the message, only because the original "target"
+            points there. */
+        if (target == &pd_objectmaker)
+            maxnargs = ac;
+        else
+        {
+            int i, j = (target ? 0 : -1);
+            for (i = 0; i < ac; i++)
+            {
+                if (at[i].a_type == A_SEMI)
+                    j = -1;
+                else if (at[i].a_type == A_COMMA)
+                    j = 0;
+                else if (++j > maxnargs)
+                    maxnargs = j;
+            }
+        }
+        if (maxnargs <= SMALLMSG)
+            mstack = smallstack;
+        else ATOMS_ALLOCA(mstack, maxnargs);
+#else
+            /* just pessimistically allocate enough to hold everything
+            at once.  This turned out to run slower in a simple benchmark
+            I tried, perhaps because the extra memory allocation
+            hurt the cache hit rate. */
+        maxnargs = ac;
+        ATOMS_ALLOCA(mstack, maxnargs);
+#endif
+
+    }
+    msp = mstack;
     while (1)
     {
         t_pd *nexttarget;
@@ -597,11 +651,6 @@ void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv)
         {
             t_symbol *s9;
             if (!ac) goto gotmess;
-            if (msp >= ems)
-            {
-                error("message stack overflow");
-                goto broken;
-            }
             switch (at->a_type)
             {
             case A_SEMI:
@@ -667,27 +716,26 @@ void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv)
     gotmess:
         if (nargs)
         {
-            switch (stackwas->a_type)
+            switch (mstack->a_type)
             {
             case A_SYMBOL:
-                typedmess(target, stackwas->a_w.w_symbol, nargs-1, stackwas+1);
+                typedmess(target, mstack->a_w.w_symbol, nargs-1, mstack+1);
                 break;
             case A_FLOAT:
-                if (nargs == 1) pd_float(target, stackwas->a_w.w_float);
-                else pd_list(target, 0, nargs, stackwas);
+                if (nargs == 1) pd_float(target, mstack->a_w.w_float);
+                else pd_list(target, 0, nargs, mstack);
                 break;
             }
         }
-        msp = stackwas;
+        msp = mstack;
         if (!ac) break;
         target = nexttarget;
         at++;
         ac--;
     }
-
-    return;
-broken:
-    msp = stackwas;
+broken: 
+    if (maxnargs > SMALLMSG)
+         ATOMS_FREEA(mstack, maxnargs);
 }
 
 static int binbuf_doopen(char *s, int mode)
diff --git a/src/notes.txt b/src/notes.txt
index 3da075642..472487c20 100644
--- a/src/notes.txt
+++ b/src/notes.txt
@@ -1,6 +1,7 @@
 ---------------- dolist --------------------
 fixed crash bug closing patches with open GOPs
 fixed PC device counting problem (first device invoked by -audiodev 0)
+fixed MSTACKSIZE limitation in m_binbuf.c
 
 test:
 compile on various versions of linux
@@ -19,7 +20,6 @@ problems:
 check real-time gaps in writesf~
 fix declare to update current patch when changed
 objects on GOP don't erase if you edit the GOP while they're showing
-MSTACKSIZE limitation in m_binbuf.c
 add -stack option to make 'regular' stack larger
 TK menu on Windows says "wish"
 help browser broke on Panurge
@@ -36,7 +36,6 @@ when retyping abstractions, offer to save dirty one
 should linux stop grabbing focus on vis?  Is there a way to detect whether
     the mouse is in a window when it opens?
 arrays that don't fit in bounds don't update (same as red rectangle problem?)
-look in d_resample.pd to understand inlet~ upsampling...
 patcher inlets don't deal with scalars (zbug.pd)
 check if there's a problem loading libs on startup if superuser
 read xx.txt in "bad" gives warnings
-- 
GitLab