sqlite源码中表示WSD的方式

/*
When SQLITE_OMIT_WSD is defined, it means that the target platform does
not support Writable Static Data (WSD) such as global and static variables.
All variables must either be on the stack or dynamically allocated from
the heap.  When WSD is unsupported, the variable declarations scattered
throughout the SQLite code must become constants instead.  The SQLITE_WSD
macro is used for this purpose.  And instead of referencing the variable
directly, we use its constant as a key to lookup the run-time allocated
buffer that holds real variable.  The constant is also the initializer
for the run-time allocated buffer.

当SQLITE_OMIT_WSD被定义了,这意味着目标平台不支持Writable Static Data(WSD)(可写的静态数据如:静态变量和全局变量)。也就是说,所有的变量要么通过栈来分配,要么通过堆来动态分配分配。当不支持WSD时,分散在 SQLite 代码中的变量声明必须改为常量。SQLITE_WSD宏正是用于这个目的。我们使用常量来作为key来寻找运行时分配的buffer(其中包含真正的变量),而不是通过直接引用变量来使用它们。常量也是运行时分配缓冲区的初始化程序。

In the usual case where WSD is supported, the SQLITE_WSD and GLOBAL
macros become no-ops and have zero performance impact.

在大多数案例中,如果WSD被支持,那么SQLITE_WSD宏和GLOBAL宏将会变成空操作并且不会对别人产生影响。
*/
#ifdef SQLITE_OMIT_WSD
  #define SQLITE_WSD const
  #define GLOBAL(t,v) (*(t*)sqlite3_wsd_find((void*)&(v), sizeof(v)))

  #define sqlite3GlobalConfig GLOBAL(struct Sqlite3Config, sqlite3Config)
  int sqlite3_wsd_init(int N, int J);
  void *sqlite3_wsd_find(void *K, int L);
#else
  #define SQLITE_WSD
  #define GLOBAL(t,v) v
  #define sqlite3GlobalConfig sqlite3Config
#endif

例如:接下来的就是一个常量sqlite3Config,常量使用const作为变量的修饰符

/*
The following singleton contains the global configuration for
the SQLite library.
*/
SQLITE_WSD struct Sqlite3Config sqlite3Config = {//SQLITE_WSD和const是一致的
   SQLITE_DEFAULT_MEMSTATUS,  /* bMemstat */
   1,                         /* bCoreMutex */
   SQLITE_THREADSAFE==1,      /* bFullMutex */
   SQLITE_USE_URI,            /* bOpenUri */
   SQLITE_ALLOW_COVERING_INDEX_SCAN,   /* bUseCis */
   0,                         /* bSmallMalloc */
   0x7ffffffe,                /* mxStrlen */
   0,                         /* neverCorrupt */
   SQLITE_DEFAULT_LOOKASIDE,  /* szLookaside, nLookaside */
   SQLITE_STMTJRNL_SPILL,     /* nStmtSpill */
   {0,0,0,0,0,0,0,0},         /* m */
   {0,0,0,0,0,0,0,0,0},       /* mutex */
   {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
   (void*)0,                  /* pHeap */
   0,                         /* nHeap */
   0, 0,                      /* mnHeap, mxHeap */
   SQLITE_DEFAULT_MMAP_SIZE,  /* szMmap */
   SQLITE_MAX_MMAP_SIZE,      /* mxMmap */
   (void*)0,                  /* pPage */
   0,                         /* szPage */
   SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */
   0,                         /* mxParserStack */
   0,                         /* sharedCacheEnabled */
   SQLITE_SORTER_PMASZ,       /* szPma */
   /* All the rest should always be initialized to zero */
   0,                         /* isInit */
   0,                         /* inProgress */
   0,                         /* isMutexInit */
   0,                         /* isMallocInit */
   0,                         /* isPCacheInit */
   0,                         /* nRefInitMutex */
   0,                         /* pInitMutex */
   0,                         /* xLog */
   0,                         /* pLogArg */
#ifdef SQLITE_ENABLE_SQLLOG
   0,                         /* xSqllog */
   0,                         /* pSqllogArg */
#endif
#ifdef SQLITE_VDBE_COVERAGE
   0,                         /* xVdbeBranch */
   0,                         /* pVbeBranchArg */
#endif
#ifdef SQLITE_ENABLE_DESERIALIZE
   SQLITE_MEMDB_DEFAULT_MAXSIZE,   /* mxMemdbSize */
#endif
#ifndef SQLITE_UNTESTABLE
   0,                         /* xTestCallback */
#endif
   0,                         /* bLocaltimeFault */
   0,                         /* bInternalFunctions */
   0x7ffffffe,                /* iOnceResetThreshold */
   SQLITE_DEFAULT_SORTERREF_SIZE,   /* szSorterRef */
};

sqlite3_wsd_init

N是给常量预留的存储空间的大小,J指向对应常量的结构体的个数,eg:N=4096,J=24,表示24个ProcessLocalVar可以指向24个常量,而这24个常量占用的内存大小总和最大是4096字节,从下面的代码可以看出其中分配内存的细节


int sqlite3_wsd_init(int N, int J){
  if( !pGlobal ){
    int nMalloc = N + sizeof(ProcessLocalStorage) + J*sizeof(ProcessLocalVar);
    pGlobal = (ProcessLocalStorage *)malloc(nMalloc);
    if( pGlobal ){
      memset(pGlobal, 0, sizeof(ProcessLocalStorage));
      pGlobal->nFree = nMalloc - sizeof(ProcessLocalStorage);//剩余的空间的大小
      pGlobal->pFree = (u8 *)&pGlobal[1];//在为pGlobal分配的内存的后面紧接着就是为Free分配的内存
    }
  }

  return pGlobal ? SQLITE_OK : SQLITE_NOMEM;
}

sqlite3_wsd_find

请注意,我们首先要搞清楚sqlite3_wsd_find函数的目的是什么。实际上,从sqlite3_wsd_find函数最后一行的返回值可以看出,其目的是得到一个常量的起始地址。

可以把参数K理解为任意一个常量(如:sqlite3Config)的起始地址(而地址常常使用指针来表示,这里为K),把参数L理解为任意一个常量(如:sqlite3Config)所占用的内存的大小。sizeof(void *)的结果为8,即:把指针(地址)K所指向的前8个字节的内容经过hash函数处理之后得到一个哈希值,iHash,因为,所以再经过一次取模运算得到最终的iHash值,然后通过iHash可以在哈希表中找到一个对应的存储桶(如果存储通内部不止一个元素,那么就说明以前已经发生了哈希冲突),接下来通过for循环遍历这个哈希桶(这是在遍历iHash值相等的哈希表中的所有元素,因为不同的key经过哈希函数处理之后可以得到相同的iHash值)。请注意,这个时候,查找操作会得到两种结果。第一种情况:元素找到了(pVar->pKey == K),说明找到了这个元素,这时可以直接返回要找的常量的地址(至于为什么要返回它,接下来会阐明)。第二种情况:元素没有找到(即:for循环中的每次循环都是),这个时候,就需要为这个常量分配内存了(请看代码注释)。当多次查找同一个元素时(如多次查找常量sqlite3Config时),第一次的查找总是失败的(对应第二种情况),由于哈希表中不存在这个元素,所以需要将其在哈希表中分配内存并放入到哈希表中。后续对该常量的查找是成功的(因为之前已经存入到了哈希表中),这对应第一种情况,由于哈希冲突是不可避免的,所以需要for循环对同属于同一个哈希冲突的存储桶中查找key==K的元素。

由于该函数返回的是(void *)类型的变量,所以需要在外部进行强制类型转换为正确的类型。,可以看出,                                     *(t*)sqlite3_wsd_find......正是在做这种强制类型转换。

void *sqlite3_wsd_find(void *K, int L){
  int i;
  int iHash = 0;
  ProcessLocalVar *pVar;

  /* Calculate a hash of K */
  for(i=0; i<sizeof(void*); i++){
    iHash = (iHash<<3) + ((unsigned char *)&K)[i];
  }
  iHash = iHash%PLS_HASHSIZE;

  /* Search the hash table for K. */
  for(pVar=pGlobal->aData[iHash]; pVar && pVar->pKey!=K; pVar=pVar->pNext);

  /* If no entry for K was found, create and populate a new one. */
  if( !pVar ){
    /*分配内存时8字节对齐,nByte=(ProcessLocalVar的大小)+(L),因为一个ProcessLocalVar指向一个常量,
而一个常量占用的内内存大小是L,所以这两方面占用的内存要加起来*/
    int nByte = ROUND8(sizeof(ProcessLocalVar) + L);
    assert( pGlobal->nFree>=nByte );//剩余的空间要大于即将分配的空间
    pVar = (ProcessLocalVar *)pGlobal->pFree;//指针pVar指向的内容紧接着pGlobal占用的内存
    pVar->pKey = K;//给这个元素设定键值
    pVar->pNext = pGlobal->aData[iHash];//插入到哈希表当中(头插法)
    pGlobal->aData[iHash] = pVar;
    pGlobal->nFree -= nByte;//更新剩余空间
    pGlobal->pFree += nByte;//更新指向剩余空间内容的指针
    memcpy(&pVar[1], K, L);//把常量所占用的内存空间复制到pGlobal的管辖范围内
  }

  return (void *)&pVar[1];//返回这个常量的地址
}

其中,memcpy函数的定义如下:可以看出,这是一个宏定义,\表示换行

# define memcpy(D,S,N) {char*xxd=(char*)(D);const char*xxs=(const char*)(S);\
                        int xxn=(N);while(xxn-->0)*(xxd++)=*(xxs++);}

struct ProcessLocalStorage {
  ProcessLocalVar *aData[PLS_HASHSIZE];//可以理解为一个hash表
  int nFree;
  u8 *pFree;
};

struct ProcessLocalVar {//可以理解为haxi表中的一个元素
  void *pKey;
  ProcessLocalVar *pNext;
};


使用示例:

/*
Global data used by this cache.
*/

一、首先定义一个常量
static SQLITE_WSD struct PCacheGlobal {
  PGroup grp;                    /* The global PGroup for mode (2) */

  /* Variables related to SQLITE_CONFIG_PAGECACHE settings.  The
szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all
fixed at sqlite3_initialize() time and do not require mutex protection.
The nFreeSlot and pFree values do require mutex protection.
  */
  int isInit;                    /* True if initialized */
  int separateCache;             /* Use a new PGroup for each PCache */
  int nInitPage;                 /* Initial bulk allocation size */   
  int szSlot;                    /* Size of each free slot */
  int nSlot;                     /* The number of pcache slots */
  int nReserve;                  /* Try to keep nFreeSlot above this */
  void *pStart, *pEnd;           /* Bounds of global page cache memory */
  /* Above requires no mutex.  Use mutex below for variable that follow. */
  sqlite3_mutex *mutex;          /* Mutex for accessing the following: */
  PgFreeslot *pFree;             /* Free page blocks */
  int nFreeSlot;                 /* Number of unused pcache slots */
  /* The following value requires a mutex to change.  We skip the mutex on
reading because (1) most platforms read a 32-bit integer atomically and
(2) even if an incorrect value is read, no great harm is done since this
is really just an optimization. */
  int bUnderPressure;            /* True if low on PAGECACHE memory */
} pcache1_g;

/*
All code in this file should access the global structure above via the
alias "pcache1". This ensures that the WSD emulation is used when
compiling for systems that do not support real WSD.
*/

二、把刚刚定义的常量复制到哈希表中
#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值