Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Added ability to tie client data/finalizer to th1, allowing a refactoring of OB manager to use per-interpreter-instance state instead of global state. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | th1-query-api |
Files: | files | file ages | folders |
SHA1: |
147c602d935c272b1f754581427fab0b |
User & Date: | stephan 2012-07-15 10:59:44.779 |
Context
2012-07-15
| ||
11:04 | minor doc additions. ... (check-in: 236cf135 user: stephan tags: th1-query-api) | |
10:59 | Added ability to tie client data/finalizer to th1, allowing a refactoring of OB manager to use per-interpreter-instance state instead of global state. ... (check-in: 147c602d user: stephan tags: th1-query-api) | |
09:23 | Added push/pop as aliases for start/end in the ob API. Fixed a horrible size calculation bug which triggered an assert() for ob nesting levels deeper than 2. ... (check-in: 9b3a11e1 user: stephan tags: th1-query-api) | |
Changes
Changes to src/th.c.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 | return fossil_realloc( p, n ); } typedef struct Th_Command Th_Command; typedef struct Th_Frame Th_Frame; typedef struct Th_Variable Th_Variable; /* ** Interpreter structure. */ struct Th_Interp { | > > > > > > | | | | | > < | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | return fossil_realloc( p, n ); } typedef struct Th_Command Th_Command; typedef struct Th_Frame Th_Frame; typedef struct Th_Variable Th_Variable; struct Th_GcEntry { void * pData; void (*xDel)( Th_Interp *, void * ); }; typedef struct Th_GcEntry Th_GcEntry; /* ** Interpreter structure. */ struct Th_Interp { Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */ char *zResult; /* Current interpreter result (Th_Malloc()ed) */ int nResult; /* number of bytes in zResult */ Th_Hash *paCmd; /* Table of registered commands */ Th_Frame *pFrame; /* Current execution frame */ int isListMode; /* True if thSplitList() should operate in "list" mode */ Th_Hash * paGc; /* holds client-provided data owned by this object */ #ifdef TH_USE_SQLITE struct { sqlite3_stmt ** aStmt; int nStmt; } stmt; /* list of prepared statements */ #endif }; /* ** Each TH command registered using Th_CreateCommand() is represented ** by an instance of the following structure stored in the Th_Interp.paCmd ** hash-table. |
︙ | ︙ | |||
119 120 121 122 123 124 125 126 127 128 129 130 131 132 | static int thEndOfLine(const char *, int); static int thPushFrame(Th_Interp*, Th_Frame*); static void thPopFrame(Th_Interp*); static void thFreeVariable(Th_HashEntry*, void*); static void thFreeCommand(Th_HashEntry*, void*); /* ** The following are used by both the expression and language parsers. ** Given that the start of the input string (z, n) is a language ** construct of the relevant type (a command enclosed in [], an escape ** sequence etc.), these functions determine the number of bytes ** of the input consumed by the construct. For example: | > | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | static int thEndOfLine(const char *, int); static int thPushFrame(Th_Interp*, Th_Frame*); static void thPopFrame(Th_Interp*); static void thFreeVariable(Th_HashEntry*, void*); static void thFreeCommand(Th_HashEntry*, void*); static void thFreeGc(Th_HashEntry*, void*); /* ** The following are used by both the expression and language parsers. ** Given that the start of the input string (z, n) is a language ** construct of the relevant type (a command enclosed in [], an escape ** sequence etc.), these functions determine the number of bytes ** of the input consumed by the construct. For example: |
︙ | ︙ | |||
301 302 303 304 305 306 307 308 309 310 311 312 313 314 | Th_Command *pCommand = (Th_Command *)pEntry->pData; if( pCommand->xDel ){ pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); } Th_Free((Th_Interp *)pContext, pEntry->pData); pEntry->pData = 0; } /* ** Push a new frame onto the stack. */ static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){ pFrame->paVar = Th_HashNew(interp); pFrame->pCaller = interp->pFrame; | > > > > > > > > > > > > > > > > | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | Th_Command *pCommand = (Th_Command *)pEntry->pData; if( pCommand->xDel ){ pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); } Th_Free((Th_Interp *)pContext, pEntry->pData); pEntry->pData = 0; } /* ** Th_Hash visitor/destructor for Th_Interp::paGc entries. Frees ** pEntry->pData but not pEntry. */ static void thFreeGc(Th_HashEntry *pEntry, void *pContext){ Th_GcEntry *gc = (Th_GcEntry *)pEntry->pData; if(gc){ if( gc->xDel ){ gc->xDel( (Th_Interp*)pContext, gc->pData ); } Th_Free((Th_Interp *)pContext, pEntry->pData); pEntry->pData = 0; } } /* ** Push a new frame onto the stack. */ static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){ pFrame->paVar = Th_HashNew(interp); pFrame->pCaller = interp->pFrame; |
︙ | ︙ | |||
1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 | /* ** Delete an interpreter. */ void Th_DeleteInterp(Th_Interp *interp){ assert(interp->pFrame); assert(0==interp->pFrame->pCaller); /* Delete the contents of the global frame. */ thPopFrame(interp); /* Delete any result currently stored in the interpreter. */ Th_SetResult(interp, 0, 0); /* Delete all registered commands and the command hash-table itself. */ | > > > > > > > | 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 | /* ** Delete an interpreter. */ void Th_DeleteInterp(Th_Interp *interp){ assert(interp->pFrame); assert(0==interp->pFrame->pCaller); /* Delete any client-side gc entries first. */ if( interp->paGc ){ Th_HashIterate(interp, interp->paGc, thFreeGc, (void *)interp); Th_HashDelete(interp, interp->paGc); interp->paGc = NULL; } /* Delete the contents of the global frame. */ thPopFrame(interp); /* Delete any result currently stored in the interpreter. */ Th_SetResult(interp, 0, 0); /* Delete all registered commands and the command hash-table itself. */ |
︙ | ︙ | |||
2330 2331 2332 2333 2334 2335 2336 | ** This function is the same as the standard strlen() function, except ** that it returns 0 (instead of being undefined) if the argument is ** a null pointer. */ int th_strlen(const char *zStr){ int n = 0; if( zStr ){ | | | 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 | ** This function is the same as the standard strlen() function, except ** that it returns 0 (instead of being undefined) if the argument is ** a null pointer. */ int th_strlen(const char *zStr){ int n = 0; if( zStr ){ while( zStr[n] ) ++n; } return n; } /* Whitespace characters: ** ** ' ' 0x20 |
︙ | ︙ | |||
2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 | } } *z = '\0'; return Th_SetResult(interp, zBuf, -1); } #ifdef TH_USE_SQLITE int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){ int i, x; sqlite3_stmt * s; sqlite3_stmt ** list = interp->stmt.aStmt; for( i = 0; i < interp->stmt.nStmt; ++i ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 | } } *z = '\0'; return Th_SetResult(interp, zBuf, -1); } int Th_Data_Set( Th_Interp * interp, char const * key, void * pData, void (*finalizer)( Th_Interp *, void * ) ){ Th_HashEntry * pEnt; Th_GcEntry * pGc; if(NULL == interp->paGc){ interp->paGc = Th_HashNew(interp); assert(NULL != interp->paGc); if(!interp->paGc){ return TH_ERROR; } } pEnt = Th_HashFind(interp, interp->paGc, key, th_strlen(key), 1); if( pEnt->pData ){ thFreeGc( pEnt, interp ); } assert( NULL == pEnt->pData ); pEnt->pData = pGc = (Th_GcEntry*)Th_Malloc(interp, sizeof(Th_GcEntry)); pGc->pData = pData; pGc->xDel = finalizer; return 0; } void * Th_Data_Get( Th_Interp * interp, char const * key ){ Th_HashEntry * e = interp->paGc ? Th_HashFind(interp, interp->paGc, key, th_strlen(key), 0) : NULL; return e ? e->pData : NULL; } #ifdef TH_USE_SQLITE int Th_AddStmt(Th_Interp *interp, sqlite3_stmt * pStmt){ int i, x; sqlite3_stmt * s; sqlite3_stmt ** list = interp->stmt.aStmt; for( i = 0; i < interp->stmt.nStmt; ++i ){ |
︙ | ︙ | |||
2751 2752 2753 2754 2755 2756 2757 | #ifdef TH_USE_OUTBUF /* Reminder: the ob code "really" belongs in th_lang.c, but we need access to Th_Interp internals in order to swap out Th_Vtab parts for purposes of stacking layers of buffers. */ | | > > > > > > | | > | > > | | 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 | #ifdef TH_USE_OUTBUF /* Reminder: the ob code "really" belongs in th_lang.c, but we need access to Th_Interp internals in order to swap out Th_Vtab parts for purposes of stacking layers of buffers. */ #define Th_Ob_Man_empty_m { \ NULL/*aBuf*/, \ 0/*nBuf*/, \ -1/*cursor*/, \ NULL/*interp*/, \ NULL/*aVtab*/ \ } static const Th_Ob_Man Th_Ob_Man_empty = Th_Ob_Man_empty_m; static Th_Ob_Man Th_Ob_Man_instance = Th_Ob_Man_empty_m; #define Th_Ob_Man_KEY "Th_Ob_Man" Th_Ob_Man * Th_ob_manager(Th_Interp *interp){ void * rc = Th_Data_Get(interp, Th_Ob_Man_KEY ); return rc ? ((Th_GcEntry*)rc)->pData : NULL; } Blob * Th_ob_current( Th_Ob_Man * pMan ){ return pMan->nBuf>0 ? pMan->aBuf[pMan->cursor] : 0; } /* |
︙ | ︙ | |||
2803 2804 2805 2806 2807 2808 2809 | if( pMan->cursor >= pMan->nBuf-2 ){ /* expand if needed */ x = pMan->nBuf + 5; if( pMan->cursor >= x ) { assert( 0 && "This really should not happen." ); x = pMan->cursor + 5; } | < | 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 | if( pMan->cursor >= pMan->nBuf-2 ){ /* expand if needed */ x = pMan->nBuf + 5; if( pMan->cursor >= x ) { assert( 0 && "This really should not happen." ); x = pMan->cursor + 5; } void * re = Th_Realloc( pMan->interp, pMan->aBuf, x * sizeof(Blob*) ); if(NULL==re){ goto error; } pMan->aBuf = (Blob **)re; re = Th_Realloc( pMan->interp, pMan->aVtab, x * sizeof(Th_Vtab*) ); if(NULL==re){ |
︙ | ︙ | |||
2830 2831 2832 2833 2834 2835 2836 | pMan->aBuf[pMan->cursor] = pBlob; pMan->aVtab[pMan->cursor] = pMan->interp->pVtab; pMan->interp->pVtab = &Th_Vtab_Ob; Th_Vtab_Ob.out.pState = pMan; if( pOut ){ *pOut = pBlob; } | < > > > > < > > > > > > > > > > | > | > > | > | > | 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 | pMan->aBuf[pMan->cursor] = pBlob; pMan->aVtab[pMan->cursor] = pMan->interp->pVtab; pMan->interp->pVtab = &Th_Vtab_Ob; Th_Vtab_Ob.out.pState = pMan; if( pOut ){ *pOut = pBlob; } return TH_OK; error: if( pBlob ){ Th_Free( pMan->interp, pBlob ); } return TH_ERROR; } Blob * Th_ob_pop( Th_Ob_Man * pMan ){ if( pMan->cursor < 0 ){ return NULL; }else{ Blob * rc; assert( pMan->nBuf > pMan->cursor ); rc = pMan->aBuf[pMan->cursor]; pMan->aBuf[pMan->cursor] = NULL; pMan->interp->pVtab = pMan->aVtab[pMan->cursor]; pMan->aVtab[pMan->cursor] = NULL; if(-1 == --pMan->cursor){ Th_Interp * interp = pMan->interp; Th_Free( pMan->interp, pMan->aBuf ); Th_Free( pMan->interp, pMan->aVtab ); *pMan = Th_Ob_Man_empty; pMan->interp = interp; } return rc; } } void Th_ob_cleanup( Th_Ob_Man * man ){ Blob * b; while( (b = Th_ob_pop(man)) ){ blob_reset(b); Th_Free( man->interp, b ); } } /* ** TH Syntax: ** ** ob clean ** ** Erases any currently buffered contents but does not modify ** the buffering level. */ static int ob_clean_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ const char doRc = ctx ? 1 : 0; Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp); Blob * b; assert( pMan && (interp == pMan->interp) ); b = pMan ? Th_ob_current(pMan) : NULL; if(!b){ Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); return TH_ERROR; }else{ blob_reset(b); if( doRc ) { Th_SetResultInt( interp, 0 ); } return TH_OK; } } /* ** TH Syntax: ** ** ob end ** ** Erases any currently buffered contents and pops the current buffer ** from the stack. */ static int ob_end_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ const char doRc = ctx ? 1 : 0; Th_Ob_Man * pMan = ctx ? (Th_Ob_Man *)ctx : Th_ob_manager(interp); Blob * b; assert( pMan && (interp == pMan->interp) ); b = Th_ob_pop(pMan); if(!b){ Th_ErrorMessage( interp, "Not currently buffering.", NULL, 0 ); return TH_ERROR; }else{ blob_reset(b); Th_Free( interp, b ); if(doRc){ Th_SetResultInt( interp, 0 ); } return TH_OK; } } /* ** TH Syntax: ** |
︙ | ︙ | |||
2947 2948 2949 2950 2951 2952 2953 | int argPos = 2; char const * sub = argv[argPos]; int subL = argl[argPos]; /* "flush end" */ if(th_strlen(sub)==3 && ((0==memcmp("end", sub, subL) || (0==memcmp("pop", sub, subL))))){ | | | 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 | int argPos = 2; char const * sub = argv[argPos]; int subL = argl[argPos]; /* "flush end" */ if(th_strlen(sub)==3 && ((0==memcmp("end", sub, subL) || (0==memcmp("pop", sub, subL))))){ rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1); } } Th_SetResultInt( interp, 0 ); return rc; } /* |
︙ | ︙ | |||
2985 2986 2987 2988 2989 2990 2991 | int rc = TH_OK; Th_SetResult( interp, blob_str(b), b->nUsed ); if(argc>argPos){ sub = argv[argPos]; subL = argl[argPos]; /* "ob get clean" */ if(!rc && th_strlen(sub)==5 && 0==memcmp("clean", sub, subL)){ | | | | 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 | int rc = TH_OK; Th_SetResult( interp, blob_str(b), b->nUsed ); if(argc>argPos){ sub = argv[argPos]; subL = argl[argPos]; /* "ob get clean" */ if(!rc && th_strlen(sub)==5 && 0==memcmp("clean", sub, subL)){ rc |= ob_clean_command(interp, NULL, argc-1, argv+1, argl+1); }/* "ob get end" */ else if(!rc && th_strlen(sub)==3 && ((0==memcmp("end", sub, subL)) || (0==memcmp("pop", sub, subL)))){ rc |= ob_end_command(interp, NULL, argc-1, argv+1, argl+1); } } return rc; } } /* |
︙ | ︙ | |||
3034 3035 3036 3037 3038 3039 3040 | assert( pMan && (interp == pMan->interp) ); rc = Th_ob_push(pMan, &b); if( TH_OK != rc ){ assert( NULL == b ); return rc; } assert( NULL != b ); | < > > > > > > > > > > > > | | > | < | < < < < < < < > | > > > > > > > > > > > > | 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 | assert( pMan && (interp == pMan->interp) ); rc = Th_ob_push(pMan, &b); if( TH_OK != rc ){ assert( NULL == b ); return rc; } assert( NULL != b ); Th_SetResultInt( interp, 1 + pMan->cursor ); return TH_OK; } static void finalizerObMan( Th_Interp * interp, void * p ){ Th_Ob_Man * man = (Th_Ob_Man*)p; /*printf("finalizerObMan(%p,%p)\n", interp, p );*/ if(man){ assert( interp == man->interp ); Th_ob_cleanup( man ); if( man != &Th_Ob_Man_instance ){ Th_Free( interp, man ); } } } /* ** TH Syntax: ** ** ob clean|(end|pop)|flush|get|level|(start|push) ** ** Runs the given subcommand. Some subcommands have other subcommands ** (see their docs for details). ** */ static int ob_cmd( Th_Interp *interp, void *ignored, int argc, const char **argv, int *argl ){ Th_Ob_Man * pMan = Th_ob_manager(interp); Th_SubCommand aSub[] = { { "clean", ob_clean_command }, { "end", ob_end_command }, { "flush", ob_flush_command }, { "get", ob_get_command }, { "level", ob_level_command }, { "pop", ob_end_command }, { "push", ob_start_command }, { "start", ob_start_command }, { 0, 0 } }; assert(NULL != pMan && pMan->interp==interp); return Th_CallSubCommand(interp, pMan, argc, argv, argl, aSub); } int th_register_ob(Th_Interp * interp){ int rc; static Th_Command_Reg aCommand[] = { {"ob", ob_cmd, 0}, {0,0,0} }; rc = Th_register_commands( interp, aCommand ); if(NULL == Th_ob_manager(interp)){ Th_Ob_Man * pMan; pMan = 1 ? &Th_Ob_Man_instance : Th_Malloc(interp, sizeof(Th_Ob_Man)); /* *pMan = Th_Ob_Man_empty;*/ pMan->interp = interp; Th_Data_Set( interp, Th_Ob_Man_KEY, pMan, finalizerObMan ); assert( NULL != Th_ob_manager(interp) ); } return rc; } #undef Th_Ob_Man_empty_m #undef Th_Ob_Man_KEY #endif /* end TH_USE_OUTBUF */ |
Changes to src/th.h.
︙ | ︙ | |||
267 268 269 270 271 272 273 274 275 276 277 278 279 280 | /* mkindex cannot do enums enum Th_Render_Flags { */ #define Th_Render_Flags_DEFAULT 0 #define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1) /*};*/ int Th_Render(const char *z, int flags); /* ** Registers a list of commands with the interpreter. pList must be a non-NULL ** pointer to an array of Th_Command_Reg objects, the last one of which MUST ** have a NULL zName field (that is the end-of-list marker). ** Returns TH_OK on success, "something else" on error. */ | > > > > > > > > > > > > > > > > > > > > > > > > > | 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | /* mkindex cannot do enums enum Th_Render_Flags { */ #define Th_Render_Flags_DEFAULT 0 #define Th_Render_Flags_NO_DOLLAR_DEREF (1 << 1) /*};*/ int Th_Render(const char *z, int flags); /* ** Adds a piece of memory to the given interpreter, such that: ** ** a) it will be cleaned up when the interpreter is destroyed, by ** calling finalizer(interp, pData). The finalizer may be NULL. ** Cleanup happens in an unspecified/unpredictable order. ** ** b) it can be fetched via Th_Data_Get(). ** ** If a given key is added more than once then any previous ** entry is cleaned up before adding it. ** ** Returns 0 on success, non-0 on allocation error. */ int Th_Data_Set( Th_Interp * interp, char const * key, void * pData, void (*finalizer)( Th_Interp *, void * ) ); /* ** Fetches data added via Th_Data_Set(), or NULL if no data ** has been associated with the given key. */ void * Th_Data_Get( Th_Interp * interp, char const * key ); /* ** Registers a list of commands with the interpreter. pList must be a non-NULL ** pointer to an array of Th_Command_Reg objects, the last one of which MUST ** have a NULL zName field (that is the end-of-list marker). ** Returns TH_OK on success, "something else" on error. */ |
︙ | ︙ |