view include/SDL_atomic.h @ 3191:91b335df6fc8

Fixed bug #750 Since many different event structures include windowID it should be placed near the beginning of the structure (preferably right after type) so it's position is the same between different events. This is to avoid code like this: if (event.type == SDL_WINDOWEVENT) win = event.window.windowID; else if ((SDL_EVENTMASK(event.type) & SDL_KEYEVENTMASK) != 0) win = event.key.windowID; else if (event.type == SDL_TEXTINPUT) win = event.text.windowID; else if (event.type == SDL_MOUSEMOTION) win = event.motion.windowID; else if ((SDL_EVENTMASK(event.type) & (SDL_MOUBUTTONDOWNMASK | SDL_MOUBUTTONUPMASK)) != 0) win = event.button.windowID; else if (event.type == SDL_MOUSEWHEEL) win = event.wheel.windowID; ... in favor of: win = event.window.windowID;
author Sam Lantinga <slouken@libsdl.org>
date Wed, 10 Jun 2009 14:00:21 +0000
parents e041d2c603fe
children 3e1bf2b8bd81
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
 */

/**
 * \file SDL_atomic.h
 *
 * Atomic int and pointer magic
 */

#ifndef _SDL_atomic_h_
#define _SDL_atomic_h_


#include "SDL_stdinc.h"
#include "SDL_platform.h"

#include "begin_code.h"

/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
/* *INDENT-OFF* */
extern "C" {
/* *INDENT-ON* */
#endif

/* indent is really bad at handling assembly */
/* *INDENT-OFF* */

#if defined(__GNUC__) && (defined(i386) || defined(__i386__)  || defined(__x86_64__))
static __inline__ void
SDL_atomic_int_add(volatile int* atomic, int value)
{
  __asm__ __volatile__("lock;"
                       "addl %1, %0"
                       : "=m" (*atomic)
                       : "ir" (value),
                         "m" (*atomic));
}

static __inline__ int
SDL_atomic_int_xchg_add(volatile int* atomic, int value)
{                                              
  int rv;                                    
  __asm__ __volatile__("lock;"               
                       "xaddl %0, %1"        
                       : "=r" (rv),          
                         "=m" (*atomic)    
                       : "0" (value),        
                         "m" (*atomic));   
  return rv;                                        
}

static __inline__ SDL_bool
SDL_atomic_int_cmp_xchg(volatile int* atomic, int oldvalue, int newvalue)
{
  int rv;                                                      
  __asm__ __volatile__("lock;"                               
                       "cmpxchgl %2, %1"                     
                       : "=a" (rv),                          
                         "=m" (*atomic)             
                       : "r" (newvalue),                     
                         "m" (*atomic),                    
                         "0" (oldvalue));
  return (SDL_bool)(rv == oldvalue);                                          
}

static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  void* rv;
  __asm__ __volatile__("lock;"
# if defined(__x86_64__)                       
                       "cmpxchgq %q2, %1"
# else
                       "cmpxchgl %2, %1"
# endif                       
                       : "=a" (rv),
                         "=m" (*atomic)
                       : "r" (newvalue),
                         "m" (*atomic),
                         "0" (oldvalue));
  return (SDL_bool)(rv == oldvalue);
}
#elif defined(__GNUC__) && defined(__alpha__)
# define ATOMIC_MEMORY_BARRIER (__asm__ __volatile__ ("mb" : : : "memory"))
# define ATOMIC_INT_CMP_XCHG(atomic,value)              \
  ({                                                    \
    int rv,prev;                                        \
    __asm__ __volatile__("   mb\n"                      \
                         "1: ldl_l   %0,%2\n"           \
                         "   cmpeq   %0,%3,%1\n"        \
                         "   beq     %1,2f\n"           \
                         "   mov     %4,%1\n"           \
                         "   stl_c   %1,%2\n"           \
                         "   beq     %1,1b\n"           \
                         "   mb\n"                      \
                         "2:"                           \
                         : "=&r" (prev),                \
                           "=&r" (rv)                   \
                         : "m" (*(atomic)),             \
                           "Ir" (oldvalue),             \
                           "Ir" (newvalue)              \
                         : "memory");                   \
    (rv != 0);                                          \
  })

# if (SIZEOF_VOIDP == 4)
static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  int rv;
  void* prev;
  __asm__ __volatile__("   mb\n"
                       "1: ldl_l %0,%2\n"
                       "   cmpeq %0,%3,%1\n"
                       "   beq   $1,2f\n"
                       "   mov   %4,%1\n"
                       "   stl_c %1,%2\n"
                       "   beq   %1,1b\n"
                       "   mb\n"
                       "2:"
                       : "=&r" (prev),
                         "=&r" (rv)
                       : "m" (*atomic),
                         "Ir" (oldvalue),
                         "Ir" (newvalue)
                       : "memory");
  return (SDL_bool)(rv != 0);
}
# elif (SIZEOF_VOIDP == 8)
static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  int rv;
  void* prev;
  __asm__ __volatile__("   mb\n"
                       "1: ldq_l %0,%2\n"
                       "   cmpeq %0,%3,%1\n"
                       "   beq   %1,2f\n"
                       "   mov   %4,%1\n"
                       "   stq_c %1,%2\n"
                       "   beq   %1,1b\n"
                       "   mb\n"
                       "2:"
                       : "=&r" (prev),
                         "=&r" (rv)
                       : "m" (*atomic),
                         "Ir" (oldvalue),
                         "Ir" (newvalue)
                       : "memory");
  return (SDL_bool)(rv != 0);
}
# else
#  error "Your system has an unsupported pointer size"  
# endif  /* SIZEOF_VOIDP */
#elif defined(__GNUC__) && defined(__sparc__)
# define ATOMIC_MEMORY_BARRIER                                          \
  (__asm__ __volatile__("membar #LoadLoad | #LoadStore"                 \
                        " | #StoreLoad | #StoreStore" : : : "memory"))
# define ATOMIC_INT_CMP_XCHG(atomic,oldvalue,newvalue)                  \
  ({                                                                    \
    int rv;                                                             \
    __asm__ __volatile__("cas [%4], %2, %0"                             \
                         : "=r" (rv), "=m" (*(atomic))                  \
                         : "r" (oldvalue), "m" (*(atomic)),             \
                         "r" (atomic), "0" (newvalue));                 \
    rv == oldvalue;                                                     \
  })

# if (SIZEOF_VOIDP == 4)
static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  void* rv;
  __asm__ __volatile__("cas [%4], %2, %0"
                       : "=r" (rv),
                         "=m" (*atomic)
                       : "r" (oldvalue),
                         "m" (*atomic),
                         "r" (atomic),
                         "0" (newvalue));
  return (SDL_bool)(rv == oldvalue);
}
# elif (SIZEOF_VOIDP == 8)
static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  void* rv;
  void** a = atomic;
  __asm__ __volatile__("casx [%4], %2, %0"
                       : "=r" (rv),
                         "=m" (*a)
                       : "r" (oldvalue),
                         "m" (*a),
                         "r" (a),
                         "0" (newvalue));
  return (SDL_bool)(rv == oldvalue);
}
# else
#  error "Your system has an unsupported pointer size"
# endif /* SIZEOF_VOIDP */
#elif defined(__GNUC__) && (defined(__POWERPC__) || defined(__powerpc__) || defined(__ppc__) || defined(_M_PPC))
# define ATOMIC_MEMORY_BARRIER \
  (__asm__ __volatile__ ("sync" : : : "memory"))
static __inline__ void
SDL_atomic_int_add(volatile int* atomic, int value)
{                                           
  int rv,tmp;                                   
  __asm__ __volatile__("1: lwarx   %0,  0, %3\n" 
                       "   add     %1, %0, %4\n"
                       "   stwcx.  %1,  0, %3\n" 
                       "   bne-    1b"          
                       : "=&b" (rv),            
                         "=&r" (tmp),           
                         "=m" (*atomic)       
                       : "b" (atomic),          
                         "r" (value),           
                         "m" (*atomic)        
                       : "cr0",                 
                         "memory");             
}

static __inline__ int
SDL_atomic_int_xchg_add(volatile int* atomic, int value)
{                                          
  int rv,tmp;                               
  __asm__ __volatile__("1: lwarx  %0, 0, %3\n"        
                       "   add    %1, %0, %4\n"       
                       "   stwcx. %1, 0, %3\n"        
                       "   bne-   1b"                 
                       : "=&b" (rv),                  
                         "=&r" (tmp),                 
                         "=m" (*atomic)
                       : "b" (atomic),                
                         "r" (value),                 
                         "m" (*atomic)
                       : "cr0",                       
                       "memory");                   
  return rv;                                                 
}

# if (SIZEOF_VOIDP == 4)
static __inline__ SDL_bool
SDL_atomic_int_cmp_xchg(volatile int* atomic, int oldvalue, int newvalue)
{                                                        
  int rv;                                                 
  __asm__ __volatile__("   sync\n"                         
                       "1: lwarx   %0, 0, %1\n"           
                       "   subf.   %0, %2, %0\n"          
                       "   bne     2f\n"                  
                       "   stwcx.  %3, 0, %1\n"           
                       "   bne-    1b\n"                  
                       "2: isync"                         
                       : "=&r" (rv)                       
                       : "b" (atomic),                    
                         "r" (oldvalue),                  
                         "r"                              
                       : "cr0",                           
                         "memory");                         
  return (SDL_bool)(rv == 0);                                              
}

static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  void* rv;
  __asm__ __volatile__("sync\n"
                       "1: lwarx  %0,  0, %1\n"
                       "   subf.  %0, %2, %0\n"
                       "   bne    2f\n"
                       "   stwcx. %3,  0, %1\n"
                       "   bne-   1b\n"
                       "2: isync"
                       : "=&r" (rv)
                       : "b" (atomic),
                         "r" (oldvalue),
                         "r" (newvalue)
                       : "cr0",
                       "memory");
  return (SDL_bool)(rv == 0);
}
# elif (SIZEOF_VOIDP == 8)
static __inline__ SDL_bool
SDL_atomic_int_cmp_xchg(volatile int* atomic, int oldvalue, int newvalue)
{                                                        
  int rv;                                                 
  __asm__ __volatile__("   sync\n"                         
                       "1: lwarx   %0,  0, %1\n"
                       "   extsw   %0, %0\n"
                       "   subf.   %0, %2, %0\n"          
                       "   bne     2f\n"                  
                       "   stwcx.  %3,  0, %1\n"           
                       "   bne-    1b\n"                  
                       "2: isync"                         
                       : "=&r" (rv)                       
                       : "b" (atomic),                    
                         "r" (oldvalue),                  
                         "r"                              
                       : "cr0",                           
                         "memory");                         
  return (SDL_bool)(rv == 0);                                              
}

static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  void* rv;
  __asm__ __volatile__("sync\n"
                       "1: ldarx  %0,  0, %1\n"
                       "   subf.  %0, %2, %0\n"
                       "   bne    2f\n"
                       "   stdcx. %3,  0, %1\n"
                       "   bne-   1b\n"
                       "2: isync"
                       : "=&r" (rv)
                       : "b" (atomic),
                         "r" (oldvalue),
                         "r" (newvalue)
                       : "cr0",
                       "memory");
  return (SDL_bool)(rv == 0);
}
# else
#  error "Your system has an unsupported pointer size"
# endif /* SIZEOF_VOIDP */
#elif defined(__GNUC__) && (defined(__IA64__) || defined(__ia64__))
# define ATOMIC_MEMORY_BARRIER (__sync_synchronize())
# define SDL_atomic_int_xchg_add(atomic, value)     \
  (__sync_fetch_and_add((atomic),(value)))
# define SDL_atomic_int_add(atomic, value)                  \
  ((void)__sync_fetch_and_add((atomic),(value)))
# define SDL_atomic_int_cmp_xchg(atomic,oldvalue,newvalue)  \
  (__sync_bool_compare_and_swap((atomic),(oldvalue),(newvalue)))
# define SDL_atomic_ptr_cmp_xchg(atomic,oldvalue,newvalue)              \
  (__sync_bool_compare_and_swap((long*)(atomic),(long)(oldvalue),(long)(newvalue)))
#elif defined(__GNUC__) && defined(__LINUX__) && (defined(__mips__) || defined(__MIPS__))
static __inline__ int
SDL_atomic_int_xchg_add(volatile int* atomic, int value)
{                                            
  int rv,tmp;                                 
  __asm__ __volatile__("1:              \n"                 
                       ".set  push      \n"         
                       ".set  mips2     \n"        
                       "ll    %0,%3     \n"        
                       "addu  %1,%4,%0  \n"     
                       "sc    %1,%2     \n"        
                       ".set  pop       \n"          
                       "beqz  %1,1b     \n"        
                       : "=&r" (rv),          
                         "=&r" (tmp),         
                         "=m" (*atomic)     
                       : "m" (*atomic),     
                         "r" (value)          
                       : "memory");           
  return rv;                                         
}

static __inline__ void
SDL_atomic_int_add(volatile int* atomic, int value)
{                                           
  int rv;                                    
  __asm__ __volatile__("1:               \n"                
                       ".set  push       \n"        
                       ".set  mips2      \n"       
                       "ll    %0,%2      \n"       
                       "addu  %0,%3,%0   \n"    
                       "sc    %0,%1      \n"       
                       ".set  pop        \n"         
                       "beqz  %0,1b      \n"       
                       : "=&r" (rv),         
                         "=m" (*atomic)    
                       : "m" (*atomic),    
                         "r" (value)         
                       : "memory");          
}

static __inline__ SDL_bool
SDL_atomic_int_cmp_xchg(volatile int* atomic, int oldvalue, int newvalue)
{
  int rv;
  __asm__ __volatile__("     .set push        \n"
                       "     .set noat        \n"
                       "     .set mips3       \n"
                       "1:   ll   %0, %2      \n"
                       "     bne  %0, %z3, 2f \n"
                       "     .set mips0       \n"
                       "     move $1, %z4     \n"
                       "     .set mips3       \n"
                       "     sc   $1, %1      \n"
                       "     beqz $1, 1b      \n"
                       "     sync             \n"
                       "2:                    \n"
                       "     .set pop         \n"
                       : "=&r" (rv),
                         "=R" (*atomic)
                       : "R" (*atomic),
                         "Jr" (oldvalue),
                         "Jr" (newvalue)
                       : "memory");
  return (SDL_bool)rv;                  
}

static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{                                                     
  int rv;
  __asm__ __volatile__("     .set push        \n"
                       "     .set noat        \n"
                       "     .set mips3       \n"
# if defined(__mips64)
                       "1:   lld  %0, %2      \n"
# else
                       "1:   ll   %0, %2      \n"
# endif                       
                       "     bne  %0, %z3, 2f \n"
                       "     move $1, %z4     \n"
# if defined(__mips64)
                       "     sc   $1, %1      \n"
# else
                       "     scd  $1, %1      \n"
# endif                       
                       "     beqz $1, 1b      \n"
                       "     sync             \n"
                       "2:                    \n"
                       "     .set pop         \n"
                       : "=&r" (rv),
                         "=R" (*atomic)
                       : "R" (*atomic),
                         "Jr" (oldvalue),
                         "Jr" (newvalue)
                       : "memory");
  return (SDL_bool)rv;                                                  
}
#elif defined(__GNUC__) && defined(__m68k__)
static __inline__ int
SDL_atomic_int_xchg_add(volatile int* atomic, int value)
{                                          
  int rv = *atomic;
  int tmp;
  __asm__ __volatile__("1: move%.l %0,%1    \n"
                       "   add%.l  %2,%1    \n"
                       "   cas%.l  %0,%1,%3 \n"
                       "   jbne    1b       \n"
                       : "=d" (rv),
                         "=&d" (tmp)
                       : "d" (value),
                         "m" (*atomic),
                         "0" (rv)
                       : "memory");
  return (SDL_bool)rv;
}

static __inline__ void
SDL_atomic_int_add(volatile int* atomic, int value)
{                                           
  __asm__ __volatile__("add%.l %0,%1"        
                       :                     
                       : "id" (value),       
                         "m" (*atomic)
                       : "memory");          
}

static __inline__ SDL_bool
SDL_atomic_int_cmp_xchg(volatile int* atomic, int oldvalue, int newvalue)
{                                           
  char rv;                                   
  int readvalue;                             
  __asm__ __volatile__("cas%.l %2,%3,%1\n"   
                       "seq    %0"           
                       : "=dm" (rv),         
                         "=m" (*atomic),   
                         "=d" (readvalue)    
                       : "d" (newvalue),     
                         "m" (*atomic),    
                         "2" (oldvalue));    
    return (SDL_bool)rv;                                        
}

static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  char rv;                                   
  int readvalue;                             
  __asm__ __volatile__("cas%.l %2,%3,%1\n"   
                       "seq    %0"           
                       : "=dm" (rv),         
                         "=m" (*atomic),   
                         "=d" (readvalue)    
                       : "d" (newvalue),     
                         "m" (*atomic),    
                         "2" (oldvalue));    
    return (SDL_bool)rv;                                        
}
#elif defined(__GNUC__) && defined(__s390__)
# define ATOMIC_INT_CMP_XCHG(atomic,oldvalue,newvalue)  \
  ({                                                    \
    int rv = oldvalue;                                  \
    __asm__ __volatile__("cs %0, %2, %1"                \
                         : "+d" (rv),                   \
                           "=Q" (*(atomic))             \
                         : "d" (newvalue),              \
                           "m" (*(atomic))              \
                         : "cc");                       \
    rv == oldvalue;                                     \
  })
# if (SIZEOF_VOIDP == 4)
static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  void* rv = oldvalue;
  __asm__ __volatile__("cs %0, %2, %1"
                       : "+d" (rv),
                         "=Q" (*atomic)
                       : "d" (newvalue),
                         "m" (*atomic)
                       : "cc");
  return (SDL_bool)(rv == oldvalue);
}
# elif (SIZEOF_VOIDP == 8)
static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  void* rv = oldvalue;
  void** a = atomic;
  __asm__ __volatile__("csg %0, %2, %1"
                       : "+d" (rv),
                         "=Q" (*a)
                       : "d" ((long)(newvalue)),
                         "m" (*a)
                       : "cc");
  return (SDL_bool)(rv == oldvalue);
}
# else
#  error "Your system has an unsupported pointer size"
# endif /* SIZEOF_VOIDP */
#elif defined(__WIN32__)
# include <windows.h>
static __inline__ int
SDL_atomic_int_xchg_add(volatile int* atomic, int value)
{
  return InterlockedExchangeAdd(atomic, value);
}

static __inline__ void
SDL_atomic_int_add(volatile int* atomic, int value)
{
  InterlockedExchangeAdd(atomic, value);
}

# if (WINVER > 0X0400)
static __inline__ SDL_bool
SDL_atmoic_int_cmp_xchg(volatile int* atomic, int oldvalue, int newvalue)
{
   return (SDL_bool)(InterlockedCompareExchangePointer((PVOID*)atomic,
                                                       (PVOID)newvalue,
                                                       (PVOID)oldvalue) == oldvalue);
}


static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  return (InterlockedCompareExchangePointer(atomic, newvalue, oldvalue) == oldvalue);
}
# else /* WINVER <= 0x0400 */
#  if (SIZEOF_VOIDP != 4)
#   error "InterlockedCompareExchangePointer needed"
#  endif

static __inline__ SDL_bool
SDL_atomic_int_cmp_xchg(volatile int* atomic, int oldvalue, int newvalue)
{
  return (InterlockedCompareExchange(atomic, newvalue, oldvalue) == oldvalue);
}

static __inline__ SDL_bool
SDL_atomic_ptr_cmp_xchg(volatile void** atomic, void* oldvalue, void* newvalue)
{
  return (InterlockedCompareExchange(atomic, newvalue, oldvalue) == oldvalue);
}
# endif
#else /* when all else fails */
# define SDL_ATOMIC_OPS_NOT_SUPPORTED
# warning "Atomic Ops for this platform not supported!"
static __inline__ int
SDL_atomic_int_xchg_add(volatile int* atomic, int value)
{                                           
  int rv = *atomic;                          
  *(atomic) += value;                        
  return rv;                                        
}

static __inline__ SDL_bool
SDL_atomic_int_cmp_xchg(volatile int* atomic, int oldvalue, int newvalue)
{
  return (*atomic == oldvalue) ?  
    ((*atomic = newvalue), SDL_TRUE) : SDL_FALSE;
}

static __inline__ void
SDL_atomic_int_add(volatile int* atomic, int value)
{
  *atomic += value;
}
#endif /* arch & platforms */
  
/* *INDENT-ON* */

#ifdef ATOMIC_INT_CMP_XCHG
static __inline__ SDL_bool
SDL_atomic_int_cmp_xchg(volatile int *atomic, int oldvalue, int newvalue)
{
    return (SDL_bool) ATOMIC_INT_CMP_XCHG(atomic, oldvalue, newvalue);
}

static __inline__ int
SDL_atomic_int_xchg_add(volatile int *atomic, int value)
{
    int rv;
    do
        rv = *atomic;
    while (!ATOMIC_INT_CMP_XCHG(atomic, rv, rv + value));
    return rv;
}

static __inline__ void
SDL_atomic_int_add(volatile int *atomic, int value)
{
    int rv;
    do
        rv = *atomic;
    while (!ATOMIC_INT_CMP_XCHG(atomic, rv, rv + value));
}
#endif /* ATOMIC_CMP_XCHG */

#ifdef ATOMIC_MEMORY_BARRIER
# define SDL_atomic_int_get(atomic) \
  (ATOMIC_MEMORY_BARRIER,*(atomic))
# define SDL_atomic_int_set(atomic,value) \
  (*(atomic)=value,ATOMIC_MEMORY_BARRIER)
#else
# define SDL_atomic_int_get(atomic) (*(atomic))
# define SDL_atomic_int_set(atomic, newvalue) ((void)(*(atomic) = (newvalue)))
#endif /* MEMORY_BARRIER_NEEDED */

#define SDL_atomic_int_inc(atomic) (SDL_atomic_int_add((atomic),1))
#define SDL_atomic_int_dec_test(atomic) (SDL_atomic_int_xchg_add((atomic),-1) == 1)

/* Ends C function definitions when using C++ */
#ifdef __cplusplus
/* *INDENT-OFF* */
}
/* *INDENT-ON* */
#endif

#include "close_code.h"

#endif /* _SDL_atomic_h_ */

/* vi: set ts=4 sw=4 expandtab: */