From e9117b46c881fb9f385b60217e1c0211d0954168 Mon Sep 17 00:00:00 2001
From: Per Hedbor <ph@opera.com>
Date: Tue, 25 Feb 2014 14:24:24 +0100
Subject: [PATCH] Unified INDEX_CHARP and generic_extract (and thus
 index_shared_string)

In non-debug mode indexing a shared string is now the same thing as
doing the old INDEX_CHARP, and thus actually noticeably faster.

However, INDEX_CHARP will be slower in debug mode since it will check
if the size_shift is >2. That seems rather harmless, though, since
debug mode is seldom used when performance matters.
---
 src/stralloc.c | 60 +++++++++++++++++++++++---------------------------
 src/stralloc.h | 39 +++++++++++++++++++++++++++-----
 2 files changed, 62 insertions(+), 37 deletions(-)

diff --git a/src/stralloc.c b/src/stralloc.c
index 006f3e3a26..f025fb8af0 100644
--- a/src/stralloc.c
+++ b/src/stralloc.c
@@ -256,38 +256,6 @@ static INLINE int min_magnitude(p_wchar2 c)
   return 2;
 }
 
-static INLINE p_wchar2 generic_extract (const void *str, int size, ptrdiff_t pos)
-{
-  switch(size)
-  {
-    case 0: return ((p_wchar0 *)str)[pos];
-    case 1: return ((p_wchar1 *)str)[pos];
-    case 2: return ((p_wchar2 *)str)[pos];
-  }
-#ifdef PIKE_DEBUG
-  Pike_fatal("Illegal shift size!\n");
-#endif
-  return 0;
-}
-
-PMOD_EXPORT p_wchar2 index_shared_string(struct pike_string *s,
-					 ptrdiff_t pos)
-{
-#ifdef PIKE_DEBUG
-  if(pos > s->len || pos<0) {
-    if (s->len) {
-      Pike_fatal("String index %"PRINTPTRDIFFT"d is out of "
-		 "range 0..%"PRINTPTRDIFFT"d.\n",
-		 pos, s->len-1);
-    } else {
-      Pike_fatal("Attempt to index the empty string with %"PRINTPTRDIFFT"d.\n",
-		 pos);
-    }
-  }
-#endif
-  return generic_extract(s->str,s->size_shift,pos);
-}
-
 void low_set_index(struct pike_string *s, ptrdiff_t pos, int value)
 {
 #ifdef PIKE_DEBUG
@@ -409,6 +377,34 @@ PMOD_EXPORT void pike_string_cpy(PCHARP to, struct pike_string *from)
 #define DM(X)
 #endif
 
+PMOD_EXPORT p_wchar2 index_shared_string(struct pike_string *s,
+                                         ptrdiff_t pos)
+{
+  if(pos > s->len || pos<0) {
+    if (s->len) {
+      Pike_fatal("String index %"PRINTPTRDIFFT"d is out of "
+		 "range 0..%"PRINTPTRDIFFT"d.\n",
+		 pos, s->len-1);
+    } else {
+      Pike_fatal("Attempt to index the empty string with %"PRINTPTRDIFFT"d.\n",
+		 pos);
+    }
+  }
+  return generic_extract(s->str,s->size_shift,pos);
+}
+
+PMOD_EXPORT p_wchar2 generic_extract (const void *str, int size, ptrdiff_t pos)
+{
+  switch(size)
+  {
+    case 0: return ((p_wchar0 *)str)[pos];
+    case 1: return ((p_wchar1 *)str)[pos];
+    case 2: return ((p_wchar2 *)str)[pos];
+  }
+  Pike_fatal("Unsupported string shift: %d\n", size);
+  return 0;
+}
+
 static void locate_problem(int (*isproblem)(struct pike_string *))
 {
   unsigned INT32 e;
diff --git a/src/stralloc.h b/src/stralloc.h
index 829c121096..e1d4f681dc 100644
--- a/src/stralloc.h
+++ b/src/stralloc.h
@@ -16,6 +16,12 @@
 #define STRUCT_PIKE_STRING_DECLARED
 #endif
 
+enum size_shift {
+    eightbit=0,
+    sixteenbit=1,
+    thirtytwobit=2,
+};
+
 #ifdef ATOMIC_SVALUE
 #define PIKE_STRING_CONTENTS						\
   INT32 refs;								\
@@ -31,7 +37,7 @@
 #define PIKE_STRING_CONTENTS                \
     INT32 refs;                                     \
     unsigned char  flags;                           \
-    unsigned char  size_shift;                      \
+    enum size_shift  size_shift:8;                        \
     unsigned char  min;								\
     unsigned char  max;                                             \
     ptrdiff_t len; /* Not counting terminating NUL. */              \
@@ -115,8 +121,33 @@ struct pike_string *debug_findstring(const struct pike_string *foo);
 #define STR2(X) ((p_wchar2 *)(X)->str)
 #endif
 
-#define INDEX_CHARP(PTR,IND,SHIFT) \
-  ((SHIFT)==0?((p_wchar0 *)(PTR))[(IND)]:(SHIFT)==1?((p_wchar1 *)(PTR))[(IND)]:((p_wchar2 *)(PTR))[(IND)])
+#ifndef PIKE_DEBUG
+static p_wchar2 generic_extract (const void *str, int size, ptrdiff_t pos) ATTRIBUTE((pure));
+
+static p_wchar2 generic_extract (const void *str, int size, ptrdiff_t pos)
+{
+/* this gives better code than a lot of other versions I have tested.
+
+When inlined the ret/eax is of course somewhat different, it can be
+less or more optimal, but this is at least actually smaller than the
+expanded code for INDEX_CHARP.
+*/
+  if( LIKELY(size == 0) ) return ((p_wchar0 *)str)[pos];
+  if( LIKELY(size == 1) ) return ((p_wchar1 *)str)[pos];
+  return ((p_wchar2 *)str)[pos];
+}
+
+static INLINE p_wchar2 index_shared_string(struct pike_string *s,  ptrdiff_t pos)
+{
+  return generic_extract(s->str,s->size_shift,pos);
+}
+#else
+PMOD_EXPORT p_wchar2 generic_extract (const void *str, int size, ptrdiff_t pos);
+PMOD_EXPORT p_wchar2 index_shared_string(struct pike_string *s,
+                                         ptrdiff_t pos)
+#endif
+
+#define INDEX_CHARP(PTR,IND,SHIFT) generic_extract(PTR,SHIFT,IND)
 
 #define SET_INDEX_CHARP(PTR,IND,SHIFT,VAL) \
   ((SHIFT)==0?								\
@@ -259,8 +290,6 @@ INT32 PIKE_CONCAT4(compare_,FROM,_to_,TO)(const PIKE_CONCAT(p_wchar,TO) *to, con
 PMOD_EXPORT extern struct pike_string *empty_pike_string;
 
 /* Prototypes begin here */
-PMOD_EXPORT p_wchar2 index_shared_string(struct pike_string *s,
-					 ptrdiff_t pos);
 void low_set_index(struct pike_string *s, ptrdiff_t pos, int value);
 PMOD_EXPORT struct pike_string *debug_check_size_shift(struct pike_string *a,int shift);
 CONVERT(0,1)
-- 
GitLab