diff --git a/Basics/conversions.c b/Basics/conversions.c index 32d116d6ed..ddf522fcee 100644 --- a/Basics/conversions.c +++ b/Basics/conversions.c @@ -28,6 +28,7 @@ #include "conversions.h" #include +#include // ----------------------------------------------------------------------------- // --SECTION-- public functions for string to number @@ -398,6 +399,19 @@ char* TRI_StringUInt64 (uint64_t attr) { return TRI_DuplicateString(buffer); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief convert to string from double +//////////////////////////////////////////////////////////////////////////////// + +char* TRI_StringDouble (double value) { + TRI_string_buffer_t buffer; + + TRI_InitStringBuffer(&buffer); + TRI_AppendDoubleStringBuffer(&buffer, value); + + return buffer._buffer; +} + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// diff --git a/Basics/conversions.h b/Basics/conversions.h index e313561aba..49ff8b3698 100644 --- a/Basics/conversions.h +++ b/Basics/conversions.h @@ -128,6 +128,12 @@ char* TRI_StringInt64 (int64_t); char* TRI_StringUInt64 (uint64_t); +//////////////////////////////////////////////////////////////////////////////// +/// @brief convert to string from double +//////////////////////////////////////////////////////////////////////////////// + +char* TRI_StringDouble (double); + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// diff --git a/Basics/fsrt.inc b/Basics/fsrt.inc new file mode 100644 index 0000000000..024a98ae2e --- /dev/null +++ b/Basics/fsrt.inc @@ -0,0 +1,837 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief High-Performance Database Framework made by triagens +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-2011 triagens GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Martin Schoenert +/// @author Copyright 2009-2010, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @page FruitSort Fruit Sort +/// +/// @section Pre-processor directive FSRT_TYPE +/// +/// The directive @ec FSRT_TYPE refers to the type of each element which is to +/// be sorted. If this directive is undefined, then the type @c char* is used +/// instead, which may result in a reduction in the speed of the sort +/// algorithm. For example, +/// +/// If you define @c FSRT_TYPE the corresponding macros functions will have the +/// following signatures: +/// +/// void FSRT_NAME( FSRT_TYPE * fs_b, FSRT_TYPE * fs_e ) @n +/// int FSRT_LT( FSRT_TYPE * fs_x, FSRT_TYPE * fs_y, size_t fs_s ) @n +/// int FSRT_EQ( FSRT_TYPE * fs_x, FSRT_TYPE * fs_y, size_t fs_s ) @n +/// int FSRT_COMP( FSRT_TYPE * fs_x, FSRT_TYPE * fs_y, size_t fs_s ) @n +/// void FSRT_COPY( FSRT_TYPE * fs_x, FSRT_TYPE * fs_y, size_t fs_s ) @n +/// +/// If @c FSRT_TYPE is undefined the corresponding macros functions will macros +/// have the following signatures: +/// +/// void FSRT_NAME( char * fs_b, char * fs_e, size_t fs_s, char * fs_t ) @n +/// int FSRT_LT( char * fs_x, char * fs_y, size_t fs_s ) @n +/// int FSRT_EQ( char * fs_x, char * fs_y, size_t fs_s ) @n +/// int FSRT_COMP( char * fs_x, char * fs_y, size_t fs_s ) @n +/// void FSRT_COPY( char * fs_x, char * fs_y, size_t fs_s ) @n +/// +/// @section Pre-processor directive FSRT_NAME +/// +/// The pre-processor directive @c FSRT_NAME must be defined. +/// +/// The function @c FSRT_NAME(fs_b,fs_e) sorts an array of statically sized +/// elements. The parameters @c fs_b and @c fs_e are pointers or iterators +/// which access the first and last - 1 element. +/// +/// The function @c FSRT_NAME(fs_b,fs_e,fs_s,fs_t) can be used to sort elements +/// whose complete type and size is unkown at compile time. The additional +/// parameter @c fs_s refers to the size of the elements, while the parameter +/// @c fs_t refers to a location in memory used to store a temporary element. +/// +/// @section Pre-processor directive FSRT_UNIQ +/// +/// The directive @c FSRT_UNIQ provides a hint that the elements to be sorted +/// are unique (or little repetitions). In this case, the sorting algorithm will +/// use a 2-way partitioning of the elements into <= and >=. The default +/// alogorithm is a 3-way partitioning of the elements into <, = and >. +/// +/// Define this directive when know that the number of repetitions is small. +/// +/// @section Pre-processor directive FSRT_INSR +/// +/// The directive @c FSRT_INSR represents the cutoff where the algorithm +/// switches from quick sort to insertion sort, i.e. subranges shorter than +/// @c FSRT_INSR will be sorted with Insertion sort algorithm rather than the +/// Quicksort algorithm. +/// +/// Thus optimal value for @c FSRT_INSR is dependent on the costs associated +/// with the comparisons copies of elements. The default value of 8 (when +/// @c FSRT_INSR is undefined) is reasonable most of the time. If the cost of +/// comparisons and copies is small, you can use larger values (up to about 40) +/// to increase performance. Consider reducing the value of @c FSRT_INSR where +/// the cost of comparisons and copies is high (down to 4). +/// +/// @section Pre-processor directive FSRT_THREE +/// +/// The directive @c FSRT_THREE represents the cutoff for median of 3 pivot +/// selection. Defaults to 10 unless otherwise defined. The directive +/// @c FSRT_NINE represents the cutoff for median of 9 pivot selection. +/// Defaults to 10 unless otherwise defined. +/// +/// @section Pre-processor directive FSRT_EXTRA_ARGS +/// +/// The directive @c FSRT_EXTRA_ARGS provides a list of additional arguments +/// which may be required by the copy and comparison functions. +/// @c FSRT_EXTRA_ARGS is a comma separated list of names of the additional +/// function arguments required. By default no additional arguments are defined. +/// The directive @c FSRT_EXTRA_DECL provides additional declarations for any +/// extra arguments which may be required. @c FSRT_EXTRA_DECL is a comman +/// separated list of declarations. +/// +/// By default no additional declarations are defined. For example: +/// +/// #define FSRT_EXTRA_ARGS dsd, loc @n +/// #define FSRT_EXTRA_DECL DataSetDescription* dsd, Locale* loc @n +/// #define FSRT_LT(x,y,s,d,l) my_str_cmp(x,y,d,l) @n +/// +/// @section Required Directives +/// +/// @c FSRT_NAME and @c FSRT_NAM2. +/// +/// @section Required Macros +/// +/// @c FSRT_LT and @c FSRT_EQ or @c FSRT_COMP. +/// +/// @section Optional Macros +/// +/// @c FSRT_COPY, @c FSRT_TYPE, @c FSRT_UNIQ, @c FSRT_INSR, @c FSRT_THREE, +/// @c FSRT_NINE @c FSRT_EXTRA_ARGS, @c FSRT_EXTRA_DECL, @c FSRT_COUNT, @c +/// FSRT_DEBUG, @c FSRT_COUNT. +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup FruitSort Fruit Sort +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT_COUNT +/// @brief statistics buffer +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FSRT_COUNT +static uint32_t FSRT_Count [20]; +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__TYPE +/// @brief the type of the elements which are begin sorted +/// +/// Optional. Unless specified the type defaults to a @c char. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__UNIT +/// @brief the size of an element specified in terms of the @c FSRT_TYPE +/// +/// When the default value of @c FSRT_TYPE is @c char, this defaults +/// to the size of the elements in bytes. That is, @c fs_s. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__SIZE +/// @brief the size of an element given in bytes +/// +/// That is, @a fs_s. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__TEMP +/// @brief the address of some memory storage set aside for an element +/// +/// The address of some memory storage set aside at least as large as the size +/// of an element. This temporary storage is used by the function @c FSRT_NAME. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__DIST +/// @brief The number of elements between a given two elements. +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FSRT_TYPE + +#define FSRT__TYPE FSRT_TYPE +#define FSRT__UNIT 1 +#define FSRT__SIZE sizeof(FSRT_TYPE) +#define FSRT__TEMP &fs_t +#define FSRT__DIST(fs_x,fs_y,fs_s) ((fs_x) - (fs_y)) + +#else + +#define FSRT__TYPE char +#define FSRT__UNIT (fs_s) +#define FSRT__SIZE (fs_s) +#define FSRT__TEMP fs_t +#define FSRT__DIST(fs_x,fs_y,fs_s) (((fs_x) - (fs_y)) / (fs_s)) + +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__LT(x,y,s,d) +/// @brief less-than comparison +/// +/// Returns true if @c x is less than @c y. Otherwise, returns false. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__EQ(x,y,s,d) +/// @brief equal-than comparison +/// +/// Returns the integer 0 if the elements @c x and @c y both of size @c s are +/// equal. A non-zero value is returned otherwise. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__LEC(x,y,s,d) +/// @brief comparison +/// +/// Sets the var @c fs_c to the result of a comparision of @c x and @c y. A +/// value of 0 indicates equality between @c x and @c y. This variable is used +/// as part of the sorting algorithm. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__EQC(x,y,s,d) +/// @brief equalitity test +/// +/// Performs a test of equality between @c x and @c y. The value of the variable +/// @a fs_c contains an integer which is the result of a @em previous +/// comparision between @a x and @a y. +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FSRT_LT + +#ifndef FSRT__COUNT +#define FSRT__LT(fs_x,fs_y,fs_s,fs_d) FSRT_LT((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS) +#define FSRT__EQ(fs_x,fs_y,fs_s,fs_d) FSRT_EQ((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS) +#define FSRT__LEC(fs_x,fs_y,fs_s,fs_d) (! FSRT_LT((fs_y),(fs_x),(fs_s) FSRT__EXTRA_ARGS)) +#define FSRT__EQC(fs_x,fs_y,fs_s,fs_d) FSRT_EQ((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS) +#else +#define FSRT__LT(fs_x,fs_y,fs_s,fs_d) (FSRT_Count[(fs_d)]++, FSRT_LT((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS)) +#define FSRT__EQ(fs_x,fs_y,fs_s,fs_d) (FSRT_Count[(fs_d)]++, FSRT_EQ((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS)) +#define FSRT__LEC(fs_x,fs_y,fs_s,fs_d) (FSRT_Count[(fs_d)]++, (! FSRT_LT((fs_y),(fs_x),(fs_s) FSRT__EXTRA_ARGS))) +#define FSRT__EQC(fs_x,fs_y,fs_s,fs_d) (FSRT_Count[(fs_d)]++, FSRT_EQ((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS)) +#endif + +#else + +#ifndef FSRT__COUNT +#define FSRT__LT(fs_x,fs_y,fs_s,fs_d) (FSRT_COMP((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS) < 0) +#define FSRT__EQ(fs_x,fs_y,fs_s,fs_d) (FSRT_COMP((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS) == 0) +#define FSRT__LEC(fs_x,fs_y,fs_s,fs_d) ((fs_c = FSRT_COMP((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS)) <= 0) +#define FSRT__EQC(fs_x,fs_y,fs_s,fs_d) (fs_c == 0) +#else +#define FSRT__LT(fs_x,fs_y,fs_s,fs_d) (FSRT_Count[(fs_d)]++, (FSRT_COMP((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS) < 0)) +#define FSRT__EQ(fs_x,fs_y,fs_s,fs_d) (FSRT_Count[(fs_d)]++, (FSRT_COMP((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS) == 0)) +#define FSRT__LEC(fs_x,fs_y,fs_s,fs_d) (FSRT_Count[(fs_d)]++, ((fs_c = FSRT_COMP((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS)) <= 0)) +#define FSRT__EQC(fs_x,fs_y,fs_s,fs_d) (FSRT_Count[(fs_d)]++, (fs_c == 0)) +#endif + +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__COPY +/// @brief copies one element onto another +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__SWAP +/// @brief swaps two elements +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FSRT_COPY + +#ifndef FSRT__COUNT +#define FSRT__COPY(fs_x,fs_y,fs_s,fs_d) FSRT_COPY((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS) +#else +#define FSRT__COPY(fs_x,fs_y,fs_s,fs_d) (FSRT_Count[(fs_d)]++, FSRT_COPY((fs_x),(fs_y),(fs_s) FSRT__EXTRA_ARGS)) +#endif + +#else + +#ifndef FSRT__COUNT +#define FSRT__COPY(fs_x,fs_y,fs_s,fs_d) (memmove( (char*)(fs_x), (char*)(fs_y), (fs_s) )) +#else +#define FSRT__COPY(fs_x,fs_y,fs_s,fs_d) (FSRT_Count[(fs_d)]++, (memcpy( (char*)(fs_x), (char*)(fs_y), (fs_s) ))) +#endif + +#endif + +#define FSRT__SWAP(fs_x,fs_y,fs_s,fs_d) (FSRT__COPY( FSRT__TEMP, (fs_x), (fs_s), (fs_d) ), \ + FSRT__COPY( (fs_x), (fs_y), (fs_s), (0) ), \ + FSRT__COPY( (fs_y), FSRT__TEMP, (fs_s), (0) )) + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__INSR +/// @brief Cutoff point when to use the insertion short algorithm. +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FSRT_INSR +#define FSRT__INSR FSRT_INSR +#else +#define FSRT__INSR 8 +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__THREE +/// @brief Used for sorting short arrays. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__NINE +/// @brief Used for sorting short arrays. +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FSRT_THREE +#define FSRT__THREE FSRT_THREE +#else +#define FSRT__THREE 10 +#endif +#ifdef FSRT_NINE +#define FSRT__NINE FSRT_NINE +#else +#define FSRT__NINE 40 +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__EXTRA_ARGS +/// @brief additional arguments which may be required for the algorithm +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__EXTRA_DECL +/// @brief additional declarations which may be required for the algorithm +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FSRT_EXTRA_ARGS +#define FSRT__EXTRA_ARGS , FSRT_EXTRA_ARGS +#define FSRT__EXTRA_DECL , FSRT_EXTRA_DECL +#else +#define FSRT__EXTRA_ARGS // none +#define FSRT__EXTRA_DECL // none +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @fn FSRT__TYPE * FSRT_NAM2 ( +/// FSRT__TYPE * fs_x, +/// FSRT__TYPE * fs_y, +/// FSRT__TYPE * fs_z +/// FSRT__EXTRA_DECL ) +/// +/// @brief returns the middle element given three elements +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__MED3 +/// @brief an alias for the FSRT_NAM2 function +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FSRT_TYPE + +#define FSRT__MED3(fs_x,fs_y,fs_z,fs_s) FSRT_NAM2((fs_x),(fs_y),(fs_z) FSRT__EXTRA_ARGS) + +FSRT__TYPE * FSRT_NAM2 ( + FSRT__TYPE * fs_x, + FSRT__TYPE * fs_y, + FSRT__TYPE * fs_z + FSRT__EXTRA_DECL ) { + +#else + +#define FSRT__MED3(fs_x,fs_y,fs_z,fs_s) FSRT_NAM2((fs_x),(fs_y),(fs_z),(fs_s) FSRT__EXTRA_ARGS) + +FSRT__TYPE * FSRT_NAM2 ( + FSRT__TYPE * fs_x, + FSRT__TYPE * fs_y, + FSRT__TYPE * fs_z, + size_t fs_s + FSRT__EXTRA_DECL ) { + +#endif + + if ( FSRT__LT( fs_x, fs_y, FSRT__SIZE, 1 ) ) { + if ( FSRT__LT( fs_y, fs_z, FSRT__SIZE, 1 ) ) { return fs_y; } + else if ( FSRT__LT( fs_x, fs_z, FSRT__SIZE, 1 ) ) { return fs_z; } + else { return fs_x; } + } + else { + if ( FSRT__LT( fs_x, fs_z, FSRT__SIZE, 1 ) ) { return fs_x; } + else if ( FSRT__LT( fs_y, fs_z, FSRT__SIZE, 1 ) ) { return fs_z; } + else { return fs_y; } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @var FSRT_Rand +/// @brief stores the position of the current random element within the array +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FSRT__RAND +uint32_t FSRT_Rand; +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__RAND +/// @brief computes a quasi-random element +//////////////////////////////////////////////////////////////////////////////// + +#ifndef FSRT__RAND +#define FSRT__RAND \ + ((fs_b) + FSRT__UNIT * ((FSRT_Rand = FSRT_Rand * 31415 + 27818) % FSRT__DIST(fs_e,fs_b,FSRT__SIZE))) +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// @fn void FSRT_NAME ( +/// FSRT__TYPE * fs_b, +/// FSRT__TYPE * fs_e +/// FSRT__EXTRA_DECL ) +/// +/// @brief The actual sorting algorithm. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @def FSRT__SORT +/// +/// @brief An alias for the sorting algorithm FSRT_NAME. +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FSRT_TYPE + +#define FSRT__SORT(fs_x,fs_y,fs_s,fs_t) FSRT_NAME((fs_x),(fs_y) FSRT__EXTRA_ARGS) + +void FSRT_NAME ( + FSRT__TYPE * fs_b, + FSRT__TYPE * fs_e + FSRT__EXTRA_DECL ) { + FSRT__TYPE * fs_l; + FSRT__TYPE * fs_r; + FSRT__TYPE * fs_p; +#ifndef FSRT_UNIQ + FSRT__TYPE * fs_m; + FSRT__TYPE * fs_q; +#endif + FSRT__TYPE fs_t; + +#else + +#define FSRT__SORT(fs_x,fs_y,fs_s,fs_t) FSRT_NAME((fs_x),(fs_y),(fs_s),(fs_t) FSRT__EXTRA_ARGS) + +void FSRT_NAME ( + FSRT__TYPE * fs_b, + FSRT__TYPE * fs_e, + size_t fs_s, + FSRT__TYPE * fs_t + FSRT__EXTRA_DECL ) { + + FSRT__TYPE * fs_l; + FSRT__TYPE * fs_r; + FSRT__TYPE * fs_p; +#ifndef FSRT_UNIQ + FSRT__TYPE * fs_m; + FSRT__TYPE * fs_q; +#endif + +#endif + +#ifndef FSRT_LT + int fs_c; +#endif + + // iterate while the number of elements in the range is large enough + while ( FSRT__INSR <= FSRT__DIST(fs_e,fs_b,FSRT__SIZE) ) { + + // for larger ranges take the median-of-median of nine random elms + if ( FSRT__NINE <= FSRT__DIST(fs_e,fs_b,FSRT__SIZE) ) { + fs_l = FSRT__MED3( FSRT__RAND, FSRT__RAND, FSRT__RAND, FSRT__SIZE ); + fs_r = FSRT__MED3( FSRT__RAND, FSRT__RAND, FSRT__RAND, FSRT__SIZE ); + fs_p = FSRT__MED3( FSRT__RAND, FSRT__RAND, FSRT__RAND, FSRT__SIZE ); + fs_p = FSRT__MED3( fs_l, fs_r, fs_p, FSRT__SIZE ); + } + + // for smaller ranges take the median of three random elms + else if ( FSRT__THREE <= FSRT__DIST(fs_e,fs_b,FSRT__SIZE) ) { + fs_p = FSRT__MED3( FSRT__RAND, FSRT__RAND, FSRT__RAND, FSRT__SIZE ); + } + + // and for very small ranges take one random element + else { + fs_p = FSRT__RAND; + } + + // swap the pivot to the beg pos (out of the way for partitioning) + FSRT__SWAP( fs_b, fs_p, FSRT__SIZE, 4 ); + +#ifdef FSRT_UNIQ + + // 2-way partitioning into elements le and ge. + // Swap elements smaller (or equal) to the pivot to the left, + // and elements larger (or equal) to the pivot to the right. + // Note that scanning pointers stop on and exchange equal elements + // This is important if we sort arrays with few different keys + // because after a few iterations the subranges contain only + // elements with equal keys, and if we would scan over elements + // equal to the pivot, the right pointer would scan all the way + // down and the quick sort would degenerate to a quadratic algo. + +#define FSRT__UNIQ__ALG1 +#ifdef FSRT__UNIQ__ALG1 + + // Note that this code is a lot trickier than it appears ;-) + // It is faster than other more obvious formulations, because + // it compares every element exactly once to the pivot and + // it keeps the number of statements (especially jumps) minimal + // so the optimizer can generate compact and fast code. + + fs_p = fs_b; + fs_l = fs_b; + fs_r = fs_e; + + // *(b==l) <= *p and *p <= *r .. *(e-1) since the set is empty + while ( fs_l < fs_r ) { + + // *b .. *(l-1) *l <= *p *p <= *r *(r+1) .. *(e-1) + do { fs_r -= FSRT__UNIT; } + while ( fs_l < fs_r && FSRT__LT( fs_p, fs_r, FSRT__SIZE, 2 ) ); + + // *p >= *r *p <= *(r+1) .. *(e-1) + do { fs_l += FSRT__UNIT; } + while ( fs_l < fs_r && FSRT__LT( fs_l, fs_p, FSRT__SIZE, 2 ) ); + + // *b .. *(l-1) <= *p and ((l < r & *l >= *p) or (l >= r)) + if ( fs_l < fs_r ) { + + // .. *(l-1) <= *p *l >= *p *p >= *r *p <= *(r+1) .. + FSRT__SWAP( fs_l, fs_r, FSRT__SIZE, 4 ); + // *b .. *(l-1) *l <= *p *p >= *r *(r+1) .. *(e-1) + } + } + + // l == r and *b .. *(l-1) *(l==r) <= *p <= *(r+1) .. *(e-1) + // l == r+1 and *b .. *(l-1==r) <= *p <= *(l==r+1) .. *(e-1) + + // swap the pivot to the middle + FSRT__SWAP( fs_b, fs_r, FSRT__SIZE, 4 ); + fs_l = fs_r; + fs_r = fs_l + FSRT__UNIT; + +#endif + +#ifdef FSRT__UNIQ__ALG2 + + // This is a 2-way partitioning algorithm. + // It performs exactely the same comparisons and swaps as alg1. + // However it is formulated slightly different. + // This is bad for the performance (the code is bit slower). + // But I leave it in here, because this algorithm serves as a good + // starting point for the 3-way partitioning algorithm below. + + fs_p = fs_b; + fs_l = fs_b+FSRT__UNIT; + fs_r = fs_e-FSRT__UNIT; + + while ( 1 ) { + while ( fs_l <= fs_r && FSRT__LT( fs_p, fs_r, FSRT__SIZE, 2 ) ) { + fs_r -= FSRT__UNIT; + } + + while ( fs_l < fs_r && FSRT__LT( fs_l, fs_p, FSRT__SIZE, 2 ) ) { + fs_l += FSRT__UNIT; + } + + if ( fs_r <= fs_l ) { break; } + FSRT__SWAP( fs_l, fs_r, FSRT__SIZE, 4 ); + fs_r -= FSRT__UNIT; + fs_l += FSRT__UNIT; + } + + fs_l = fs_r + FSRT__UNIT; + + if ( fs_b < fs_r ) { FSRT__SWAP( fs_b, fs_r, FSRT__SIZE, 4 ); } + fs_l -= FSRT__UNIT; + fs_r += FSRT__UNIT; + +#endif + +#ifdef FSRT_DEBUG + for ( fs_p = fs_b; fs_p < fs_e; fs_p++ ) { + if ( fs_p < fs_l ) { + if ( FSRT__LT( fs_l, fs_p, FSRT__SIZE, 0 ) ) { + printf("fs_p %08x not less equal after partitioning\n",fs_p); + break; + } + } + else if ( fs_p < fs_r ) { + if ( ! FSRT__EQ( fs_l, fs_p, FSRT__SIZE, 0 ) ) { + printf("fs_p %08x not equal after partitioning\n",fs_p); + break; + } + } + else { + if ( FSRT__LT( fs_p, fs_l, FSRT__SIZE, 0 ) ) { + printf("fs_p %08x not greater equal after partitioning\n",fs_p); + break; + } + } + } +#endif + +#else + + // 3-way partitioning into elements lt, eq, and gt + // This is a bit slower than 2-way partitioning but a lot faster + // if the range contains only elements with few different keys. + // In the first step this does a split-end partitioning, + // i.e. the elements equal to the pivot end up at the very left + // (left of m) or at the very right (right of q) end of the range. + // In the second step (down below) they are swapped to the middle. + // This has the advantage, that it executes fewer swaps + // (which is quite counter to the intuition). + // It executes about 1/4 N swaps for the not-equal elements and + // 2 E swaps for the equal elements (and even fewer if E > N). + // The alternative (that keeps the equal elements during the + // partitioning in the middle) executes about 3/4 N swaps for the + // not-equal elements and 1 E swaps for the equal elements. + + fs_p = fs_b; + fs_l = fs_b+FSRT__UNIT; + fs_m = fs_b+FSRT__UNIT; + fs_r = fs_e-FSRT__UNIT; + fs_q = fs_e-FSRT__UNIT; + + while ( 1 ) { + while ( fs_l <= fs_r && FSRT__LEC( fs_p, fs_r, FSRT__SIZE, 2 ) ) { + if ( FSRT__EQC( fs_p, fs_r, FSRT__SIZE, 0 ) ) { + if ( fs_r < fs_q ) { FSRT__SWAP( fs_r, fs_q, FSRT__SIZE, 4 ); } + fs_q -= FSRT__UNIT; + } + fs_r -= FSRT__UNIT; + } + + while ( fs_l < fs_r && FSRT__LEC( fs_l, fs_p, FSRT__SIZE, 2 ) ) { + if ( FSRT__EQC( fs_p, fs_l, FSRT__SIZE, 0 ) ) { + if ( fs_m < fs_l ) { FSRT__SWAP( fs_m, fs_l, FSRT__SIZE, 4 ); } + fs_m += FSRT__UNIT; + } + fs_l += FSRT__UNIT; + } + + if ( fs_r <= fs_l ) { break; } + + FSRT__SWAP( fs_l, fs_r, FSRT__SIZE, 4 ); + fs_r -= FSRT__UNIT; + fs_l += FSRT__UNIT; + } + + fs_l = fs_r + FSRT__UNIT; + +#ifdef FSRT_DEBUG + // printf("%08x %08x %08x %08x %08x %08x\n",fs_b,fs_m,fs_l,fs_r,fs_q,fs_e); + for ( fs_p = fs_b; fs_p < fs_e; fs_p++ ) { + if ( fs_p < fs_m ) { + if ( ! FSRT__EQ( fs_p, fs_b, FSRT__SIZE, 0 ) ) { + printf("fs_p %08x < fs_m not equal after partitioning\n",fs_p); + break; + } + } + else if ( fs_p < fs_l ) { + if ( ! FSRT__LT( fs_p, fs_b, FSRT__SIZE, 0 ) ) { + printf("fs_p %08x < fs_l not less after partitioning\n",fs_p); + break; + } + } + if ( fs_q < fs_p ) { + if ( ! FSRT__EQ( fs_b, fs_p, FSRT__SIZE, 0 ) ) { + printf("fs_q < fs_p %08x not equal after partitioning\n",fs_p); + break; + } + } + else if ( fs_r < fs_p ) { + if ( ! FSRT__LT( fs_b, fs_p, FSRT__SIZE, 0 ) ) { + printf("fs_r < fs_p %08x not greater after partitioning\n",fs_p); + break; + } + } + } +#endif + + // Swap the equal elements (including the pivot) to the middle. + // I found it not so easy to get this code correct, so beware ;-) + fs_p = fs_b; + + while ( fs_p < fs_m && fs_m < fs_l ) { + fs_l -= FSRT__UNIT; + FSRT__SWAP( fs_p, fs_l, FSRT__SIZE, 4 ); + fs_p += FSRT__UNIT; + } + + if ( fs_l == fs_m ) { + fs_l = fs_p; + } + + fs_p = fs_e-FSRT__UNIT; + + while ( fs_q < fs_p && fs_r < fs_q ) { + fs_r += FSRT__UNIT; + FSRT__SWAP( fs_r, fs_p, FSRT__SIZE, 4 ); + fs_p -= FSRT__UNIT; + } + + if ( fs_r == fs_q ) { + fs_r = fs_p; + } + + fs_r = fs_r + FSRT__UNIT; + +#ifdef FSRT_DEBUG + + for ( fs_p = fs_b; fs_p < fs_e; fs_p++ ) { + if ( fs_p < fs_l ) { + if ( ! FSRT__LT( fs_p, fs_l, FSRT__SIZE, 0 ) ) { + printf("fs_p %08x < fs_l not less after swapping\n",fs_p); + break; + } + } + else if ( fs_p < fs_r ) { + if ( ! FSRT__EQ( fs_l, fs_p, FSRT__SIZE, 0 ) ) { + printf("fs_p %08x < fs_r not equal after swapping\n",fs_p); + break; + } + } + else { + if ( ! FSRT__LT( fs_l, fs_p, FSRT__SIZE, 0 ) ) { + printf("fs_r <= fs_p %08x not greater after swapping\n",fs_p); + break; + } + } + } +#endif + +#endif + + // Now sort the range before and the range after the pivot. + // Sort the smaller range recursively and the larger iteratively. + // This limits the maximal redursion depth to log2(N). + // To avoid extra variables we modify the arguments of the func. + + if ( fs_l-fs_b < fs_e-fs_r ) { + FSRT__SORT( fs_b, fs_l, FSRT__SIZE, FSRT__TEMP ); + fs_b = fs_r; + } + else { + FSRT__SORT( fs_r, fs_e, FSRT__SIZE, FSRT__TEMP ); + fs_e = fs_l; + } + } + + // Finally the remaining short subrange is sorted with insertion sort. + // Sorting each individual short subrange instead of executing one + // insertion sort over the entire range at the end is more efficient, + // because we know that all the pivots (and all the elements equal to + // them) are at the right location, so we need not compare them with + // their predecessor and successor. + // It may also be a bit more cache friendly. + // I tried other algorithms (shell sort, special cases, etc.) + // but in the end this algorithm always was the fastest + // (probably because the subranges are so small). + + for ( fs_l = fs_b+FSRT__UNIT; fs_l < fs_e; fs_l += FSRT__UNIT ) { + if ( FSRT__LT( fs_l, fs_l-FSRT__UNIT, FSRT__SIZE, 3 ) ) { + FSRT__COPY( FSRT__TEMP, fs_l, FSRT__SIZE, 5 ); + FSRT__COPY( fs_l, fs_l-FSRT__UNIT, FSRT__SIZE, 5 ); + for ( fs_r = fs_l-2*FSRT__UNIT; + fs_b <= fs_r && FSRT__LT( FSRT__TEMP, fs_r, FSRT__SIZE, 3 ); + fs_r -= FSRT__UNIT ) { + FSRT__COPY( fs_r+FSRT__UNIT, fs_r, FSRT__SIZE, 5 ); + } + FSRT__COPY( fs_r+FSRT__UNIT, FSRT__TEMP, FSRT__SIZE, 5 ); + } + } +} + +#undef FSRT__TYPE +#undef FSRT__UNIT +#undef FSRT__SIZE +#undef FSRT__DIST +#undef FSRT__TEMP +#undef FSRT__LT +#undef FSRT__EQ +#undef FSRT__LEC +#undef FSRT__EQC +#undef FSRT__COPY +#undef FSRT__SWAP +#undef FSRT__INSR +#undef FSRT__THREE +#undef FSRT__NINE +#undef FSRT__EXTRA_ARGS +#undef FSRT__EXTRA_DECL +#undef FSRT__MED3 +// #undef FSRT__RAND +#undef FSRT__SORT + +#undef FSRT_NAME +#undef FSRT_NAM2 +#undef FSRT_LT +#undef FSRT_EQ +#undef FSRT_COMP +#undef FSRT_TYPE +#undef FSRT_COPY +#undef FSRT_UNIQ +#undef FSRT_INSR +#undef FSRT_THREE +#undef FSRT_NINE +#undef FSRT_EXTRA_ARGS +#undef FSRT_EXTRA_DECL + +//////////////////////////////////////////////////////////////////////////////// +// +// fsrt.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ends here +// +// 0.90 2008-11-20 mschoene initial version +// +// 0.95 2009-03-24 mschoene file is now called fsrt.h +// changed prefix to FSRT_ +// parameters and local variables begin with fs_ +// changed INSR default to 8 +// added description for split-end partitioning +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\)" +// End: diff --git a/Makefile.am b/Makefile.am index fa770d315c..3005d3019b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,5 +57,4 @@ avocdb_SOURCES = \ VocBase/simple-collection.c \ VocBase/vocbase.c \ VocBase/voc-shaper.c \ - VocBase/write-slots.c \ # end of file list diff --git a/V8/v8-json.cpp b/V8/v8-json.cpp index d29bba2e3e..ff0130174f 100644 --- a/V8/v8-json.cpp +++ b/V8/v8-json.cpp @@ -36,7 +36,7 @@ /* %endif */ /* %if-c-only */ - + /* %endif */ /* %if-c-only */ @@ -68,7 +68,7 @@ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, - * if you want the limit (max/min) macros for int types. + * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 @@ -85,7 +85,7 @@ typedef uint32_t flex_uint32_t; typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; -typedef unsigned char flex_uint8_t; +typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; #endif /* ! C99 */ @@ -131,15 +131,15 @@ typedef unsigned int flex_uint32_t; /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST -#else /* ! __cplusplus */ +#else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST -#endif /* defined (__STDC__) */ -#endif /* ! __cplusplus */ +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const @@ -235,20 +235,20 @@ typedef struct yy_buffer_state *YY_BUFFER_STATE; #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) - + /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ - do \ - { \ - /* Undo effects of setting up yytext. */ \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - *yy_cp = yyg->yy_hold_char; \ - YY_RESTORE_YY_MORE_OFFSET \ - yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ - YY_DO_BEFORE_ACTION; /* set up yytext again */ \ - } \ - while ( 0 ) + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) @@ -260,71 +260,71 @@ typedef size_t yy_size_t; #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state - { + { /* %if-c-only */ - FILE *yy_input_file; + FILE *yy_input_file; /* %endif */ /* %if-c++-only */ /* %endif */ - char *yy_ch_buf; /* input buffer */ - char *yy_buf_pos; /* current position in input buffer */ + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ - /* Size of input buffer in bytes, not including room for EOB - * characters. - */ - yy_size_t yy_buf_size; + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; - /* Number of characters read into yy_ch_buf, not including EOB - * characters. - */ - int yy_n_chars; + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; - /* Whether we "own" the buffer - i.e., we know we created it, - * and can realloc() it to grow it, and should free() it to - * delete it. - */ - int yy_is_our_buffer; + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; - /* Whether this is an "interactive" input source; if so, and - * if we're using stdio for input, then we want to use getc() - * instead of fread(), to make sure we stop fetching input after - * each newline. - */ - int yy_is_interactive; + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; - /* Whether we're considered to be at the beginning of a line. - * If so, '^' rules will be active on the next match, otherwise - * not. - */ - int yy_at_bol; + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; - /* Whether to try to fill the input buffer when we reach the - * end of it. - */ - int yy_fill_buffer; - - int yy_buffer_status; + int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 - /* When an EOF's been seen but there's still some text to process - * then we mark the buffer as YY_EOF_PENDING, to indicate that we - * shouldn't try reading from the input source any more. We might - * still have a bunch of tokens to match, though, because of - * possible backing-up. - * - * When we actually see the EOF, we change the status to "new" - * (via tri_v8_restart()), so that the user can continue scanning by - * just pointing yyin at a new input file. - */ + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via tri_v8_restart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ #define YY_BUFFER_EOF_PENDING 2 - }; + }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* %if-c-only Standard (non-C++) definition */ @@ -387,24 +387,24 @@ void tri_v8_free (void * ,yyscan_t yyscanner ); #define yy_new_buffer tri_v8__create_buffer #define yy_set_interactive(is_interactive) \ - { \ - if ( ! YY_CURRENT_BUFFER ){ \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ tri_v8_ensure_buffer_stack (yyscanner); \ - YY_CURRENT_BUFFER_LVALUE = \ + YY_CURRENT_BUFFER_LVALUE = \ tri_v8__create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ - } \ - YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ - } + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } #define yy_set_bol(at_bol) \ - { \ - if ( ! YY_CURRENT_BUFFER ){\ + { \ + if ( ! YY_CURRENT_BUFFER ){\ tri_v8_ensure_buffer_stack (yyscanner); \ - YY_CURRENT_BUFFER_LVALUE = \ + YY_CURRENT_BUFFER_LVALUE = \ tri_v8__create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ - } \ - YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ - } + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) @@ -435,13 +435,13 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ - yyg->yytext_ptr = yy_bp; \ + yyg->yytext_ptr = yy_bp; \ /* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\ - yyleng = (size_t) (yy_cp - yy_bp); \ - yyg->yy_hold_char = *yy_cp; \ - *yy_cp = '\0'; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ /* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\ - yyg->yy_c_buf_p = yy_cp; + yyg->yy_c_buf_p = yy_cp; /* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */ #define YY_NUM_RULES 14 @@ -449,10 +449,10 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info - { - flex_int32_t yy_verify; - flex_int32_t yy_nxt; - }; + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; static yyconst flex_int16_t yy_accept[42] = { 0, 12, 12, 15, 13, 12, 12, 13, 13, 10, 5, @@ -759,33 +759,33 @@ static int input (yyscan_t yyscanner ); #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ /* %% [5.0] fread()/read() definition of YY_INPUT goes here unless we're doing C++ \ */\ - if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ - { \ - int c = '*'; \ - int n; \ - for ( n = 0; n < max_size && \ - (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ - buf[n] = (char) c; \ - if ( c == '\n' ) \ - buf[n++] = (char) c; \ - if ( c == EOF && ferror( yyin ) ) \ - YY_FATAL_ERROR( "input in flex scanner failed" ); \ - result = n; \ - } \ - else \ - { \ - errno=0; \ - while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ - { \ - if( errno != EINTR) \ - { \ - YY_FATAL_ERROR( "input in flex scanner failed" ); \ - break; \ - } \ - errno=0; \ - clearerr(yyin); \ - } \ - }\ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ \ /* %if-c++-only C++ definition \ */\ /* %endif */ @@ -856,7 +856,7 @@ extern int tri_v8_lex (yyscan_t yyscanner); /* %% [6.0] YY_RULE_SETUP definition goes here */ #define YY_RULE_SETUP \ - YY_USER_ACTION + YY_USER_ACTION /* %not-for-header */ @@ -864,9 +864,9 @@ extern int tri_v8_lex (yyscan_t yyscanner); */ YY_DECL { - register yy_state_type yy_current_state; - register char *yy_cp, *yy_bp; - register int yy_act; + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* %% [7.0] user's declarations go here */ @@ -875,132 +875,132 @@ YY_DECL * keywords * ----------------------------------------------------------------------------- */ - if ( !yyg->yy_init ) - { - yyg->yy_init = 1; + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; #ifdef YY_USER_INIT - YY_USER_INIT; + YY_USER_INIT; #endif - if ( ! yyg->yy_start ) - yyg->yy_start = 1; /* first start state */ + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ - if ( ! yyin ) + if ( ! yyin ) /* %if-c-only */ - yyin = stdin; + yyin = stdin; /* %endif */ /* %if-c++-only */ /* %endif */ - if ( ! yyout ) + if ( ! yyout ) /* %if-c-only */ - yyout = stdout; + yyout = stdout; /* %endif */ /* %if-c++-only */ /* %endif */ - if ( ! YY_CURRENT_BUFFER ) { - tri_v8_ensure_buffer_stack (yyscanner); - YY_CURRENT_BUFFER_LVALUE = - tri_v8__create_buffer(yyin,YY_BUF_SIZE ,yyscanner); - } + if ( ! YY_CURRENT_BUFFER ) { + tri_v8_ensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + tri_v8__create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } - tri_v8__load_buffer_state(yyscanner ); - } + tri_v8__load_buffer_state(yyscanner ); + } - while ( 1 ) /* loops until end-of-file is reached */ - { + while ( 1 ) /* loops until end-of-file is reached */ + { /* %% [8.0] yymore()-related code goes here */ - yy_cp = yyg->yy_c_buf_p; + yy_cp = yyg->yy_c_buf_p; - /* Support of yytext. */ - *yy_cp = yyg->yy_hold_char; + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; - /* yy_bp points to the position in yy_ch_buf of the start of - * the current run. - */ - yy_bp = yy_cp; + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; /* %% [9.0] code to set up and find next match goes here */ - yy_current_state = yyg->yy_start; + yy_current_state = yyg->yy_start; yy_match: - do - { - register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; - if ( yy_accept[yy_current_state] ) - { - yyg->yy_last_accepting_state = yy_current_state; - yyg->yy_last_accepting_cpos = yy_cp; - } - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 42 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - ++yy_cp; - } - while ( yy_current_state != 41 ); - yy_cp = yyg->yy_last_accepting_cpos; - yy_current_state = yyg->yy_last_accepting_state; + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 42 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_current_state != 41 ); + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; yy_find_action: /* %% [10.0] code to find the action number goes here */ - yy_act = yy_accept[yy_current_state]; + yy_act = yy_accept[yy_current_state]; - YY_DO_BEFORE_ACTION; + YY_DO_BEFORE_ACTION; /* %% [11.0] code for yylineno update goes here */ -do_action: /* This label is used only to access EOF actions. */ +do_action: /* This label is used only to access EOF actions. */ /* %% [12.0] debug code goes here */ - if ( yy_flex_debug ) - { - if ( yy_act == 0 ) - fprintf( stderr, "--scanner backing up\n" ); - else if ( yy_act < 14 ) - fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n", - (long)yy_rule_linenum[yy_act], yytext ); - else if ( yy_act == 14 ) - fprintf( stderr, "--accepting default rule (\"%s\")\n", - yytext ); - else if ( yy_act == 15 ) - fprintf( stderr, "--(end of buffer or a NUL)\n" ); - else - fprintf( stderr, "--EOF (start condition %d)\n", YY_START ); - } + if ( yy_flex_debug ) + { + if ( yy_act == 0 ) + fprintf( stderr, "--scanner backing up\n" ); + else if ( yy_act < 14 ) + fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n", + (long)yy_rule_linenum[yy_act], yytext ); + else if ( yy_act == 14 ) + fprintf( stderr, "--accepting default rule (\"%s\")\n", + yytext ); + else if ( yy_act == 15 ) + fprintf( stderr, "--(end of buffer or a NUL)\n" ); + else + fprintf( stderr, "--EOF (start condition %d)\n", YY_START ); + } - switch ( yy_act ) - { /* beginning of action switch */ + switch ( yy_act ) + { /* beginning of action switch */ /* %% [13.0] actions go here */ - case 0: /* must back up */ - /* undo the effects of YY_DO_BEFORE_ACTION */ - *yy_cp = yyg->yy_hold_char; - yy_cp = yyg->yy_last_accepting_cpos; - yy_current_state = yyg->yy_last_accepting_state; - goto yy_find_action; + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; case 1: YY_RULE_SETUP { return FALSE_CONSTANT; } - YY_BREAK + YY_BREAK case 2: YY_RULE_SETUP { return NULL_CONSTANT; } - YY_BREAK + YY_BREAK case 3: YY_RULE_SETUP { return TRUE_CONSTANT; } - YY_BREAK + YY_BREAK /* ----------------------------------------------------------------------------- * strings * ----------------------------------------------------------------------------- */ @@ -1010,7 +1010,7 @@ YY_RULE_SETUP { return STRING_CONSTANT; } - YY_BREAK + YY_BREAK /* ----------------------------------------------------------------------------- * numbers * ----------------------------------------------------------------------------- */ @@ -1019,46 +1019,46 @@ YY_RULE_SETUP { return NUMBER_CONSTANT; } - YY_BREAK + YY_BREAK /* ----------------------------------------------------------------------------- * special characters * ----------------------------------------------------------------------------- */ case 6: YY_RULE_SETUP { - return OPEN_BRACE; + return OPEN_BRACE; } - YY_BREAK + YY_BREAK case 7: YY_RULE_SETUP { - return CLOSE_BRACE; + return CLOSE_BRACE; } - YY_BREAK + YY_BREAK case 8: YY_RULE_SETUP { - return OPEN_BRACKET; + return OPEN_BRACKET; } - YY_BREAK + YY_BREAK case 9: YY_RULE_SETUP { return CLOSE_BRACKET; } - YY_BREAK + YY_BREAK case 10: YY_RULE_SETUP { - return COMMA; + return COMMA; } - YY_BREAK + YY_BREAK case 11: YY_RULE_SETUP { - return COLON; + return COLON; } - YY_BREAK + YY_BREAK /* ----------------------------------------------------------------------------- * Skip whitespaces. Whatever is left, should be an unquoted string appearing * somewhere. This will be reported as an error. @@ -1068,149 +1068,149 @@ case 12: YY_RULE_SETUP { } - YY_BREAK + YY_BREAK case 13: YY_RULE_SETUP { return UNQUOTED_STRING; -} - YY_BREAK +} + YY_BREAK case 14: YY_RULE_SETUP ECHO; - YY_BREAK + YY_BREAK case YY_STATE_EOF(INITIAL): - yyterminate(); + yyterminate(); - case YY_END_OF_BUFFER: - { - /* Amount of text matched not including the EOB char. */ - int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; - /* Undo the effects of YY_DO_BEFORE_ACTION. */ - *yy_cp = yyg->yy_hold_char; - YY_RESTORE_YY_MORE_OFFSET + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET - if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) - { - /* We're scanning a new file or input source. It's - * possible that this happened because the user - * just pointed yyin at a new source and called - * tri_v8_lex(). If so, then we have to assure - * consistency between YY_CURRENT_BUFFER and our - * globals. Here is the right place to do so, because - * this is the first action (other than possibly a - * back-up) that will match for the new input source. - */ - yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; - YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; - } + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * tri_v8_lex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } - /* Note that here we test for yy_c_buf_p "<=" to the position - * of the first EOB in the buffer, since yy_c_buf_p will - * already have been incremented past the NUL character - * (since all states make transitions on EOB to the - * end-of-buffer state). Contrast this with the test - * in input(). - */ - if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) - { /* This was really a NUL. */ - yy_state_type yy_next_state; + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; - yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( yyscanner ); + yy_current_state = yy_get_previous_state( yyscanner ); - /* Okay, we're now positioned to make the NUL - * transition. We couldn't have - * yy_get_previous_state() go ahead and do it - * for us because it doesn't know how to deal - * with the possibility of jamming (and we don't - * want to build jamming into it because then it - * will run more slowly). - */ + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ - yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); - yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; - if ( yy_next_state ) - { - /* Consume the NUL. */ - yy_cp = ++yyg->yy_c_buf_p; - yy_current_state = yy_next_state; - goto yy_match; - } + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } - else - { + else + { /* %% [14.0] code to do back-up for compressed tables and set up yy_cp goes here */ - yy_cp = yyg->yy_last_accepting_cpos; - yy_current_state = yyg->yy_last_accepting_state; - goto yy_find_action; - } - } + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + } + } - else switch ( yy_get_next_buffer( yyscanner ) ) - { - case EOB_ACT_END_OF_FILE: - { - yyg->yy_did_buffer_switch_on_eof = 0; + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; - if ( tri_v8_wrap(yyscanner ) ) - { - /* Note: because we've taken care in - * yy_get_next_buffer() to have set up - * yytext, we can now set up - * yy_c_buf_p so that if some total - * hoser (like flex itself) wants to - * call the scanner after we return the - * YY_NULL, it'll still work - another - * YY_NULL will get returned. - */ - yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + if ( tri_v8_wrap(yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; - yy_act = YY_STATE_EOF(YY_START); - goto do_action; - } + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } - else - { - if ( ! yyg->yy_did_buffer_switch_on_eof ) - YY_NEW_FILE; - } - break; - } + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } - case EOB_ACT_CONTINUE_SCAN: - yyg->yy_c_buf_p = - yyg->yytext_ptr + yy_amount_of_matched_text; + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; - yy_current_state = yy_get_previous_state( yyscanner ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = yyg->yy_c_buf_p; - yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; - goto yy_match; + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; - case EOB_ACT_LAST_MATCH: - yyg->yy_c_buf_p = - &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; - yy_current_state = yy_get_previous_state( yyscanner ); + yy_current_state = yy_get_previous_state( yyscanner ); - yy_cp = yyg->yy_c_buf_p; - yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; - goto yy_find_action; - } - break; - } + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } - default: - YY_FATAL_ERROR( - "fatal flex scanner internal error--no action found" ); - } /* end of action switch */ - } /* end of scanning one token */ + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ } /* end of tri_v8_lex */ /* %ok-for-header */ @@ -1224,9 +1224,9 @@ case YY_STATE_EOF(INITIAL): /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: - * EOB_ACT_LAST_MATCH - - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position - * EOB_ACT_END_OF_FILE - end of file + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file */ /* %if-c-only */ static int yy_get_next_buffer (yyscan_t yyscanner) @@ -1235,134 +1235,134 @@ static int yy_get_next_buffer (yyscan_t yyscanner) /* %endif */ { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; - register char *source = yyg->yytext_ptr; - register int number_to_move, i; - int ret_val; + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = yyg->yytext_ptr; + register int number_to_move, i; + int ret_val; - if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) - YY_FATAL_ERROR( - "fatal flex scanner internal error--end of buffer missed" ); + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); - if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) - { /* Don't try to fill the buffer, so this is an EOF. */ - if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) - { - /* We matched a single character, the EOB, so - * treat this as a final EOF. - */ - return EOB_ACT_END_OF_FILE; - } + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } - else - { - /* We matched some text prior to the EOB, first - * process it. - */ - return EOB_ACT_LAST_MATCH; - } - } + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } - /* Try to read more data. */ + /* Try to read more data. */ - /* First move last chars to start of buffer. */ - number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; - for ( i = 0; i < number_to_move; ++i ) - *(dest++) = *(source++); + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); - if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) - /* don't do the read, it's not guaranteed to return an EOF, - * just force an EOF - */ - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; - else - { - int num_to_read = - YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; - while ( num_to_read <= 0 ) - { /* Not enough room in the buffer - grow it. */ + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ - /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; - int yy_c_buf_p_offset = - (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); - if ( b->yy_is_our_buffer ) - { - int new_size = b->yy_buf_size * 2; + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; - if ( new_size <= 0 ) - b->yy_buf_size += b->yy_buf_size / 8; - else - b->yy_buf_size *= 2; + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; - b->yy_ch_buf = (char *) - /* Include room in for 2 EOB chars. */ - tri_v8_realloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); - } - else - /* Can't grow it, we don't own it. */ - b->yy_ch_buf = 0; + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + tri_v8_realloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; - if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( - "fatal error - scanner input buffer overflow" ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); - yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; - num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - - number_to_move - 1; + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; - } + } - if ( num_to_read > YY_READ_BUF_SIZE ) - num_to_read = YY_READ_BUF_SIZE; + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; - /* Read in more data. */ - YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), - yyg->yy_n_chars, (size_t) num_to_read ); + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, (size_t) num_to_read ); - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; - } + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } - if ( yyg->yy_n_chars == 0 ) - { - if ( number_to_move == YY_MORE_ADJ ) - { - ret_val = EOB_ACT_END_OF_FILE; - tri_v8_restart(yyin ,yyscanner); - } + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + tri_v8_restart(yyin ,yyscanner); + } - else - { - ret_val = EOB_ACT_LAST_MATCH; - YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = - YY_BUFFER_EOF_PENDING; - } - } + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } - else - ret_val = EOB_ACT_CONTINUE_SCAN; + else + ret_val = EOB_ACT_CONTINUE_SCAN; - if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { - /* Extend the array by 50%, plus the number we really need. */ - yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) tri_v8_realloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); - if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); - } + if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) tri_v8_realloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } - yyg->yy_n_chars += number_to_move; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; - yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; - return ret_val; + return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ @@ -1375,38 +1375,38 @@ static int yy_get_next_buffer (yyscan_t yyscanner) /* %if-c++-only */ /* %endif */ { - register yy_state_type yy_current_state; - register char *yy_cp; + register yy_state_type yy_current_state; + register char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* %% [15.0] code to get the start state into yy_current_state goes here */ - yy_current_state = yyg->yy_start; + yy_current_state = yyg->yy_start; - for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) - { + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { /* %% [16.0] code to find the next state goes here */ - register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); - if ( yy_accept[yy_current_state] ) - { - yyg->yy_last_accepting_state = yy_current_state; - yyg->yy_last_accepting_cpos = yy_cp; - } - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 42 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - } + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 42 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } - return yy_current_state; + return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis - * next_state = yy_try_NUL_trans( current_state ); + * next_state = yy_try_NUL_trans( current_state ); */ /* %if-c-only */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) @@ -1414,27 +1414,27 @@ static int yy_get_next_buffer (yyscan_t yyscanner) /* %if-c++-only */ /* %endif */ { - register int yy_is_jam; + register int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ /* %% [17.0] code to find the next state, and perhaps do backing up, goes here */ - register char *yy_cp = yyg->yy_c_buf_p; + register char *yy_cp = yyg->yy_c_buf_p; - register YY_CHAR yy_c = 1; - if ( yy_accept[yy_current_state] ) - { - yyg->yy_last_accepting_state = yy_current_state; - yyg->yy_last_accepting_cpos = yy_cp; - } - while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) - { - yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 42 ) - yy_c = yy_meta[(unsigned int) yy_c]; - } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - yy_is_jam = (yy_current_state == 41); + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 42 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 41); - return yy_is_jam ? 0 : yy_current_state; + return yy_is_jam ? 0 : yy_current_state; } /* %if-c-only */ @@ -1453,75 +1453,75 @@ static int yy_get_next_buffer (yyscan_t yyscanner) /* %if-c++-only */ /* %endif */ { - int c; + int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - *yyg->yy_c_buf_p = yyg->yy_hold_char; + *yyg->yy_c_buf_p = yyg->yy_hold_char; - if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) - { - /* yy_c_buf_p now points to the character we want to return. - * If this occurs *before* the EOB characters, then it's a - * valid NUL; if not, then we've hit the end of the buffer. - */ - if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) - /* This was really a NUL. */ - *yyg->yy_c_buf_p = '\0'; + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; - else - { /* need more input */ - int offset = yyg->yy_c_buf_p - yyg->yytext_ptr; - ++yyg->yy_c_buf_p; + else + { /* need more input */ + int offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; - switch ( yy_get_next_buffer( yyscanner ) ) - { - case EOB_ACT_LAST_MATCH: - /* This happens because yy_g_n_b() - * sees that we've accumulated a - * token and flags that we need to - * try matching the token before - * proceeding. But for input(), - * there's no matching to consider. - * So convert the EOB_ACT_LAST_MATCH - * to EOB_ACT_END_OF_FILE. - */ + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ - /* Reset buffer status. */ - tri_v8_restart(yyin ,yyscanner); + /* Reset buffer status. */ + tri_v8_restart(yyin ,yyscanner); - /*FALLTHROUGH*/ + /*FALLTHROUGH*/ - case EOB_ACT_END_OF_FILE: - { - if ( tri_v8_wrap(yyscanner ) ) - return EOF; + case EOB_ACT_END_OF_FILE: + { + if ( tri_v8_wrap(yyscanner ) ) + return EOF; - if ( ! yyg->yy_did_buffer_switch_on_eof ) - YY_NEW_FILE; + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; #ifdef __cplusplus - return yyinput(yyscanner); + return yyinput(yyscanner); #else - return input(yyscanner); + return input(yyscanner); #endif - } + } - case EOB_ACT_CONTINUE_SCAN: - yyg->yy_c_buf_p = yyg->yytext_ptr + offset; - break; - } - } - } + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } - c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ - *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ - yyg->yy_hold_char = *++yyg->yy_c_buf_p; + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; /* %% [19.0] update BOL and yylineno */ - return c; + return c; } /* %if-c-only */ -#endif /* ifndef YY_NO_INPUT */ +#endif /* ifndef YY_NO_INPUT */ /* %endif */ /** Immediately switch to a different input stream. @@ -1537,14 +1537,14 @@ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - if ( ! YY_CURRENT_BUFFER ){ + if ( ! YY_CURRENT_BUFFER ){ tri_v8_ensure_buffer_stack (yyscanner); - YY_CURRENT_BUFFER_LVALUE = + YY_CURRENT_BUFFER_LVALUE = tri_v8__create_buffer(yyin,YY_BUF_SIZE ,yyscanner); - } + } - tri_v8__init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); - tri_v8__load_buffer_state(yyscanner ); + tri_v8__init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); + tri_v8__load_buffer_state(yyscanner ); } /** Switch to a different input buffer. @@ -1559,32 +1559,32 @@ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - /* TODO. We should be able to replace this entire function body - * with - * tri_v8_pop_buffer_state(); - * tri_v8_push_buffer_state(new_buffer); + /* TODO. We should be able to replace this entire function body + * with + * tri_v8_pop_buffer_state(); + * tri_v8_push_buffer_state(new_buffer); */ - tri_v8_ensure_buffer_stack (yyscanner); - if ( YY_CURRENT_BUFFER == new_buffer ) - return; + tri_v8_ensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; - if ( YY_CURRENT_BUFFER ) - { - /* Flush out information for old buffer. */ - *yyg->yy_c_buf_p = yyg->yy_hold_char; - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; - } + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } - YY_CURRENT_BUFFER_LVALUE = new_buffer; - tri_v8__load_buffer_state(yyscanner ); + YY_CURRENT_BUFFER_LVALUE = new_buffer; + tri_v8__load_buffer_state(yyscanner ); - /* We don't actually know whether we did this switch during - * EOF (tri_v8_wrap()) processing, but the only time this flag - * is looked at is after tri_v8_wrap() is called, so it's safe - * to go ahead and always set it. - */ - yyg->yy_did_buffer_switch_on_eof = 1; + /* We don't actually know whether we did this switch during + * EOF (tri_v8_wrap()) processing, but the only time this flag + * is looked at is after tri_v8_wrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; } /* %if-c-only */ @@ -1594,10 +1594,10 @@ static void tri_v8__load_buffer_state (yyscan_t yyscanner) /* %endif */ { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; - yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; - yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; - yyg->yy_hold_char = *yyg->yy_c_buf_p; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. @@ -1612,26 +1612,26 @@ static void tri_v8__load_buffer_state (yyscan_t yyscanner) /* %if-c++-only */ /* %endif */ { - YY_BUFFER_STATE b; + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) tri_v8_alloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in tri_v8__create_buffer()" ); - b = (YY_BUFFER_STATE) tri_v8_alloc(sizeof( struct yy_buffer_state ) ,yyscanner ); - if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in tri_v8__create_buffer()" ); + b->yy_buf_size = size; - b->yy_buf_size = size; + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) tri_v8_alloc(b->yy_buf_size + 2 ,yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in tri_v8__create_buffer()" ); - /* yy_ch_buf has to be 2 characters longer than the size given because - * we need to put in 2 end-of-buffer characters. - */ - b->yy_ch_buf = (char *) tri_v8_alloc(b->yy_buf_size + 2 ,yyscanner ); - if ( ! b->yy_ch_buf ) - YY_FATAL_ERROR( "out of dynamic memory in tri_v8__create_buffer()" ); + b->yy_is_our_buffer = 1; - b->yy_is_our_buffer = 1; + tri_v8__init_buffer(b,file ,yyscanner); - tri_v8__init_buffer(b,file ,yyscanner); - - return b; + return b; } /** Destroy the buffer. @@ -1646,16 +1646,16 @@ static void tri_v8__load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - if ( ! b ) - return; + if ( ! b ) + return; - if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ - YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; - if ( b->yy_is_our_buffer ) - tri_v8_free((void *) b->yy_ch_buf ,yyscanner ); + if ( b->yy_is_our_buffer ) + tri_v8_free((void *) b->yy_ch_buf ,yyscanner ); - tri_v8_free((void *) b ,yyscanner ); + tri_v8_free((void *) b ,yyscanner ); } /* %if-c-only */ @@ -1663,7 +1663,7 @@ static void tri_v8__load_buffer_state (yyscan_t yyscanner) #ifndef __cplusplus extern int isatty (int ); #endif /* __cplusplus */ - + /* %endif */ /* %if-c++-only */ @@ -1680,13 +1680,13 @@ extern int isatty (int ); /* %endif */ { - int oerrno = errno; + int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - tri_v8__flush_buffer(b ,yyscanner); + tri_v8__flush_buffer(b ,yyscanner); - b->yy_input_file = file; - b->yy_fill_buffer = 1; + b->yy_input_file = file; + b->yy_fill_buffer = 1; /* If b is the current buffer, then tri_v8__init_buffer was _probably_ * called from tri_v8_restart() or through yy_get_next_buffer. @@ -1700,11 +1700,11 @@ extern int isatty (int ); /* %if-c-only */ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; - + /* %endif */ /* %if-c++-only */ /* %endif */ - errno = oerrno; + errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. @@ -1718,25 +1718,25 @@ extern int isatty (int ); /* %endif */ { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - if ( ! b ) - return; + if ( ! b ) + return; - b->yy_n_chars = 0; + b->yy_n_chars = 0; - /* We always need two end-of-buffer characters. The first causes - * a transition to the end-of-buffer state. The second causes - * a jam in that state. - */ - b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; - b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; - b->yy_buf_pos = &b->yy_ch_buf[0]; + b->yy_buf_pos = &b->yy_ch_buf[0]; - b->yy_at_bol = 1; - b->yy_buffer_status = YY_BUFFER_NEW; + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; - if ( b == YY_CURRENT_BUFFER ) - tri_v8__load_buffer_state(yyscanner ); + if ( b == YY_CURRENT_BUFFER ) + tri_v8__load_buffer_state(yyscanner ); } /* %if-c-or-c++ */ @@ -1753,28 +1753,28 @@ void tri_v8_push_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) /* %endif */ { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - if (new_buffer == NULL) - return; + if (new_buffer == NULL) + return; - tri_v8_ensure_buffer_stack(yyscanner); + tri_v8_ensure_buffer_stack(yyscanner); - /* This block is copied from tri_v8__switch_to_buffer. */ - if ( YY_CURRENT_BUFFER ) - { - /* Flush out information for old buffer. */ - *yyg->yy_c_buf_p = yyg->yy_hold_char; - YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; - YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; - } + /* This block is copied from tri_v8__switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } - /* Only push if top exists. Otherwise, replace top. */ - if (YY_CURRENT_BUFFER) - yyg->yy_buffer_stack_top++; - YY_CURRENT_BUFFER_LVALUE = new_buffer; + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; - /* copied from tri_v8__switch_to_buffer. */ - tri_v8__load_buffer_state(yyscanner ); - yyg->yy_did_buffer_switch_on_eof = 1; + /* copied from tri_v8__switch_to_buffer. */ + tri_v8__load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; } /* %endif */ @@ -1790,18 +1790,18 @@ void tri_v8_pop_buffer_state (yyscan_t yyscanner) /* %endif */ { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - if (!YY_CURRENT_BUFFER) - return; + if (!YY_CURRENT_BUFFER) + return; - tri_v8__delete_buffer(YY_CURRENT_BUFFER ,yyscanner); - YY_CURRENT_BUFFER_LVALUE = NULL; - if (yyg->yy_buffer_stack_top > 0) - --yyg->yy_buffer_stack_top; + tri_v8__delete_buffer(YY_CURRENT_BUFFER ,yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; - if (YY_CURRENT_BUFFER) { - tri_v8__load_buffer_state(yyscanner ); - yyg->yy_did_buffer_switch_on_eof = 1; - } + if (YY_CURRENT_BUFFER) { + tri_v8__load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } } /* %endif */ @@ -1815,46 +1815,46 @@ static void tri_v8_ensure_buffer_stack (yyscan_t yyscanner) /* %if-c++-only */ /* %endif */ { - int num_to_alloc; + int num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - if (!yyg->yy_buffer_stack) { + if (!yyg->yy_buffer_stack) { - /* First allocation is just for 2 elements, since we don't know if this - * scanner will even need a stack. We use 2 instead of 1 to avoid an - * immediate realloc on the next call. + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. */ - num_to_alloc = 1; - yyg->yy_buffer_stack = (struct yy_buffer_state**)tri_v8_alloc - (num_to_alloc * sizeof(struct yy_buffer_state*) - , yyscanner); - if ( ! yyg->yy_buffer_stack ) - YY_FATAL_ERROR( "out of dynamic memory in tri_v8_ensure_buffer_stack()" ); + num_to_alloc = 1; + yyg->yy_buffer_stack = (struct yy_buffer_state**)tri_v8_alloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in tri_v8_ensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } - memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ - yyg->yy_buffer_stack_max = num_to_alloc; - yyg->yy_buffer_stack_top = 0; - return; - } + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; - if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)tri_v8_realloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in tri_v8_ensure_buffer_stack()" ); - /* Increase the buffer to prepare for a possible push. */ - int grow_size = 8 /* arbitrary grow size */; - - num_to_alloc = yyg->yy_buffer_stack_max + grow_size; - yyg->yy_buffer_stack = (struct yy_buffer_state**)tri_v8_realloc - (yyg->yy_buffer_stack, - num_to_alloc * sizeof(struct yy_buffer_state*) - , yyscanner); - if ( ! yyg->yy_buffer_stack ) - YY_FATAL_ERROR( "out of dynamic memory in tri_v8_ensure_buffer_stack()" ); - - /* zero only the new slots.*/ - memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); - yyg->yy_buffer_stack_max = num_to_alloc; - } + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } } /* %endif */ @@ -1863,35 +1863,35 @@ static void tri_v8_ensure_buffer_stack (yyscan_t yyscanner) * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. - * @return the newly allocated buffer state object. + * @return the newly allocated buffer state object. */ YY_BUFFER_STATE tri_v8__scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { - YY_BUFFER_STATE b; + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; - if ( size < 2 || - base[size-2] != YY_END_OF_BUFFER_CHAR || - base[size-1] != YY_END_OF_BUFFER_CHAR ) - /* They forgot to leave room for the EOB's. */ - return 0; + b = (YY_BUFFER_STATE) tri_v8_alloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in tri_v8__scan_buffer()" ); - b = (YY_BUFFER_STATE) tri_v8_alloc(sizeof( struct yy_buffer_state ) ,yyscanner ); - if ( ! b ) - YY_FATAL_ERROR( "out of dynamic memory in tri_v8__scan_buffer()" ); + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; - b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ - b->yy_buf_pos = b->yy_ch_buf = base; - b->yy_is_our_buffer = 0; - b->yy_input_file = 0; - b->yy_n_chars = b->yy_buf_size; - b->yy_is_interactive = 0; - b->yy_at_bol = 1; - b->yy_fill_buffer = 0; - b->yy_buffer_status = YY_BUFFER_NEW; + tri_v8__switch_to_buffer(b ,yyscanner ); - tri_v8__switch_to_buffer(b ,yyscanner ); - - return b; + return b; } /* %endif */ @@ -1906,8 +1906,8 @@ YY_BUFFER_STATE tri_v8__scan_buffer (char * base, yy_size_t size , yyscan_t yy */ YY_BUFFER_STATE tri_v8__scan_string (yyconst char * yystr , yyscan_t yyscanner) { - - return tri_v8__scan_bytes(yystr,strlen(yystr) ,yyscanner); + + return tri_v8__scan_bytes(yystr,strlen(yystr) ,yyscanner); } /* %endif */ @@ -1921,32 +1921,32 @@ YY_BUFFER_STATE tri_v8__scan_string (yyconst char * yystr , yyscan_t yyscanner) */ YY_BUFFER_STATE tri_v8__scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner) { - YY_BUFFER_STATE b; - char *buf; - yy_size_t n; - int i; + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) tri_v8_alloc(n ,yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in tri_v8__scan_bytes()" ); - /* Get memory for full buffer, including space for trailing EOB's. */ - n = _yybytes_len + 2; - buf = (char *) tri_v8_alloc(n ,yyscanner ); - if ( ! buf ) - YY_FATAL_ERROR( "out of dynamic memory in tri_v8__scan_bytes()" ); + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; - for ( i = 0; i < _yybytes_len; ++i ) - buf[i] = yybytes[i]; + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; - buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + b = tri_v8__scan_buffer(buf,n ,yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in tri_v8__scan_bytes()" ); - b = tri_v8__scan_buffer(buf,n ,yyscanner); - if ( ! b ) - YY_FATAL_ERROR( "bad buffer in tri_v8__scan_bytes()" ); + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; - /* It's okay to grow etc. this buffer, and we should throw it - * away when we're done. - */ - b->yy_is_our_buffer = 1; - - return b; + return b; } /* %endif */ @@ -1957,8 +1957,8 @@ YY_BUFFER_STATE tri_v8__scan_bytes (yyconst char * yybytes, int _yybytes_len , /* %if-c-only */ static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { - (void) fprintf( stderr, "%s\n", msg ); - exit( YY_EXIT_FAILURE ); + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); } /* %endif */ /* %if-c++-only */ @@ -1968,18 +1968,18 @@ static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) #undef yyless #define yyless(n) \ - do \ - { \ - /* Undo effects of setting up yytext. */ \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ - yytext[yyleng] = yyg->yy_hold_char; \ - yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ - yyg->yy_hold_char = *yyg->yy_c_buf_p; \ - *yyg->yy_c_buf_p = '\0'; \ - yyleng = yyless_macro_arg; \ - } \ - while ( 0 ) + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ @@ -2003,10 +2003,10 @@ YY_EXTRA_TYPE tri_v8_get_extra (yyscan_t yyscanner) int tri_v8_get_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - + if (! YY_CURRENT_BUFFER) return 0; - + return yylineno; } @@ -2016,10 +2016,10 @@ int tri_v8_get_lineno (yyscan_t yyscanner) int tri_v8_get_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - + if (! YY_CURRENT_BUFFER) return 0; - + return yycolumn; } @@ -2084,8 +2084,8 @@ void tri_v8_set_lineno (int line_number , yyscan_t yyscanner) /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) - yy_fatal_error( "tri_v8_set_lineno called with no buffer" , yyscanner); - + yy_fatal_error( "tri_v8_set_lineno called with no buffer" , yyscanner); + yylineno = line_number; } @@ -2099,8 +2099,8 @@ void tri_v8_set_column (int column_no , yyscan_t yyscanner) /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) - yy_fatal_error( "tri_v8_set_column called with no buffer" , yyscanner); - + yy_fatal_error( "tri_v8_set_column called with no buffer" , yyscanner); + yycolumn = column_no; } @@ -2189,20 +2189,20 @@ int tri_v8_lex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals errno = EINVAL; return 1; } - + *ptr_yy_globals = (yyscan_t) tri_v8_alloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); - + if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } - + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); - + tri_v8_set_extra (yy_user_defined, *ptr_yy_globals); - + return yy_init_globals ( *ptr_yy_globals ); } @@ -2250,15 +2250,15 @@ int tri_v8_lex_destroy (yyscan_t yyscanner) struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ - while(YY_CURRENT_BUFFER){ - tri_v8__delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); - YY_CURRENT_BUFFER_LVALUE = NULL; - tri_v8_pop_buffer_state(yyscanner); - } + while(YY_CURRENT_BUFFER){ + tri_v8__delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + tri_v8_pop_buffer_state(yyscanner); + } - /* Destroy the stack itself. */ - tri_v8_free(yyg->yy_buffer_stack ,yyscanner); - yyg->yy_buffer_stack = NULL; + /* Destroy the stack itself. */ + tri_v8_free(yyg->yy_buffer_stack ,yyscanner); + yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ tri_v8_free(yyg->yy_start_stack ,yyscanner ); @@ -2284,43 +2284,43 @@ int tri_v8_lex_destroy (yyscan_t yyscanner) #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) { - register int i; - for ( i = 0; i < n; ++i ) - s1[i] = s2[i]; + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) { - register int n; - for ( n = 0; s[n]; ++n ) - ; + register int n; + for ( n = 0; s[n]; ++n ) + ; - return n; + return n; } #endif void *tri_v8_alloc (yy_size_t size , yyscan_t yyscanner) { - return (void *) malloc( size ); + return (void *) malloc( size ); } void *tri_v8_realloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { - /* The cast to (char *) in the following accommodates both - * implementations that use char* generic pointers, and those - * that use void* generic pointers. It works with the latter - * because both ANSI C and C++ allow castless assignment from - * any pointer type to void*, and deal with argument conversions - * as though doing an assignment. - */ - return (void *) realloc( (char *) ptr, size ); + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); } void tri_v8_free (void * ptr , yyscan_t yyscanner) { - free( (char *) ptr ); /* see tri_v8_realloc() for (char *) cast */ + free( (char *) ptr ); /* see tri_v8_realloc() for (char *) cast */ } /* %if-tables-serialization definitions */ diff --git a/V8/v8-json.ll b/V8/v8-json.ll index 4757072a0e..89b2ea5c77 100644 --- a/V8/v8-json.ll +++ b/V8/v8-json.ll @@ -195,7 +195,7 @@ static v8::Handle ParseArray (yyscan_t scanner) { v8::Handle array = v8::Object::New(); bool comma = false; - char const* name; + char* name; char const* ptr; size_t len; size_t outLength; @@ -270,7 +270,7 @@ static v8::Handle ParseObject (yyscan_t scanner, int c) { char buffer[1024]; char* ep; - char const* ptr; + char* ptr; double d; size_t outLength; diff --git a/V8/v8-shell.cpp b/V8/v8-shell.cpp index 50a0b86025..42026df395 100644 --- a/V8/v8-shell.cpp +++ b/V8/v8-shell.cpp @@ -28,10 +28,13 @@ #include "v8-shell.h" #define TRI_WITHIN_C +#include #include #include #include #include + +#include "ShapedJson/shaped-json.h" #undef TRI_WITHIN_C #include @@ -40,6 +43,1312 @@ using namespace std; +// ----------------------------------------------------------------------------- +// --SECTION-- forward declarations +// ----------------------------------------------------------------------------- + +static bool FillShapeValueJson (TRI_shaper_t* shaper, + TRI_shape_value_t* dst, + v8::Handle json); + +static v8::Handle JsonShapeData (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size); + +// ----------------------------------------------------------------------------- +// --SECTION-- JAVASCRIPT KEY NAMES +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private variables +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup V8ShellPrivate V8 Shell (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief "did" key name +//////////////////////////////////////////////////////////////////////////////// + +static v8::Persistent DidKey; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- CONVERSION FUNCTIONS +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup V8ShellPrivate V8 Shell (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a null into TRI_shape_value_t +//////////////////////////////////////////////////////////////////////////////// + +static bool FillShapeValueNull (TRI_shaper_t* shaper, TRI_shape_value_t* dst) { + dst->_type = TRI_SHAPE_NULL; + dst->_sid = shaper->_sidNull; + dst->_fixedSized = true; + dst->_size = 0; + dst->_value = 0; + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a boolean into TRI_shape_value_t +//////////////////////////////////////////////////////////////////////////////// + +static bool FillShapeValueBoolean (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { + TRI_shape_boolean_t* ptr; + + dst->_type = TRI_SHAPE_BOOLEAN; + dst->_sid = shaper->_sidBoolean; + dst->_fixedSized = true; + dst->_size = sizeof(TRI_shape_boolean_t); + dst->_value = (char*)(ptr = (TRI_shape_boolean_t*) TRI_Allocate(dst->_size)); + + *ptr = json->Value() ? 1 : 0; + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a boolean into TRI_shape_value_t +//////////////////////////////////////////////////////////////////////////////// + +static bool FillShapeValueBoolean (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { + TRI_shape_boolean_t* ptr; + + dst->_type = TRI_SHAPE_BOOLEAN; + dst->_sid = shaper->_sidBoolean; + dst->_fixedSized = true; + dst->_size = sizeof(TRI_shape_boolean_t); + dst->_value = (char*)(ptr = (TRI_shape_boolean_t*) TRI_Allocate(dst->_size)); + + *ptr = json->BooleanValue() ? 1 : 0; + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a number into TRI_shape_value_t +//////////////////////////////////////////////////////////////////////////////// + +static bool FillShapeValueNumber (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { + TRI_shape_number_t* ptr; + + dst->_type = TRI_SHAPE_NUMBER; + dst->_sid = shaper->_sidNumber; + dst->_fixedSized = true; + dst->_size = sizeof(TRI_shape_number_t); + dst->_value = (char*)(ptr = (TRI_shape_number_t*) TRI_Allocate(dst->_size)); + + *ptr = json->Value(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a number into TRI_shape_value_t +//////////////////////////////////////////////////////////////////////////////// + +static bool FillShapeValueNumber (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { + TRI_shape_number_t* ptr; + + dst->_type = TRI_SHAPE_NUMBER; + dst->_sid = shaper->_sidNumber; + dst->_fixedSized = true; + dst->_size = sizeof(TRI_shape_number_t); + dst->_value = (char*)(ptr = (TRI_shape_number_t*) TRI_Allocate(dst->_size)); + + *ptr = json->NumberValue(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a string into TRI_shape_value_t +//////////////////////////////////////////////////////////////////////////////// + +static bool FillShapeValueString (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { + char* ptr; + + v8::String::Utf8Value str(json); + + if (*str == 0) { + dst->_type = TRI_SHAPE_SHORT_STRING; + dst->_sid = shaper->_sidShortString; + dst->_fixedSized = true; + dst->_size = sizeof(TRI_shape_length_short_string_t) + TRI_SHAPE_SHORT_STRING_CUT; + dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); + + * ((TRI_shape_length_short_string_t*) ptr) = 1; + * (ptr + sizeof(TRI_shape_length_short_string_t)) = '\0'; + } + else if (str.length() < TRI_SHAPE_SHORT_STRING_CUT) { // includes '\0' + size_t size = str.length() + 1; + + dst->_type = TRI_SHAPE_SHORT_STRING; + dst->_sid = shaper->_sidShortString; + dst->_fixedSized = true; + dst->_size = sizeof(TRI_shape_length_short_string_t) + TRI_SHAPE_SHORT_STRING_CUT; + dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); + + * ((TRI_shape_length_short_string_t*) ptr) = size; + + memcpy(ptr + sizeof(TRI_shape_length_short_string_t), *str, size); + } + else { + size_t size = str.length() + 1; + + dst->_type = TRI_SHAPE_LONG_STRING; + dst->_sid = shaper->_sidLongString; + dst->_fixedSized = false; + dst->_size = sizeof(TRI_shape_length_long_string_t) + size; + dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); + + * ((TRI_shape_length_long_string_t*) ptr) = size; + + memcpy(ptr + sizeof(TRI_shape_length_long_string_t), *str, size); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a json list into TRI_shape_value_t +//////////////////////////////////////////////////////////////////////////////// + +static bool FillShapeValueList (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { + size_t i; + size_t n; + size_t total; + + TRI_shape_value_t* values; + TRI_shape_value_t* p; + TRI_shape_value_t* e; + + bool hs; + bool hl; + + TRI_shape_sid_t s; + TRI_shape_sid_t l; + + TRI_shape_sid_t* sids; + TRI_shape_size_t* offsets; + TRI_shape_size_t offset; + + TRI_shape_t const* found; + + char* ptr; + + // check for special case "empty list" + n = json->Length(); + + if (n == 0) { + dst->_type = TRI_SHAPE_LIST; + dst->_sid = shaper->_sidList; + + dst->_fixedSized = false; + dst->_size = sizeof(TRI_shape_length_list_t); + dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); + + * (TRI_shape_length_list_t*) ptr = 0; + + return true; + } + + // convert into TRI_shape_value_t array + p = (values = (TRI_shape_value_t*) TRI_Allocate(sizeof(TRI_shape_value_t) * n)); + memset(values, 0, sizeof(TRI_shape_value_t) * n); + + total = 0; + e = values + n; + + for (i = 0; i < n; ++i, ++p) { + v8::Local el = json->Get(i); + bool ok = FillShapeValueJson(shaper, p, el); + + if (! ok) { + for (e = p, p = values; p < e; ++p) { + if (p->_value != 0) { + TRI_Free(p->_value); + } + } + + TRI_Free(values); + + return false; + } + + total += p->_size; + } + + // check if this list is homoegenous + hs = true; + hl = true; + + s = values[0]._sid; + l = values[0]._size; + p = values; + + for (; p < e; ++p) { + if (p->_sid != s) { + hs = false; + break; + } + + if (p->_size != l) { + hl = false; + } + } + + // homogeneous sized + if (hs && hl) { + TRI_homogeneous_sized_list_shape_t* shape; + + shape = (TRI_homogeneous_sized_list_shape_t*) TRI_Allocate(sizeof(TRI_homogeneous_sized_list_shape_t)); + + shape->base._size = sizeof(TRI_homogeneous_sized_list_shape_t); + shape->base._type = TRI_SHAPE_HOMOGENEOUS_SIZED_LIST; + shape->base._dataSize = TRI_SHAPE_SIZE_VARIABLE; + shape->_sidEntry = s; + shape->_sizeEntry = l; + + found = shaper->findShape(shaper, &shape->base); + + if (found == 0) { + for (p = values; p < e; ++p) { + if (p->_value != 0) { + TRI_Free(p->_value); + } + } + + TRI_Free(values); + TRI_Free(shape); + + return false; + } + + dst->_type = found->_type; + dst->_sid = found->_sid; + + dst->_fixedSized = false; + dst->_size = sizeof(TRI_shape_length_list_t) + total; + dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); + + // copy sub-objects into data space + * (TRI_shape_length_list_t*) ptr = n; + ptr += sizeof(TRI_shape_length_list_t); + + for (p = values; p < e; ++p) { + memcpy(ptr, p->_value, p->_size); + ptr += p->_size; + } + } + + // homogeneous + else if (hs) { + TRI_homogeneous_list_shape_t* shape; + + shape = (TRI_homogeneous_list_shape_t*) TRI_Allocate(sizeof(TRI_homogeneous_list_shape_t)); + + shape->base._size = sizeof(TRI_homogeneous_list_shape_t); + shape->base._type = TRI_SHAPE_HOMOGENEOUS_LIST; + shape->base._dataSize = TRI_SHAPE_SIZE_VARIABLE; + shape->_sidEntry = s; + + found = shaper->findShape(shaper, &shape->base); + + if (found == 0) { + for (p = values; p < e; ++p) { + if (p->_value != 0) { + TRI_Free(p->_value); + } + } + + TRI_Free(values); + TRI_Free(shape); + + return false; + } + + dst->_type = found->_type; + dst->_sid = found->_sid; + + offset = sizeof(TRI_shape_length_list_t) + (n + 1) * sizeof(TRI_shape_size_t); + + dst->_fixedSized = false; + dst->_size = offset + total; + dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); + + // copy sub-objects into data space + * (TRI_shape_length_list_t*) ptr = n; + ptr += sizeof(TRI_shape_length_list_t); + + offsets = (TRI_shape_size_t*) ptr; + ptr += (n + 1) * sizeof(TRI_shape_size_t); + + for (p = values; p < e; ++p) { + *offsets++ = offset; + offset += p->_size; + + memcpy(ptr, p->_value, p->_size); + ptr += p->_size; + } + + *offsets = offset; + } + + // in-homogeneous + else { + dst->_type = TRI_SHAPE_LIST; + dst->_sid = shaper->_sidList; + + offset = + sizeof(TRI_shape_length_list_t) + + n * sizeof(TRI_shape_sid_t) + + (n + 1) * sizeof(TRI_shape_size_t); + + dst->_fixedSized = false; + dst->_size = offset + total; + dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); + + // copy sub-objects into data space + * (TRI_shape_length_list_t*) ptr = n; + ptr += sizeof(TRI_shape_length_list_t); + + sids = (TRI_shape_sid_t*) ptr; + ptr += n * sizeof(TRI_shape_sid_t); + + offsets = (TRI_shape_size_t*) ptr; + ptr += (n + 1) * sizeof(TRI_shape_size_t); + + for (p = values; p < e; ++p) { + *sids++ = p->_sid; + + *offsets++ = offset; + offset += p->_size; + + memcpy(ptr, p->_value, p->_size); + ptr += p->_size; + } + + *offsets = offset; + } + + // free TRI_shape_value_t array + for (p = values; p < e; ++p) { + if (p->_value != 0) { + TRI_Free(p->_value); + } + } + + TRI_Free(values); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a json array into TRI_shape_value_t +//////////////////////////////////////////////////////////////////////////////// + +static bool FillShapeValueArray (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { + size_t n; + size_t i; + size_t total; + + size_t f; + size_t v; + + TRI_shape_value_t* values; + TRI_shape_value_t* p; + TRI_shape_value_t* e; + + TRI_array_shape_t* a; + + TRI_shape_sid_t* sids; + TRI_shape_aid_t* aids; + TRI_shape_size_t* offsetsF; + TRI_shape_size_t* offsetsV; + TRI_shape_size_t offset; + + TRI_shape_t const* found; + + char* ptr; + + // number of attributes + v8::Handle names = json->GetPropertyNames(); + n = names->Length(); + + // convert into TRI_shape_value_t array + p = (values = (TRI_shape_value_t*) TRI_Allocate(n * sizeof(TRI_shape_value_t))); + memset(values, 0, n * sizeof(TRI_shape_value_t)); + + total = 0; + f = 0; + v = 0; + + for (i = 0; i < n; ++i, ++p) { + v8::Handle key = names->Get(i); + v8::Handle val = json->Get(key); + + // first find an identifier for the name + v8::String::Utf8Value keyStr(key); + + if (*keyStr == 0) { + --p; + continue; + } + + if (TRI_EqualString(*keyStr, "_id")) { + --p; + continue; + } + + p->_aid = shaper->findAttributeName(shaper, *keyStr); + + // convert value + bool ok; + + if (p->_aid == 0) { + ok = false; + } + else { + ok = FillShapeValueJson(shaper, p, val); + } + + if (! ok) { + for (e = p, p = values; p < e; ++p) { + if (p->_value != 0) { + TRI_Free(p->_value); + } + } + + TRI_Free(values); + return false; + } + + total += p->_size; + + // count fixed and variable sized values + if (p->_fixedSized) { + ++f; + } + else { + ++v; + } + } + + n = f + v; + + // add variable offset table size + total += (v + 1) * sizeof(TRI_shape_size_t); + + // now sort the shape entries + TRI_SortShapeValues(values, n); + +#ifdef DEBUG_JSON_SHAPER + printf("shape values\n------------\ntotal: %u, fixed: %u, variable: %u\n", + (unsigned int) n, + (unsigned int) f, + (unsigned int) v); + PrintShapeValues(values, n); + printf("\n"); +#endif + + // generate shape structure + i = + sizeof(TRI_array_shape_t) + + n * sizeof(TRI_shape_sid_t) + + n * sizeof(TRI_shape_aid_t) + + (f + 1) * sizeof(TRI_shape_size_t); + + a = (TRI_array_shape_t*) (ptr = (char*) TRI_Allocate(i)); + memset(ptr, 0, i); + + a->base._type = TRI_SHAPE_ARRAY; + a->base._size = i; + a->base._dataSize = (v == 0) ? total : TRI_SHAPE_SIZE_VARIABLE; + + a->_fixedEntries = f; + a->_variableEntries = v; + + ptr += sizeof(TRI_array_shape_t); + + // array of shape identifiers + sids = (TRI_shape_sid_t*) ptr; + ptr += n * sizeof(TRI_shape_sid_t); + + // array of attribute identifiers + aids = (TRI_shape_aid_t*) ptr; + ptr += n * sizeof(TRI_shape_aid_t); + + // array of offsets for fixed part (within the shape) + offset = (v + 1) * sizeof(TRI_shape_size_t); + offsetsF = (TRI_shape_size_t*) ptr; + + // fill destination (except sid) + dst->_type = TRI_SHAPE_ARRAY; + + dst->_fixedSized = true; + dst->_size = total; + dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); + + // array of offsets for variable part (within the value) + offsetsV = (TRI_shape_size_t*) ptr; + ptr += (v + 1) * sizeof(TRI_shape_size_t); + + // and fill in attributes + e = values + n; + + for (p = values; p < e; ++p) { + *aids++ = p->_aid; + *sids++ = p->_sid; + + memcpy(ptr, p->_value, p->_size); + ptr += p->_size; + + dst->_fixedSized &= p->_fixedSized; + + if (p->_fixedSized) { + *offsetsF++ = offset; + offset += p->_size; + *offsetsF = offset; + } + else { + *offsetsV++ = offset; + offset += p->_size; + *offsetsV = offset; + } + } + + // free TRI_shape_value_t array + for (p = values; p < e; ++p) { + if (p->_value != 0) { + TRI_Free(p->_value); + } + } + + TRI_Free(values); + + // lookup this shape + found = shaper->findShape(shaper, &a->base); + + if (found == 0) { + TRI_Free(a); + return false; + } + + // and finally add the sid + dst->_sid = found->_sid; + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a json object into TRI_shape_value_t +//////////////////////////////////////////////////////////////////////////////// + +static bool FillShapeValueJson (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { + if (json->IsNull()) { + return FillShapeValueNull(shaper, dst); + } + + if (json->IsBoolean()) { + return FillShapeValueBoolean(shaper, dst, json->ToBoolean()); + } + + if (json->IsBooleanObject()) { + v8::Handle bo = v8::Handle::Cast(json); + return FillShapeValueBoolean(shaper, dst, bo); + } + + if (json->IsNumber()) { + return FillShapeValueNumber(shaper, dst, json->ToNumber()); + } + + if (json->IsNumberObject()) { + v8::Handle no = v8::Handle::Cast(json); + return FillShapeValueNumber(shaper, dst, no); + } + + if (json->IsString()) { + return FillShapeValueString(shaper, dst, json->ToString()); + } + + if (json->IsStringObject()) { + v8::Handle so = v8::Handle::Cast(json); + return FillShapeValueString(shaper, dst, so->StringValue()); + } + + if (json->IsArray()) { + v8::Handle array = v8::Handle::Cast(json); + return FillShapeValueList(shaper, dst, array); + } + + if (json->IsObject()) { + return FillShapeValueArray(shaper, dst, json->ToObject()); + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a data null blob into a json object +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JsonShapeDataNull (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size) { + return v8::Null(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a data boolean blob into a json object +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JsonShapeDataBoolean (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size) { + bool v; + + v = (* (TRI_shape_boolean_t const*) data) != 0; + + return v ? v8::True() : v8::False(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a data number blob into a json object +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JsonShapeDataNumber (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size) { + TRI_shape_number_t v; + + v = * (TRI_shape_number_t const*) data; + + return v8::Number::New(v); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a data short string blob into a json object +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JsonShapeDataShortString (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size) { + TRI_shape_length_short_string_t l; + + l = * (TRI_shape_length_short_string_t const*) data; + data += sizeof(TRI_shape_length_short_string_t); + + return v8::String::New(data, l - 1); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a data long string blob into a json object +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JsonShapeDataLongString (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size) { + TRI_shape_length_long_string_t l; + + l = * (TRI_shape_length_long_string_t const*) data; + data += sizeof(TRI_shape_length_long_string_t); + + return v8::String::New(data, l - 1); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a data array blob into a json object +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JsonShapeDataArray (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size) { + TRI_array_shape_t const* s; + TRI_shape_aid_t const* aids; + TRI_shape_sid_t const* sids; + TRI_shape_size_t const* offsetsF; + TRI_shape_size_t const* offsetsV; + TRI_shape_size_t f; + TRI_shape_size_t i; + TRI_shape_size_t n; + TRI_shape_size_t v; + char const* qtr; + + v8::Handle array; + + s = (TRI_array_shape_t const*) shape; + f = s->_fixedEntries; + v = s->_variableEntries; + n = f + v; + + qtr = (char const*) shape; + array = v8::Object::New(); + + qtr += sizeof(TRI_array_shape_t); + + sids = (TRI_shape_sid_t const*) qtr; + qtr += n * sizeof(TRI_shape_sid_t); + + aids = (TRI_shape_aid_t const*) qtr; + qtr += n * sizeof(TRI_shape_aid_t); + + offsetsF = (TRI_shape_size_t const*) qtr; + + for (i = 0; i < f; ++i, ++sids, ++aids, ++offsetsF) { + TRI_shape_sid_t sid = *sids; + TRI_shape_aid_t aid = *aids; + TRI_shape_size_t offset; + TRI_shape_t const* subshape; + char const* name; + v8::Handle element; + + offset = *offsetsF; + subshape = shaper->lookupShapeId(shaper, sid); + name = shaper->lookupAttributeId(shaper, aid); + + if (subshape == 0) { + LOG_WARNING("cannot find shape #%u", (unsigned int) sid); + continue; + } + + if (name == 0) { + LOG_WARNING("cannot find attribute #%u", (unsigned int) aid); + continue; + } + + element = JsonShapeData(shaper, subshape, data + offset, offsetsF[1] - offset); + + array->Set(v8::String::New(name), element); + } + + offsetsV = (TRI_shape_size_t const*) data; + + for (i = 0; i < v; ++i, ++sids, ++aids, ++offsetsV) { + TRI_shape_sid_t sid = *sids; + TRI_shape_aid_t aid = *aids; + TRI_shape_size_t offset; + TRI_shape_t const* subshape; + char const* name; + v8::Handle element; + + offset = *offsetsV; + subshape = shaper->lookupShapeId(shaper, sid); + name = shaper->lookupAttributeId(shaper, aid); + + if (subshape == 0) { + LOG_WARNING("cannot find shape #%u", (unsigned int) sid); + continue; + } + + if (name == 0) { + LOG_WARNING("cannot find attribute #%u", (unsigned int) aid); + continue; + } + + element = JsonShapeData(shaper, subshape, data + offset, offsetsV[1] - offset); + + array->Set(v8::String::New(name), element); + } + + return array; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a data list blob into a json object +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JsonShapeDataList (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size) { + TRI_shape_length_list_t i; + TRI_shape_length_list_t l; + TRI_shape_sid_t const* sids; + TRI_shape_size_t const* offsets; + char const* ptr; + v8::Handle list; + + list = v8::Array::New(); + + ptr = data; + l = * (TRI_shape_length_list_t const*) ptr; + + ptr += sizeof(TRI_shape_length_list_t); + sids = (TRI_shape_sid_t const*) ptr; + + ptr += l * sizeof(TRI_shape_sid_t); + offsets = (TRI_shape_size_t const*) ptr; + + for (i = 0; i < l; ++i, ++sids, ++offsets) { + TRI_shape_sid_t sid = *sids; + TRI_shape_size_t offset; + TRI_shape_t const* subshape; + v8::Handle element; + + offset = *offsets; + subshape = shaper->lookupShapeId(shaper, sid); + + if (subshape == 0) { + LOG_WARNING("cannot find shape #%u", (unsigned int) sid); + continue; + } + + element = JsonShapeData(shaper, subshape, data + offset, offsets[1] - offset); + + list->Set(i, element); + } + + return list; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a data homogeneous list blob into a json object +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JsonShapeDataHomogeneousList (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size) { + TRI_homogeneous_list_shape_t const* s; + TRI_shape_length_list_t i; + TRI_shape_length_list_t l; + TRI_shape_sid_t sid; + TRI_shape_size_t const* offsets; + char const* ptr; + v8::Handle list; + + list = v8::Array::New(); + + s = (TRI_homogeneous_list_shape_t const*) shape; + sid = s->_sidEntry; + + ptr = data; + l = * (TRI_shape_length_list_t const*) ptr; + + ptr += sizeof(TRI_shape_length_list_t); + offsets = (TRI_shape_size_t const*) ptr; + + for (i = 0; i < l; ++i, ++offsets) { + TRI_shape_size_t offset; + TRI_shape_t const* subshape; + v8::Handle element; + + offset = *offsets; + subshape = shaper->lookupShapeId(shaper, sid); + + if (subshape == 0) { + LOG_WARNING("cannot find shape #%u", (unsigned int) sid); + continue; + } + + element = JsonShapeData(shaper, subshape, data + offset, offsets[1] - offset); + + list->Set(i, element); + } + + return list; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a data homogeneous sized list blob into a json object +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JsonShapeDataHomogeneousSizedList (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size) { + TRI_homogeneous_sized_list_shape_t const* s; + TRI_shape_length_list_t i; + TRI_shape_length_list_t l; + TRI_shape_sid_t sid; + TRI_shape_size_t length; + TRI_shape_size_t offset; + char const* ptr; + v8::Handle list; + + list = v8::Array::New(); + + s = (TRI_homogeneous_sized_list_shape_t const*) shape; + sid = s->_sidEntry; + + ptr = data; + l = * (TRI_shape_length_list_t const*) ptr; + + length = s->_sizeEntry; + offset = sizeof(TRI_shape_length_list_t); + + for (i = 0; i < l; ++i, offset += length) { + TRI_shape_t const* subshape; + v8::Handle element; + + subshape = shaper->lookupShapeId(shaper, sid); + + if (subshape == 0) { + LOG_WARNING("cannot find shape #%u", (unsigned int) sid); + continue; + } + + element = JsonShapeData(shaper, subshape, data + offset, length); + + list->Set(i, element); + } + + return list; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a data blob into a json object +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JsonShapeData (TRI_shaper_t* shaper, + TRI_shape_t const* shape, + char const* data, + size_t size) { + if (shape == 0) { + return v8::Null(); + } + + switch (shape->_type) { + case TRI_SHAPE_NULL: + return JsonShapeDataNull(shaper, shape, data, size); + + case TRI_SHAPE_BOOLEAN: + return JsonShapeDataBoolean(shaper, shape, data, size); + + case TRI_SHAPE_NUMBER: + return JsonShapeDataNumber(shaper, shape, data, size); + + case TRI_SHAPE_SHORT_STRING: + return JsonShapeDataShortString(shaper, shape, data, size); + + case TRI_SHAPE_LONG_STRING: + return JsonShapeDataLongString(shaper, shape, data, size); + + case TRI_SHAPE_ARRAY: + return JsonShapeDataArray(shaper, shape, data, size); + + case TRI_SHAPE_LIST: + return JsonShapeDataList(shaper, shape, data, size); + + case TRI_SHAPE_HOMOGENEOUS_LIST: + return JsonShapeDataHomogeneousList(shaper, shape, data, size); + + case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: + return JsonShapeDataHomogeneousSizedList(shaper, shape, data, size); + } + + return v8::Null(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup V8Shell V8 Shell +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_json_t NULL into a V8 object +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ObjectJsonNull (TRI_json_t const* json) { + return v8::Null(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_json_t BOOLEAN into a V8 object +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ObjectJsonBoolean (TRI_json_t const* json) { + return json->_value._boolean ? v8::True() : v8::False(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_json_t NUMBER into a V8 object +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ObjectJsonNumber (TRI_json_t const* json) { + return v8::Number::New(json->_value._number); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_json_t NUMBER into a V8 object +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ObjectJsonString (TRI_json_t const* json) { + return v8::String::New(json->_value._string.data); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_json_t ARRAY into a V8 object +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ObjectJsonArray (TRI_json_t const* json) { + v8::Handle object = v8::Object::New(); + + size_t n = TRI_SizeVector(&json->_value._objects); + + for (size_t i = 0; i < n; i += 2) { + TRI_json_t* key = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i); + + if (key->_type != TRI_JSON_STRING) { + continue; + } + + TRI_json_t* j = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i + 1); + v8::Handle val = ObjectJson(j); + + object->Set(v8::String::New(key->_value._string.data), val); + } + + return object; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_json_t LIST into a V8 object +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ObjectJsonList (TRI_json_t const* json) { + v8::Handle object = v8::Array::New(); + + size_t n = TRI_SizeVector(&json->_value._objects); + + for (size_t i = 0; i < n; ++i) { + TRI_json_t* j = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i); + v8::Handle val = ObjectJson(j); + + object->Set(i, val); + } + + return object; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_json_t into a V8 object +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ObjectJson (TRI_json_t const* json) { + v8::HandleScope scope; + + switch (json->_type) { + case TRI_JSON_NULL: + return scope.Close(ObjectJsonNull(json)); + + case TRI_JSON_BOOLEAN: + return scope.Close(ObjectJsonBoolean(json)); + + case TRI_JSON_NUMBER: + return scope.Close(ObjectJsonNumber(json)); + + case TRI_JSON_STRING: + return scope.Close(ObjectJsonString(json)); + + case TRI_JSON_ARRAY: + return scope.Close(ObjectJsonArray(json)); + + case TRI_JSON_LIST: + return scope.Close(ObjectJsonList(json)); + } + + return scope.Close(v8::Undefined()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_shaped_json_t into a V8 object +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ObjectShapedJson (TRI_doc_collection_t* collection, + TRI_voc_did_t did, + TRI_shaper_t* shaper, + TRI_shaped_json_t const* shaped) { + TRI_shape_t const* shape; + + shape = shaper->lookupShapeId(shaper, shaped->_sid); + + if (shape == 0) { + LOG_WARNING("cannot find shape #%u", (unsigned int) shaped->_sid); + return v8::Null(); + } + + v8::Handle result = JsonShapeData(shaper, shape, shaped->_data.data, shaped->_data.length); + + if (result->IsObject()) { + char* cidStr = TRI_StringUInt64(collection->base._cid); + char* didStr = TRI_StringUInt64(did); + + string name = cidStr + string(":") + didStr; + + TRI_FreeString(didStr); + TRI_FreeString(cidStr); + + result->ToObject()->Set(DidKey, v8::String::New(name.c_str())); + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_result_set_t into a V8 array +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ArrayResultSet (TRI_result_set_t* rs) { + v8::Handle array = v8::Array::New(); + + TRI_doc_collection_t* collection = rs->_container->_collection; + TRI_shaper_t* shaper = collection->_shaper; + + size_t pos; + + for (pos = 0; rs->hasNext(rs); ++pos) { + TRI_voc_did_t did; + TRI_json_t const* augmented; + TRI_shaped_json_t* element = rs->next(rs, &did, &augmented); + + v8::Handle object = ObjectShapedJson(collection, did, shaper, element); + + if (augmented != NULL) { + AugmentObject(object, augmented); + } + + array->Set(pos, object); + } + + return array; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to a TRI_shaped_json_t +//////////////////////////////////////////////////////////////////////////////// + +TRI_shaped_json_t* ShapedJsonV8Object (v8::Handle object, TRI_shaper_t* shaper) { + TRI_shape_value_t dst; + bool ok = FillShapeValueJson(shaper, &dst, object); + + if (! ok) { + return 0; + } + + + TRI_shaped_json_t* shaped = (TRI_shaped_json_t*) TRI_Allocate(sizeof(TRI_shaped_json_t)); + + shaped->_sid = dst._sid; + shaped->_data.length = dst._size; + shaped->_data.data = dst._value; + + return shaped; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to a string +//////////////////////////////////////////////////////////////////////////////// + +string ObjectToString (v8::Handle value) { + v8::String::Utf8Value utf8Value(value); + + if (*utf8Value == 0) { + return ""; + } + else { + return string(*utf8Value, utf8Value.length()); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to a character +//////////////////////////////////////////////////////////////////////////////// + +char ObjectToCharacter (v8::Handle value, bool& error) { + error = false; + + if (! value->IsString() && ! value->IsStringObject()) { + error = true; + return '\0'; + } + + v8::String::Utf8Value sep(value->ToString()); + + if (*sep == 0 || sep.length() != 1) { + error = true; + return '\0'; + } + + return (*sep)[0]; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to a double +//////////////////////////////////////////////////////////////////////////////// + +double ObjectToDouble (v8::Handle value) { + if (value->IsNumber()) { + return value->ToNumber()->Value(); + } + + if (value->IsNumberObject()) { + v8::Handle no = v8::Handle::Cast(value); + return no->NumberValue(); + } + + return 0.0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to a double +//////////////////////////////////////////////////////////////////////////////// + +double ObjectToDouble (v8::Handle value, bool& error) { + error = false; + + if (value->IsNumber()) { + return value->ToNumber()->Value(); + } + + if (value->IsNumberObject()) { + v8::Handle no = v8::Handle::Cast(value); + return no->NumberValue(); + } + + error = true; + + return 0.0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- IMPORT / EXPORT +// ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- @@ -98,6 +1407,187 @@ static void ProcessCsvEnd (TRI_csv_parser_t* parser, char const* field, size_t r /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief processes a CSV file +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle ProcessCsvFileCall (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2) { + return scope.Close(v8::ThrowException(v8::String::New("usage: processCsvFile(, [, ])"))); + } + + // extract the filename + v8::String::Utf8Value filename(argv[0]); + + if (*filename == 0) { + return scope.Close(v8::ThrowException(v8::String::New(" must be an UTF8 filename"))); + } + + // extract the callback + v8::Handle cb = v8::Handle::Cast(argv[1]); + + // extract the options + v8::Handle separatorKey = v8::String::New("separator"); + v8::Handle quoteKey = v8::String::New("quote"); + + char separator = ','; + char quote = '"'; + + if (3 <= argv.Length()) { + v8::Handle options = argv[2]->ToObject(); + bool error; + + // separator + if (options->Has(separatorKey)) { + separator = ObjectToCharacter(options->Get(separatorKey), error); + + if (error) { + return scope.Close(v8::ThrowException(v8::String::New(".separator must be a character"))); + } + } + + // quote + if (options->Has(quoteKey)) { + quote = ObjectToCharacter(options->Get(quoteKey), error); + + if (error) { + return scope.Close(v8::ThrowException(v8::String::New(".quote must be a character"))); + } + } + } + + // read and convert + int fd = open(*filename, O_RDONLY); + + if (fd < 0) { + return scope.Close(v8::ThrowException(v8::String::New(TRI_LAST_ERROR_STR))); + } + + TRI_csv_parser_t parser; + + TRI_InitCsvParser(&parser, + ProcessCsvBegin, + ProcessCsvAdd, + ProcessCsvEnd); + + parser._separator = separator; + parser._quote = quote; + + parser._dataEnd = &cb; + + v8::Handle array; + parser._dataBegin = &array; + + char buffer[10240]; + + while (true) { + v8::HandleScope scope; + + ssize_t n = read(fd, buffer, sizeof(buffer)); + + if (n < 0) { + TRI_DestroyCsvParser(&parser); + return scope.Close(v8::ThrowException(v8::String::New(TRI_LAST_ERROR_STR))); + } + else if (n == 0) { + TRI_DestroyCsvParser(&parser); + break; + } + + TRI_ParseCsvString2(&parser, buffer, n); + } + + return scope.Close(v8::Undefined()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief processes a JSON file +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle ProcessJsonFileCall (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() < 2) { + return scope.Close(v8::ThrowException(v8::String::New("usage: processJsonFile(, )"))); + } + + // extract the filename + v8::String::Utf8Value filename(argv[0]); + + if (*filename == 0) { + return scope.Close(v8::ThrowException(v8::String::New(" must be an UTF8 filename"))); + } + + // extract the callback + v8::Handle cb = v8::Handle::Cast(argv[1]); + + // read and convert + string line; + ifstream file(*filename); + + if (file.is_open()) { + size_t row = 0; + + while (file.good()) { + v8::HandleScope scope; + + getline(file, line); + + char const* ptr = line.c_str(); + char const* end = ptr + line.length(); + + while (ptr < end && (*ptr == ' ' || *ptr == '\t' || *ptr == '\r')) { + ++ptr; + } + + if (ptr == end) { + continue; + } + + char* error; + v8::Handle object = TRI_FromJsonString(line.c_str(), &error); + + if (object->IsUndefined()) { + v8::Handle msg = v8::String::New(error); + TRI_FreeString(error); + return scope.Close(v8::ThrowException(msg)); + } + + v8::Handle r = v8::Number::New(row); + v8::Handle args[] = { object, r }; + cb->Call(cb, 2, args); + + row++; + } + + file.close(); + } + else { + return scope.Close(v8::ThrowException(v8::String::New("cannot open file"))); + } + + return scope.Close(v8::Undefined()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- GENERAL +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- JS functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup V8Shell V8 Shell +/// @{ +//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// /// @brief changes the log-level //////////////////////////////////////////////////////////////////////////////// @@ -274,170 +1764,6 @@ static v8::Handle PrintCall (v8::Arguments const& argv) { return v8::Undefined(); } -//////////////////////////////////////////////////////////////////////////////// -/// @brief processes a csv file -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle ProcessCsvFileCall (v8::Arguments const& argv) { - v8::HandleScope scope; - - if (argv.Length() < 2) { - return scope.Close(v8::ThrowException(v8::String::New("usage: processCsvFile(, [, ])"))); - } - - // extract the filename - v8::String::Utf8Value filename(argv[0]); - - if (*filename == 0) { - return scope.Close(v8::ThrowException(v8::String::New(" must be an UTF8 filename"))); - } - - // extract the callback - v8::Handle cb = v8::Handle::Cast(argv[1]); - - // extract the options - v8::Handle separatorKey = v8::String::New("separator"); - v8::Handle quoteKey = v8::String::New("quote"); - - char separator = ','; - char quote = '"'; - - if (3 <= argv.Length()) { - v8::Handle options = argv[2]->ToObject(); - bool error; - - // separator - if (options->Has(separatorKey)) { - separator = ObjectToCharacter(options->Get(separatorKey), error); - - if (error) { - return scope.Close(v8::ThrowException(v8::String::New(".separator must be a character"))); - } - } - - // quote - if (options->Has(quoteKey)) { - quote = ObjectToCharacter(options->Get(quoteKey), error); - - if (error) { - return scope.Close(v8::ThrowException(v8::String::New(".quote must be a character"))); - } - } - } - - // read and convert - int fd = open(*filename, O_RDONLY); - - if (fd < 0) { - return scope.Close(v8::ThrowException(v8::String::New(TRI_LAST_ERROR_STR))); - } - - TRI_csv_parser_t parser; - - TRI_InitCsvParser(&parser, - ProcessCsvBegin, - ProcessCsvAdd, - ProcessCsvEnd); - - parser._separator = separator; - parser._quote = quote; - - parser._dataEnd = &cb; - - v8::Handle array; - parser._dataBegin = &array; - - char buffer[10240]; - - while (true) { - v8::HandleScope scope; - - ssize_t n = read(fd, buffer, sizeof(buffer)); - - if (n < 0) { - TRI_DestroyCsvParser(&parser); - return scope.Close(v8::ThrowException(v8::String::New(TRI_LAST_ERROR_STR))); - } - else if (n == 0) { - TRI_DestroyCsvParser(&parser); - break; - } - - TRI_ParseCsvString2(&parser, buffer, n); - } - - return scope.Close(v8::Undefined()); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief processes a JSON file -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle ProcessJsonFileCall (v8::Arguments const& argv) { - v8::HandleScope scope; - - if (argv.Length() < 2) { - return scope.Close(v8::ThrowException(v8::String::New("usage: processJsonFile(, )"))); - } - - // extract the filename - v8::String::Utf8Value filename(argv[0]); - - if (*filename == 0) { - return scope.Close(v8::ThrowException(v8::String::New(" must be an UTF8 filename"))); - } - - // extract the callback - v8::Handle cb = v8::Handle::Cast(argv[1]); - - // read and convert - string line; - ifstream file(*filename); - - if (file.is_open()) { - size_t row = 0; - - while (file.good()) { - v8::HandleScope scope; - - getline(file, line); - - char const* ptr = line.c_str(); - char const* end = ptr + line.length(); - - while (ptr < end && (*ptr == ' ' || *ptr == '\t' || *ptr == '\r')) { - ++ptr; - } - - if (ptr == end) { - continue; - } - - char* error; - v8::Handle object = TRI_FromJsonString(line.c_str(), &error); - - if (object->IsUndefined()) { - v8::Handle msg = v8::String::New(error); - TRI_FreeString(error); - return scope.Close(v8::ThrowException(msg)); - } - - v8::Handle r = v8::Number::New(row); - v8::Handle args[] = { object, r }; - cb->Call(cb, 2, args); - - row++; - } - - file.close(); - } - else { - return scope.Close(v8::ThrowException(v8::String::New("cannot open file"))); - } - - return scope.Close(v8::Undefined()); -} - //////////////////////////////////////////////////////////////////////////////// /// @brief returns the current time //////////////////////////////////////////////////////////////////////////////// @@ -462,78 +1788,36 @@ static v8::Handle TimeCall (v8::Arguments const& argv) { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief converts an object to a string +/// @brief adds attributes to array //////////////////////////////////////////////////////////////////////////////// -string ObjectToString (v8::Handle value) { - v8::String::Utf8Value utf8Value(value); +void AugmentObject (v8::Handle value, TRI_json_t const* json) { + v8::HandleScope scope; - if (*utf8Value == 0) { - return ""; - } - else { - return string(*utf8Value, utf8Value.length()); - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts an object to a character -//////////////////////////////////////////////////////////////////////////////// - -char ObjectToCharacter (v8::Handle value, bool& error) { - error = false; - - if (! value->IsString() && ! value->IsStringObject()) { - error = true; - return '\0'; + if (! value->IsObject()) { + return; } - v8::String::Utf8Value sep(value->ToString()); - - if (*sep == 0 || sep.length() != 1) { - error = true; - return '\0'; + if (json->_type != TRI_JSON_ARRAY) { + return; } - return (*sep)[0]; -} + v8::Handle object = value->ToObject(); -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts an object to a double -//////////////////////////////////////////////////////////////////////////////// + size_t n = TRI_SizeVector(&json->_value._objects); -double ObjectToDouble (v8::Handle value) { - if (value->IsNumber()) { - return value->ToNumber()->Value(); + for (size_t i = 0; i < n; i += 2) { + TRI_json_t* key = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i); + + if (key->_type != TRI_JSON_STRING) { + continue; + } + + TRI_json_t* j = (TRI_json_t*) TRI_AtVector(&json->_value._objects, i + 1); + v8::Handle val = ObjectJson(j); + + object->Set(v8::String::New(key->_value._string.data), val); } - - if (value->IsNumberObject()) { - v8::Handle no = v8::Handle::Cast(value); - return no->NumberValue(); - } - - return 0.0; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts an object to a double -//////////////////////////////////////////////////////////////////////////////// - -double ObjectToDouble (v8::Handle value, bool& error) { - error = false; - - if (value->IsNumber()) { - return value->ToNumber()->Value(); - } - - if (value->IsNumberObject()) { - v8::Handle no = v8::Handle::Cast(value); - return no->NumberValue(); - } - - error = true; - - return 0.0; } //////////////////////////////////////////////////////////////////////////////// @@ -542,7 +1826,10 @@ double ObjectToDouble (v8::Handle value, bool& error) { void InitV8Shell (v8::Handle global) { + // ............................................................................. // create the global functions + // ............................................................................. + global->Set(v8::String::New("logLevel"), v8::FunctionTemplate::New(LogLevelCall), v8::ReadOnly); @@ -571,6 +1858,11 @@ void InitV8Shell (v8::Handle global) { v8::FunctionTemplate::New(TimeCall), v8::ReadOnly); + // ............................................................................. + // keys + // ............................................................................. + + DidKey = v8::Persistent::New(v8::String::New("_id")); } //////////////////////////////////////////////////////////////////////////////// diff --git a/V8/v8-shell.h b/V8/v8-shell.h index bf65461688..1e3aa3b573 100644 --- a/V8/v8-shell.h +++ b/V8/v8-shell.h @@ -29,13 +29,19 @@ #define TRIAGENS_DURHAM_V8_V8_SHELL_H 1 #define TRI_WITHIN_C -#include +#include + +#include #undef TRI_WITHIN_C #include #include +// ----------------------------------------------------------------------------- +// --SECTION-- CONVERSION FUNCTIONS +// ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- @@ -46,29 +52,79 @@ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief converts an object to a string +/// @brief converts a TRI_json_t into a V8 object +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ObjectJson (TRI_json_t const*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_shaped_json_t into a V8 object +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ObjectShapedJson (TRI_doc_collection_t* collection, + TRI_voc_did_t did, + TRI_shaper_t* shaper, + TRI_shaped_json_t const* shaped); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_result_set_t into a V8 array +//////////////////////////////////////////////////////////////////////////////// + +v8::Handle ArrayResultSet (TRI_result_set_t* rs); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to a TRI_shaped_json_t +//////////////////////////////////////////////////////////////////////////////// + +TRI_shaped_json_t* ShapedJsonV8Object (v8::Handle object, TRI_shaper_t*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to a string //////////////////////////////////////////////////////////////////////////////// std::string ObjectToString (v8::Handle value); //////////////////////////////////////////////////////////////////////////////// -/// @brief converts an object to a character +/// @brief converts an V8 object to a character //////////////////////////////////////////////////////////////////////////////// char ObjectToCharacter (v8::Handle value, bool& error); //////////////////////////////////////////////////////////////////////////////// -/// @brief converts an object to a double +/// @brief converts an V8 object to a double //////////////////////////////////////////////////////////////////////////////// double ObjectToDouble (v8::Handle value); //////////////////////////////////////////////////////////////////////////////// -/// @brief converts an object to a double +/// @brief converts an V8 object to a double //////////////////////////////////////////////////////////////////////////////// double ObjectToDouble (v8::Handle value, bool& error); +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds attributes to array +//////////////////////////////////////////////////////////////////////////////// + +void AugmentObject (v8::Handle value, TRI_json_t const* json); + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- GENERAL +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup V8Shell V8 Shell +/// @{ +//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// /// @brief stores the V8 shell function inside the global variable //////////////////////////////////////////////////////////////////////////////// diff --git a/V8/v8-vocbase.cpp b/V8/v8-vocbase.cpp index 48928520c2..5632b13766 100644 --- a/V8/v8-vocbase.cpp +++ b/V8/v8-vocbase.cpp @@ -29,9 +29,9 @@ #define TRI_WITHIN_C #include +#include #include #include -#include #include #include @@ -49,19 +49,6 @@ using namespace std; -// ----------------------------------------------------------------------------- -// --SECTION-- forward declarations -// ----------------------------------------------------------------------------- - -static bool FillShapeValueJson (TRI_shaper_t* shaper, - TRI_shape_value_t* dst, - v8::Handle json); - -static v8::Handle JsonShapeData (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size); - // ----------------------------------------------------------------------------- // --SECTION-- private types // ----------------------------------------------------------------------------- @@ -72,76 +59,28 @@ static v8::Handle JsonShapeData (TRI_shaper_t* shaper, //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "type" +/// @brief slot for a "C++ class" //////////////////////////////////////////////////////////////////////////////// -static int SLOT_TYPE = 1; +static int SLOT_CLASS = 0; //////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "operand" +/// @brief slot for a "query" //////////////////////////////////////////////////////////////////////////////// -static int SLOT_OPERAND = 2; +static int SLOT_QUERY = 1; //////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "query" +/// @brief slot for a "result set" //////////////////////////////////////////////////////////////////////////////// -static int SLOT_RESULT_SET = 3; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "document" (shared) -//////////////////////////////////////////////////////////////////////////////// - -static int SLOT_DOCUMENT = 4; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "limit" (shared) -//////////////////////////////////////////////////////////////////////////////// - -static int SLOT_LIMIT = 4; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "skip" (shared) -//////////////////////////////////////////////////////////////////////////////// - -static int SLOT_SKIP = 4; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "latitiude" -//////////////////////////////////////////////////////////////////////////////// - -static int SLOT_LATITUDE = 5; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "longitude" -//////////////////////////////////////////////////////////////////////////////// - -static int SLOT_LONGITUDE = 6; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "locLatitude" (shared) -//////////////////////////////////////////////////////////////////////////////// - -static int SLOT_LOCATION = 7; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "locLatitude" (shared) -//////////////////////////////////////////////////////////////////////////////// - -static int SLOT_LOC_LATITUDE = 7; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief template slot "locLongitude" -//////////////////////////////////////////////////////////////////////////////// - -static int SLOT_LOC_LONGITUDE = 8; +static int SLOT_RESULT_SET = 2; //////////////////////////////////////////////////////////////////////////////// /// @brief end marker //////////////////////////////////////////////////////////////////////////////// -static int SLOT_END = 8; +static int SLOT_END = 3; //////////////////////////////////////////////////////////////////////////////// /// @} @@ -156,18 +95,24 @@ static int SLOT_END = 8; /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief query mapping for weak pointers +//////////////////////////////////////////////////////////////////////////////// + +static map< TRI_query_t*, v8::Persistent > JSQueries; + //////////////////////////////////////////////////////////////////////////////// /// @brief result set mapping for weak pointers //////////////////////////////////////////////////////////////////////////////// -static map< TRI_result_set_t*, v8::Persistent > JSResultSets; +static map< TRI_result_set_t*, v8::Persistent > JSResultSets; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- -// --SECTION-- OBJECT TEMPLATES +// --SECTION-- JAVASCRIPT OBJECT TEMPLATES // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- @@ -185,12 +130,6 @@ static map< TRI_result_set_t*, v8::Persistent > JSResultSets; static v8::Persistent QueryTempl; -//////////////////////////////////////////////////////////////////////////////// -/// @brief result-set class template -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent ResultSetTempl; - //////////////////////////////////////////////////////////////////////////////// /// @brief TRI_vocbase_col_t template //////////////////////////////////////////////////////////////////////////////// @@ -201,14 +140,14 @@ static v8::Persistent VocbaseColTempl; /// @brief TRI_vocbase_t class template //////////////////////////////////////////////////////////////////////////////// -static v8::Persistent VocBaseTempl; +static v8::Persistent VocbaseTempl; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- -// --SECTION-- FUNCTION NAMES +// --SECTION-- JAVASCRIPT FUNCTION NAMES // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- @@ -220,108 +159,18 @@ static v8::Persistent VocBaseTempl; /// @{ //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @brief "count" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent CountFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "document" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent DocumentFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "ensureGeoIndex" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent EnsureGeoIndexFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "explain" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent ExplainFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "hasNext" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent HasNextFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "limit" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent LimitFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "near" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent NearFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "next" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent NextFuncName; - //////////////////////////////////////////////////////////////////////////////// /// @brief "output" function name //////////////////////////////////////////////////////////////////////////////// static v8::Persistent OutputFuncName; -//////////////////////////////////////////////////////////////////////////////// -/// @brief "parameter" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent ParameterFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "print" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent PrintFuncName; - //////////////////////////////////////////////////////////////////////////////// /// @brief "printQuery" function name //////////////////////////////////////////////////////////////////////////////// static v8::Persistent PrintQueryFuncName; -//////////////////////////////////////////////////////////////////////////////// -/// @brief "save" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent SaveFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "select" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent SelectFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "show" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent ShowFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "skip" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent SkipFuncName; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "toArray" function name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent ToArrayFuncName; - //////////////////////////////////////////////////////////////////////////////// /// @brief "toString" function name //////////////////////////////////////////////////////////////////////////////// @@ -333,7 +182,7 @@ static v8::Persistent ToStringFuncName; //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- -// --SECTION-- QUERY TYPE NAMES +// --SECTION-- JAVASCRIPT KEY NAMES // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- @@ -345,71 +194,6 @@ static v8::Persistent ToStringFuncName; /// @{ //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @brief "collection" query type -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent CollectionQueryType; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "document" query type -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent DocumentQueryType; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "select" query type -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent SelectQueryType; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "limit" query type -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent LimitQueryType; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "near" query type -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent NearQueryType; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "near2" query type -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent Near2QueryType; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "skip" query type -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent SkipQueryType; - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- KEY NAMES -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -// --SECTION-- private variables -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup VocBasePrivate VocBase (Private) -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief "did" key name -//////////////////////////////////////////////////////////////////////////////// - -static v8::Persistent DidKey; - //////////////////////////////////////////////////////////////////////////////// /// @brief "journalSize" key name //////////////////////////////////////////////////////////////////////////////// @@ -438,6 +222,29 @@ static v8::Persistent SyncAfterTimeKey; /// @} //////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- SPECIAL QUERY TYPES +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private variables +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBasePrivate VocBase (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief "collection" query type +//////////////////////////////////////////////////////////////////////////////// + +static v8::Persistent CollectionQueryType; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + // ----------------------------------------------------------------------------- // --SECTION-- REGULAR EXPRESSIONS // ----------------------------------------------------------------------------- @@ -487,10 +294,10 @@ static v8::Handle WrapClass (v8::Persistent clas // create the new handle to return, and set its template type v8::Handle result = classTempl->NewInstance(); - v8::Handle classPtr = v8::External::New(static_cast(y)); + v8::Handle classPtr = v8::External::New(y); // point the 0 index Field to the c++ pointer for unwrapping later - result->SetInternalField(0, classPtr); + result->SetInternalField(SLOT_CLASS, classPtr); return scope.Close(result); } @@ -502,33 +309,12 @@ static v8::Handle WrapClass (v8::Persistent clas template static T* UnwrapClass (v8::Handle obj) { v8::Handle field; - field = v8::Handle::Cast(obj->GetInternalField(0)); + field = v8::Handle::Cast(obj->GetInternalField(SLOT_CLASS)); void* ptr = field->Value(); return static_cast(ptr); } -//////////////////////////////////////////////////////////////////////////////// -/// @brief calls the "toString" function -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle PrintUsingToString (v8::Arguments const& argv) { - v8::HandleScope scope; - - v8::Handle self = argv.Holder(); - v8::Handle toString = v8::Handle::Cast(self->Get(ToStringFuncName)); - - v8::Handle args1[] = { self }; - v8::Handle str = toString->Call(self, 1, args1); - - v8::Handle output = v8::Handle::Cast(self->CreationContext()->Global()->Get(OutputFuncName)); - - v8::Handle args2[] = { str, v8::String::New("\n") }; - output->Call(output, 2, args2); - - return scope.Close(v8::Undefined()); -} - //////////////////////////////////////////////////////////////////////////////// /// @brief checks if argument is a document identifier //////////////////////////////////////////////////////////////////////////////// @@ -568,10 +354,7 @@ static bool IsDocumentId (v8::Handle arg, TRI_voc_cid_t& cid, TRI_voc static TRI_vocbase_col_t const* LoadCollection (v8::Handle collection, v8::Handle* err) { - TRI_vocbase_col_t const* col; - bool ok; - - col = UnwrapClass(collection); + TRI_vocbase_col_t const* col = UnwrapClass(collection); if (col == 0) { *err = v8::String::New("illegal collection pointer"); @@ -595,7 +378,7 @@ static TRI_vocbase_col_t const* LoadCollection (v8::Handle collectio if (col->_newBorn) { LOG_INFO("creating new collection: '%s'", col->_name); - ok = TRI_ManifestCollectionVocBase(col->_vocbase, col); + bool ok = TRI_ManifestCollectionVocBase(col->_vocbase, col); if (! ok) { string msg = "cannot create collection: " + string(TRI_last_error()); @@ -610,7 +393,7 @@ static TRI_vocbase_col_t const* LoadCollection (v8::Handle collectio col = TRI_LoadCollectionVocBase(col->_vocbase, col->_name); - LOG_INFO("collection loaded"); + LOG_DEBUG("collection loaded"); } if (col == 0 || col->_collection == 0) { @@ -631,11 +414,7 @@ static TRI_vocbase_col_t const* LoadCollection (v8::Handle collectio //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- -// --SECTION-- CONVERSION FUNCTIONS -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -// --SECTION-- private functions +// --SECTION-- javascript functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// @@ -644,1030 +423,24 @@ static TRI_vocbase_col_t const* LoadCollection (v8::Handle collectio //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief converts a null into TRI_shape_value_t +/// @brief calls the "toString" function //////////////////////////////////////////////////////////////////////////////// -static bool FillShapeValueNull (TRI_shaper_t* shaper, TRI_shape_value_t* dst) { - dst->_type = TRI_SHAPE_NULL; - dst->_sid = shaper->_sidNull; - dst->_fixedSized = true; - dst->_size = 0; - dst->_value = 0; +static v8::Handle JS_PrintUsingToString (v8::Arguments const& argv) { + v8::HandleScope scope; - return true; -} + v8::Handle self = argv.Holder(); + v8::Handle toString = v8::Handle::Cast(self->Get(ToStringFuncName)); -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a boolean into TRI_shape_value_t -//////////////////////////////////////////////////////////////////////////////// + v8::Handle args1[] = { self }; + v8::Handle str = toString->Call(self, 1, args1); -static bool FillShapeValueBoolean (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { - TRI_shape_boolean_t* ptr; + v8::Handle output = v8::Handle::Cast(self->CreationContext()->Global()->Get(OutputFuncName)); - dst->_type = TRI_SHAPE_BOOLEAN; - dst->_sid = shaper->_sidBoolean; - dst->_fixedSized = true; - dst->_size = sizeof(TRI_shape_boolean_t); - dst->_value = (char*)(ptr = (TRI_shape_boolean_t*) TRI_Allocate(dst->_size)); + v8::Handle args2[] = { str, v8::String::New("\n") }; + output->Call(output, 2, args2); - *ptr = json->Value() ? 1 : 0; - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a boolean into TRI_shape_value_t -//////////////////////////////////////////////////////////////////////////////// - -static bool FillShapeValueBoolean (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { - TRI_shape_boolean_t* ptr; - - dst->_type = TRI_SHAPE_BOOLEAN; - dst->_sid = shaper->_sidBoolean; - dst->_fixedSized = true; - dst->_size = sizeof(TRI_shape_boolean_t); - dst->_value = (char*)(ptr = (TRI_shape_boolean_t*) TRI_Allocate(dst->_size)); - - *ptr = json->BooleanValue() ? 1 : 0; - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a number into TRI_shape_value_t -//////////////////////////////////////////////////////////////////////////////// - -static bool FillShapeValueNumber (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { - TRI_shape_number_t* ptr; - - dst->_type = TRI_SHAPE_NUMBER; - dst->_sid = shaper->_sidNumber; - dst->_fixedSized = true; - dst->_size = sizeof(TRI_shape_number_t); - dst->_value = (char*)(ptr = (TRI_shape_number_t*) TRI_Allocate(dst->_size)); - - *ptr = json->Value(); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a number into TRI_shape_value_t -//////////////////////////////////////////////////////////////////////////////// - -static bool FillShapeValueNumber (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { - TRI_shape_number_t* ptr; - - dst->_type = TRI_SHAPE_NUMBER; - dst->_sid = shaper->_sidNumber; - dst->_fixedSized = true; - dst->_size = sizeof(TRI_shape_number_t); - dst->_value = (char*)(ptr = (TRI_shape_number_t*) TRI_Allocate(dst->_size)); - - *ptr = json->NumberValue(); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a string into TRI_shape_value_t -//////////////////////////////////////////////////////////////////////////////// - -static bool FillShapeValueString (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { - char* ptr; - - v8::String::Utf8Value str(json); - - if (*str == 0) { - dst->_type = TRI_SHAPE_SHORT_STRING; - dst->_sid = shaper->_sidShortString; - dst->_fixedSized = true; - dst->_size = sizeof(TRI_shape_length_short_string_t) + TRI_SHAPE_SHORT_STRING_CUT; - dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); - - * ((TRI_shape_length_short_string_t*) ptr) = 1; - * (ptr + sizeof(TRI_shape_length_short_string_t)) = '\0'; - } - else if (str.length() < TRI_SHAPE_SHORT_STRING_CUT) { // includes '\0' - size_t size = str.length() + 1; - - dst->_type = TRI_SHAPE_SHORT_STRING; - dst->_sid = shaper->_sidShortString; - dst->_fixedSized = true; - dst->_size = sizeof(TRI_shape_length_short_string_t) + TRI_SHAPE_SHORT_STRING_CUT; - dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); - - * ((TRI_shape_length_short_string_t*) ptr) = size; - - memcpy(ptr + sizeof(TRI_shape_length_short_string_t), *str, size); - } - else { - size_t size = str.length() + 1; - - dst->_type = TRI_SHAPE_LONG_STRING; - dst->_sid = shaper->_sidLongString; - dst->_fixedSized = false; - dst->_size = sizeof(TRI_shape_length_long_string_t) + size; - dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); - - * ((TRI_shape_length_long_string_t*) ptr) = size; - - memcpy(ptr + sizeof(TRI_shape_length_long_string_t), *str, size); - } - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a json list into TRI_shape_value_t -//////////////////////////////////////////////////////////////////////////////// - -static bool FillShapeValueList (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { - size_t i; - size_t n; - size_t total; - - TRI_shape_value_t* values; - TRI_shape_value_t* p; - TRI_shape_value_t* e; - - bool hs; - bool hl; - - TRI_shape_sid_t s; - TRI_shape_sid_t l; - - TRI_shape_sid_t* sids; - TRI_shape_size_t* offsets; - TRI_shape_size_t offset; - - TRI_shape_t const* found; - - char* ptr; - - // check for special case "empty list" - n = json->Length(); - - if (n == 0) { - dst->_type = TRI_SHAPE_LIST; - dst->_sid = shaper->_sidList; - - dst->_fixedSized = false; - dst->_size = sizeof(TRI_shape_length_list_t); - dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); - - * (TRI_shape_length_list_t*) ptr = 0; - - return true; - } - - // convert into TRI_shape_value_t array - p = (values = (TRI_shape_value_t*) TRI_Allocate(sizeof(TRI_shape_value_t) * n)); - memset(values, 0, sizeof(TRI_shape_value_t) * n); - - total = 0; - e = values + n; - - for (i = 0; i < n; ++i, ++p) { - v8::Local el = json->Get(i); - bool ok = FillShapeValueJson(shaper, p, el); - - if (! ok) { - for (e = p, p = values; p < e; ++p) { - if (p->_value != 0) { - TRI_Free(p->_value); - } - } - - TRI_Free(values); - - return false; - } - - total += p->_size; - } - - // check if this list is homoegenous - hs = true; - hl = true; - - s = values[0]._sid; - l = values[0]._size; - p = values; - - for (; p < e; ++p) { - if (p->_sid != s) { - hs = false; - break; - } - - if (p->_size != l) { - hl = false; - } - } - - // homogeneous sized - if (hs && hl) { - TRI_homogeneous_sized_list_shape_t* shape; - - shape = (TRI_homogeneous_sized_list_shape_t*) TRI_Allocate(sizeof(TRI_homogeneous_sized_list_shape_t)); - - shape->base._size = sizeof(TRI_homogeneous_sized_list_shape_t); - shape->base._type = TRI_SHAPE_HOMOGENEOUS_SIZED_LIST; - shape->base._dataSize = TRI_SHAPE_SIZE_VARIABLE; - shape->_sidEntry = s; - shape->_sizeEntry = l; - - found = shaper->findShape(shaper, &shape->base); - - if (found == 0) { - for (p = values; p < e; ++p) { - if (p->_value != 0) { - TRI_Free(p->_value); - } - } - - TRI_Free(values); - TRI_Free(shape); - - return false; - } - - dst->_type = found->_type; - dst->_sid = found->_sid; - - dst->_fixedSized = false; - dst->_size = sizeof(TRI_shape_length_list_t) + total; - dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); - - // copy sub-objects into data space - * (TRI_shape_length_list_t*) ptr = n; - ptr += sizeof(TRI_shape_length_list_t); - - for (p = values; p < e; ++p) { - memcpy(ptr, p->_value, p->_size); - ptr += p->_size; - } - } - - // homogeneous - else if (hs) { - TRI_homogeneous_list_shape_t* shape; - - shape = (TRI_homogeneous_list_shape_t*) TRI_Allocate(sizeof(TRI_homogeneous_list_shape_t)); - - shape->base._size = sizeof(TRI_homogeneous_list_shape_t); - shape->base._type = TRI_SHAPE_HOMOGENEOUS_LIST; - shape->base._dataSize = TRI_SHAPE_SIZE_VARIABLE; - shape->_sidEntry = s; - - found = shaper->findShape(shaper, &shape->base); - - if (found == 0) { - for (p = values; p < e; ++p) { - if (p->_value != 0) { - TRI_Free(p->_value); - } - } - - TRI_Free(values); - TRI_Free(shape); - - return false; - } - - dst->_type = found->_type; - dst->_sid = found->_sid; - - offset = sizeof(TRI_shape_length_list_t) + (n + 1) * sizeof(TRI_shape_size_t); - - dst->_fixedSized = false; - dst->_size = offset + total; - dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); - - // copy sub-objects into data space - * (TRI_shape_length_list_t*) ptr = n; - ptr += sizeof(TRI_shape_length_list_t); - - offsets = (TRI_shape_size_t*) ptr; - ptr += (n + 1) * sizeof(TRI_shape_size_t); - - for (p = values; p < e; ++p) { - *offsets++ = offset; - offset += p->_size; - - memcpy(ptr, p->_value, p->_size); - ptr += p->_size; - } - - *offsets = offset; - } - - // in-homogeneous - else { - dst->_type = TRI_SHAPE_LIST; - dst->_sid = shaper->_sidList; - - offset = - sizeof(TRI_shape_length_list_t) - + n * sizeof(TRI_shape_sid_t) - + (n + 1) * sizeof(TRI_shape_size_t); - - dst->_fixedSized = false; - dst->_size = offset + total; - dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); - - // copy sub-objects into data space - * (TRI_shape_length_list_t*) ptr = n; - ptr += sizeof(TRI_shape_length_list_t); - - sids = (TRI_shape_sid_t*) ptr; - ptr += n * sizeof(TRI_shape_sid_t); - - offsets = (TRI_shape_size_t*) ptr; - ptr += (n + 1) * sizeof(TRI_shape_size_t); - - for (p = values; p < e; ++p) { - *sids++ = p->_sid; - - *offsets++ = offset; - offset += p->_size; - - memcpy(ptr, p->_value, p->_size); - ptr += p->_size; - } - - *offsets = offset; - } - - // free TRI_shape_value_t array - for (p = values; p < e; ++p) { - if (p->_value != 0) { - TRI_Free(p->_value); - } - } - - TRI_Free(values); - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a json array into TRI_shape_value_t -//////////////////////////////////////////////////////////////////////////////// - -static bool FillShapeValueArray (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { - size_t n; - size_t i; - size_t total; - - size_t f; - size_t v; - - TRI_shape_value_t* values; - TRI_shape_value_t* p; - TRI_shape_value_t* e; - - TRI_array_shape_t* a; - - TRI_shape_sid_t* sids; - TRI_shape_aid_t* aids; - TRI_shape_size_t* offsetsF; - TRI_shape_size_t* offsetsV; - TRI_shape_size_t offset; - - TRI_shape_t const* found; - - char* ptr; - - // number of attributes - v8::Handle names = json->GetPropertyNames(); - n = names->Length(); - - // convert into TRI_shape_value_t array - p = (values = (TRI_shape_value_t*) TRI_Allocate(n * sizeof(TRI_shape_value_t))); - memset(values, 0, n * sizeof(TRI_shape_value_t)); - - total = 0; - f = 0; - v = 0; - - for (i = 0; i < n; ++i, ++p) { - v8::Handle key = names->Get(i); - v8::Handle val = json->Get(key); - - // first find an identifier for the name - v8::String::Utf8Value keyStr(key); - - if (*keyStr == 0) { - --p; - continue; - } - - if (TRI_EqualString(*keyStr, "_id")) { - --p; - continue; - } - - p->_aid = shaper->findAttributeName(shaper, *keyStr); - - // convert value - bool ok; - - if (p->_aid == 0) { - ok = false; - } - else { - ok = FillShapeValueJson(shaper, p, val); - } - - if (! ok) { - for (e = p, p = values; p < e; ++p) { - if (p->_value != 0) { - TRI_Free(p->_value); - } - } - - TRI_Free(values); - return false; - } - - total += p->_size; - - // count fixed and variable sized values - if (p->_fixedSized) { - ++f; - } - else { - ++v; - } - } - - n = f + v; - - // add variable offset table size - total += (v + 1) * sizeof(TRI_shape_size_t); - - // now sort the shape entries - TRI_SortShapeValues(values, n); - -#ifdef DEBUG_JSON_SHAPER - printf("shape values\n------------\ntotal: %u, fixed: %u, variable: %u\n", - (unsigned int) n, - (unsigned int) f, - (unsigned int) v); - PrintShapeValues(values, n); - printf("\n"); -#endif - - // generate shape structure - i = - sizeof(TRI_array_shape_t) - + n * sizeof(TRI_shape_sid_t) - + n * sizeof(TRI_shape_aid_t) - + (f + 1) * sizeof(TRI_shape_size_t); - - a = (TRI_array_shape_t*) (ptr = (char*) TRI_Allocate(i)); - memset(ptr, 0, i); - - a->base._type = TRI_SHAPE_ARRAY; - a->base._size = i; - a->base._dataSize = (v == 0) ? total : TRI_SHAPE_SIZE_VARIABLE; - - a->_fixedEntries = f; - a->_variableEntries = v; - - ptr += sizeof(TRI_array_shape_t); - - // array of shape identifiers - sids = (TRI_shape_sid_t*) ptr; - ptr += n * sizeof(TRI_shape_sid_t); - - // array of attribute identifiers - aids = (TRI_shape_aid_t*) ptr; - ptr += n * sizeof(TRI_shape_aid_t); - - // array of offsets for fixed part (within the shape) - offset = (v + 1) * sizeof(TRI_shape_size_t); - offsetsF = (TRI_shape_size_t*) ptr; - - // fill destination (except sid) - dst->_type = TRI_SHAPE_ARRAY; - - dst->_fixedSized = true; - dst->_size = total; - dst->_value = (ptr = (char*) TRI_Allocate(dst->_size)); - - // array of offsets for variable part (within the value) - offsetsV = (TRI_shape_size_t*) ptr; - ptr += (v + 1) * sizeof(TRI_shape_size_t); - - // and fill in attributes - e = values + n; - - for (p = values; p < e; ++p) { - *aids++ = p->_aid; - *sids++ = p->_sid; - - memcpy(ptr, p->_value, p->_size); - ptr += p->_size; - - dst->_fixedSized &= p->_fixedSized; - - if (p->_fixedSized) { - *offsetsF++ = offset; - offset += p->_size; - *offsetsF = offset; - } - else { - *offsetsV++ = offset; - offset += p->_size; - *offsetsV = offset; - } - } - - // free TRI_shape_value_t array - for (p = values; p < e; ++p) { - if (p->_value != 0) { - TRI_Free(p->_value); - } - } - - TRI_Free(values); - - // lookup this shape - found = shaper->findShape(shaper, &a->base); - - if (found == 0) { - TRI_Free(a); - return false; - } - - // and finally add the sid - dst->_sid = found->_sid; - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a json object into TRI_shape_value_t -//////////////////////////////////////////////////////////////////////////////// - -static bool FillShapeValueJson (TRI_shaper_t* shaper, TRI_shape_value_t* dst, v8::Handle json) { - if (json->IsNull()) { - return FillShapeValueNull(shaper, dst); - } - - if (json->IsBoolean()) { - return FillShapeValueBoolean(shaper, dst, json->ToBoolean()); - } - - if (json->IsBooleanObject()) { - v8::Handle bo = v8::Handle::Cast(json); - return FillShapeValueBoolean(shaper, dst, bo); - } - - if (json->IsNumber()) { - return FillShapeValueNumber(shaper, dst, json->ToNumber()); - } - - if (json->IsNumberObject()) { - v8::Handle no = v8::Handle::Cast(json); - return FillShapeValueNumber(shaper, dst, no); - } - - if (json->IsString()) { - return FillShapeValueString(shaper, dst, json->ToString()); - } - - if (json->IsStringObject()) { - v8::Handle so = v8::Handle::Cast(json); - return FillShapeValueString(shaper, dst, so->StringValue()); - } - - if (json->IsArray()) { - v8::Handle array = v8::Handle::Cast(json); - return FillShapeValueList(shaper, dst, array); - } - - if (json->IsObject()) { - return FillShapeValueArray(shaper, dst, json->ToObject()); - } - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a data null blob into a json object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JsonShapeDataNull (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size) { - return v8::Null(); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a data boolean blob into a json object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JsonShapeDataBoolean (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size) { - bool v; - - v = (* (TRI_shape_boolean_t const*) data) != 0; - - return v ? v8::True() : v8::False(); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a data number blob into a json object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JsonShapeDataNumber (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size) { - TRI_shape_number_t v; - - v = * (TRI_shape_number_t const*) data; - - return v8::Number::New(v); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a data short string blob into a json object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JsonShapeDataShortString (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size) { - TRI_shape_length_short_string_t l; - - l = * (TRI_shape_length_short_string_t const*) data; - data += sizeof(TRI_shape_length_short_string_t); - - return v8::String::New(data, l - 1); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a data long string blob into a json object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JsonShapeDataLongString (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size) { - TRI_shape_length_long_string_t l; - - l = * (TRI_shape_length_long_string_t const*) data; - data += sizeof(TRI_shape_length_long_string_t); - - return v8::String::New(data, l - 1); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a data array blob into a json object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JsonShapeDataArray (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size) { - TRI_array_shape_t const* s; - TRI_shape_aid_t const* aids; - TRI_shape_sid_t const* sids; - TRI_shape_size_t const* offsetsF; - TRI_shape_size_t const* offsetsV; - TRI_shape_size_t f; - TRI_shape_size_t i; - TRI_shape_size_t n; - TRI_shape_size_t v; - char const* qtr; - - v8::Handle array; - - s = (TRI_array_shape_t const*) shape; - f = s->_fixedEntries; - v = s->_variableEntries; - n = f + v; - - qtr = (char const*) shape; - array = v8::Object::New(); - - qtr += sizeof(TRI_array_shape_t); - - sids = (TRI_shape_sid_t const*) qtr; - qtr += n * sizeof(TRI_shape_sid_t); - - aids = (TRI_shape_aid_t const*) qtr; - qtr += n * sizeof(TRI_shape_aid_t); - - offsetsF = (TRI_shape_size_t const*) qtr; - - for (i = 0; i < f; ++i, ++sids, ++aids, ++offsetsF) { - TRI_shape_sid_t sid = *sids; - TRI_shape_aid_t aid = *aids; - TRI_shape_size_t offset; - TRI_shape_t const* subshape; - char const* name; - v8::Handle element; - - offset = *offsetsF; - subshape = shaper->lookupShapeId(shaper, sid); - name = shaper->lookupAttributeId(shaper, aid); - - if (subshape == 0) { - LOG_WARNING("cannot find shape #%u", (unsigned int) sid); - continue; - } - - if (name == 0) { - LOG_WARNING("cannot find attribute #%u", (unsigned int) aid); - continue; - } - - element = JsonShapeData(shaper, subshape, data + offset, offsetsF[1] - offset); - - array->Set(v8::String::New(name), element); - } - - offsetsV = (TRI_shape_size_t const*) data; - - for (i = 0; i < v; ++i, ++sids, ++aids, ++offsetsV) { - TRI_shape_sid_t sid = *sids; - TRI_shape_aid_t aid = *aids; - TRI_shape_size_t offset; - TRI_shape_t const* subshape; - char const* name; - v8::Handle element; - - offset = *offsetsV; - subshape = shaper->lookupShapeId(shaper, sid); - name = shaper->lookupAttributeId(shaper, aid); - - if (subshape == 0) { - LOG_WARNING("cannot find shape #%u", (unsigned int) sid); - continue; - } - - if (name == 0) { - LOG_WARNING("cannot find attribute #%u", (unsigned int) aid); - continue; - } - - element = JsonShapeData(shaper, subshape, data + offset, offsetsV[1] - offset); - - array->Set(v8::String::New(name), element); - } - - return array; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a data list blob into a json object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JsonShapeDataList (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size) { - TRI_shape_length_list_t i; - TRI_shape_length_list_t l; - TRI_shape_sid_t const* sids; - TRI_shape_size_t const* offsets; - char const* ptr; - v8::Handle list; - - list = v8::Array::New(); - - ptr = data; - l = * (TRI_shape_length_list_t const*) ptr; - - ptr += sizeof(TRI_shape_length_list_t); - sids = (TRI_shape_sid_t const*) ptr; - - ptr += l * sizeof(TRI_shape_sid_t); - offsets = (TRI_shape_size_t const*) ptr; - - for (i = 0; i < l; ++i, ++sids, ++offsets) { - TRI_shape_sid_t sid = *sids; - TRI_shape_size_t offset; - TRI_shape_t const* subshape; - v8::Handle element; - - offset = *offsets; - subshape = shaper->lookupShapeId(shaper, sid); - - if (subshape == 0) { - LOG_WARNING("cannot find shape #%u", (unsigned int) sid); - continue; - } - - element = JsonShapeData(shaper, subshape, data + offset, offsets[1] - offset); - - list->Set(i, element); - } - - return list; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a data homogeneous list blob into a json object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JsonShapeDataHomogeneousList (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size) { - TRI_homogeneous_list_shape_t const* s; - TRI_shape_length_list_t i; - TRI_shape_length_list_t l; - TRI_shape_sid_t sid; - TRI_shape_size_t const* offsets; - char const* ptr; - v8::Handle list; - - list = v8::Array::New(); - - s = (TRI_homogeneous_list_shape_t const*) shape; - sid = s->_sidEntry; - - ptr = data; - l = * (TRI_shape_length_list_t const*) ptr; - - ptr += sizeof(TRI_shape_length_list_t); - offsets = (TRI_shape_size_t const*) ptr; - - for (i = 0; i < l; ++i, ++offsets) { - TRI_shape_size_t offset; - TRI_shape_t const* subshape; - v8::Handle element; - - offset = *offsets; - subshape = shaper->lookupShapeId(shaper, sid); - - if (subshape == 0) { - LOG_WARNING("cannot find shape #%u", (unsigned int) sid); - continue; - } - - element = JsonShapeData(shaper, subshape, data + offset, offsets[1] - offset); - - list->Set(i, element); - } - - return list; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a data homogeneous sized list blob into a json object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JsonShapeDataHomogeneousSizedList (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size) { - TRI_homogeneous_sized_list_shape_t const* s; - TRI_shape_length_list_t i; - TRI_shape_length_list_t l; - TRI_shape_sid_t sid; - TRI_shape_size_t length; - TRI_shape_size_t offset; - char const* ptr; - v8::Handle list; - - list = v8::Array::New(); - - s = (TRI_homogeneous_sized_list_shape_t const*) shape; - sid = s->_sidEntry; - - ptr = data; - l = * (TRI_shape_length_list_t const*) ptr; - - length = s->_sizeEntry; - offset = sizeof(TRI_shape_length_list_t); - - for (i = 0; i < l; ++i, offset += length) { - TRI_shape_t const* subshape; - v8::Handle element; - - subshape = shaper->lookupShapeId(shaper, sid); - - if (subshape == 0) { - LOG_WARNING("cannot find shape #%u", (unsigned int) sid); - continue; - } - - element = JsonShapeData(shaper, subshape, data + offset, length); - - list->Set(i, element); - } - - return list; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a data blob into a json object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JsonShapeData (TRI_shaper_t* shaper, - TRI_shape_t const* shape, - char const* data, - size_t size) { - if (shape == 0) { - return v8::Null(); - } - - switch (shape->_type) { - case TRI_SHAPE_NULL: - return JsonShapeDataNull(shaper, shape, data, size); - - case TRI_SHAPE_BOOLEAN: - return JsonShapeDataBoolean(shaper, shape, data, size); - - case TRI_SHAPE_NUMBER: - return JsonShapeDataNumber(shaper, shape, data, size); - - case TRI_SHAPE_SHORT_STRING: - return JsonShapeDataShortString(shaper, shape, data, size); - - case TRI_SHAPE_LONG_STRING: - return JsonShapeDataLongString(shaper, shape, data, size); - - case TRI_SHAPE_ARRAY: - return JsonShapeDataArray(shaper, shape, data, size); - - case TRI_SHAPE_LIST: - return JsonShapeDataList(shaper, shape, data, size); - - case TRI_SHAPE_HOMOGENEOUS_LIST: - return JsonShapeDataHomogeneousList(shaper, shape, data, size); - - case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: - return JsonShapeDataHomogeneousSizedList(shaper, shape, data, size); - } - - return v8::Null(); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a shaped json into a V8 object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle ObjectShapedJson (TRI_doc_collection_t* collection, - TRI_voc_did_t did, - TRI_shaper_t* shaper, - TRI_shaped_json_t const* shaped) { - TRI_shape_t const* shape; - - shape = shaper->lookupShapeId(shaper, shaped->_sid); - - if (shape == 0) { - LOG_WARNING("cannot find shape #%u", (unsigned int) shaped->_sid); - return v8::Null(); - } - - v8::Handle result = JsonShapeData(shaper, shape, shaped->_data.data, shaped->_data.length); - - if (result->IsObject()) { - char* cidStr = TRI_StringUInt64(collection->base._cid); - char* didStr = TRI_StringUInt64(did); - - string name = cidStr + string(":") + didStr; - - TRI_FreeString(didStr); - TRI_FreeString(cidStr); - - result->ToObject()->Set(DidKey, v8::String::New(name.c_str())); - } - - return result; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a result-set into a V8 array -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle ArrayResultSet (TRI_result_set_t* rs) { - v8::Handle array = v8::Array::New(); - - TRI_doc_collection_t* collection = rs->_container->_collection; - TRI_shaper_t* shaper = collection->_shaper; - - size_t pos; - - for (pos = 0; rs->hasNext(rs); ++pos) { - TRI_voc_did_t did; - TRI_shaped_json_t* element = rs->next(rs, &did); - v8::Handle object = ObjectShapedJson(collection, did, shaper, element); - - array->Set(pos, object); - } - - return array; + return scope.Close(v8::Undefined()); } //////////////////////////////////////////////////////////////////////////////// @@ -1688,337 +461,192 @@ static v8::Handle ArrayResultSet (TRI_result_set_t* rs) { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief stringify query -//////////////////////////////////////////////////////////////////////////////// - -static string StringifyQuery (v8::Handle query) { - if (! query->IsObject()) { - return "[unknown]"; - } - - v8::Handle self = query->ToObject(); - - if (self->InternalFieldCount() <= SLOT_TYPE) { - return "[unknown]"; - } - - v8::Handle type = self->GetInternalField(SLOT_TYPE); - string result; - - if (type == CollectionQueryType) { - v8::Handle err; - TRI_vocbase_col_t const* collection = LoadCollection(self, &err); - - if (collection == 0) { - return "[unknown]"; - } - - result += "[collection \"" + string(collection->_name) + "\"]"; - } - else { - if (self->InternalFieldCount() <= SLOT_OPERAND) { - return "[unknown]"; - } - - v8::Handle operand = self->GetInternalField(SLOT_OPERAND); - - result += StringifyQuery(operand); - - // document query - if (type == DocumentQueryType) { - if (self->InternalFieldCount() <= SLOT_DOCUMENT) { - return "[unknown]"; - } - - v8::Handle val = self->GetInternalField(SLOT_DOCUMENT); - result += ".document(" + ObjectToString(val) + ")"; - } - - // limit query - else if (type == LimitQueryType) { - if (self->InternalFieldCount() <= SLOT_LIMIT) { - return "[unknown]"; - } - - v8::Handle val = self->GetInternalField(SLOT_LIMIT); - result += ".limit(" + ObjectToString(val) + ")"; - } - - // near query (list) - else if (type == NearQueryType) { - if (self->InternalFieldCount() <= SLOT_LOCATION) { - return "[unknown]"; - } - - v8::Handle lat = self->GetInternalField(SLOT_LATITUDE); - v8::Handle lon = self->GetInternalField(SLOT_LONGITUDE); - v8::Handle loc = self->GetInternalField(SLOT_LOCATION); - result += ".near(\"" + ObjectToString(loc) + "\", " + ObjectToString(lat) + ", " + ObjectToString(lon) + ")"; - } - - // near query (array) - else if (type == Near2QueryType) { - if (self->InternalFieldCount() <= SLOT_LOC_LONGITUDE) { - return "[unknown]"; - } - - v8::Handle lat = self->GetInternalField(SLOT_LATITUDE); - v8::Handle lon = self->GetInternalField(SLOT_LONGITUDE); - v8::Handle locLat = self->GetInternalField(SLOT_LOC_LATITUDE); - v8::Handle locLon = self->GetInternalField(SLOT_LOC_LONGITUDE); - result += ".near(\"" - + ObjectToString(locLat) + "\", " + ObjectToString(lat) + ", \"" - + ObjectToString(locLon) + "\", " + ObjectToString(lon) + ")"; - } - - // select query - else if (type == SelectQueryType) { - result += ".select()"; - } - - // skip query - else if (type == SkipQueryType) { - if (self->InternalFieldCount() <= SLOT_SKIP) { - return "[unknown]"; - } - - v8::Handle val = self->GetInternalField(SLOT_SKIP); - result += ".skip(" + ObjectToString(val) + ")"; - } - } - - return result; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief creates a query -//////////////////////////////////////////////////////////////////////////////// - -static TRI_query_t* CreateQuery (v8::Handle query, v8::Handle* err) { - if (! query->IsObject()) { - *err = v8::String::New("corrupted query"); - return 0; - } - - v8::Handle self = query->ToObject(); - - if (self->InternalFieldCount() <= SLOT_TYPE) { - *err = v8::String::New("corrupted query"); - return 0; - } - - v8::Handle type = self->GetInternalField(SLOT_TYPE); - - // the "query" is the whole collection - if (type == CollectionQueryType) { - TRI_vocbase_col_t const* collection = LoadCollection(self, err); - - if (collection == 0) { - return 0; - } - - return TRI_CreateCollectionQuery(collection->_collection); - } - else { - if (self->InternalFieldCount() <= SLOT_OPERAND) { - *err = v8::String::New("corrupted query, missing operand"); - return 0; - } - - v8::Handle operand = self->GetInternalField(SLOT_OPERAND); - - TRI_query_t* opQuery = CreateQuery(operand, err); - - if (opQuery == 0) { - return 0; - } - - // select document by idenifier - if (type == DocumentQueryType) { - if (self->InternalFieldCount() <= SLOT_DOCUMENT) { - *err = v8::String::New("corrupted query, missing document identifier"); - return 0; - } - - TRI_voc_cid_t cid = 0; - TRI_voc_did_t did = 0; - v8::Handle idVal = self->GetInternalField(SLOT_DOCUMENT); - - if (IsDocumentId(idVal, cid, did)) { - if (cid != 0 && cid != opQuery->_collection->base._cid) { - *err = v8::String::New("cannot execute cross collection query"); - return 0; - } - } - - return TRI_CreateDocumentQuery(opQuery, did); - } - - // limit the result set - else if (type == LimitQueryType) { - if (self->InternalFieldCount() <= SLOT_LIMIT) { - *err = v8::String::New("corrupted query, missing limit"); - return 0; - } - - double limit = ObjectToDouble(self->GetInternalField(SLOT_LIMIT)); - - return TRI_CreateLimitQuery(opQuery, (TRI_voc_ssize_t) limit); - } - - // near a given point (list) - else if (type == NearQueryType) { - if (self->InternalFieldCount() <= SLOT_LOCATION) { - *err = v8::String::New("corrupted query, missing location"); - return 0; - } - - double count = ObjectToDouble(self->GetInternalField(SLOT_LIMIT)); - double latitiude = ObjectToDouble(self->GetInternalField(SLOT_LATITUDE)); - double longitude = ObjectToDouble(self->GetInternalField(SLOT_LONGITUDE)); - string location = ObjectToString(self->GetInternalField(SLOT_LOCATION)); - - char const* errorMsg; - TRI_query_t* query = TRI_CreateNearQuery(opQuery, location.c_str(), latitiude, longitude, count, &errorMsg); - - if (query == 0) { - *err = v8::String::New(errorMsg); - return 0; - } - else { - return query; - } - } - - // near a given point (array) - else if (type == Near2QueryType) { - if (self->InternalFieldCount() <= SLOT_LOC_LONGITUDE) { - *err = v8::String::New("corrupted query, missing location"); - return 0; - } - - double count = ObjectToDouble(self->GetInternalField(SLOT_LIMIT)); - - double latitiude = ObjectToDouble(self->GetInternalField(SLOT_LATITUDE)); - string locLatitude = ObjectToString(self->GetInternalField(SLOT_LOC_LATITUDE)); - - double longitude = ObjectToDouble(self->GetInternalField(SLOT_LONGITUDE)); - string locLongitude = ObjectToString(self->GetInternalField(SLOT_LOC_LONGITUDE)); - - char const* errorMsg; - TRI_query_t* query = TRI_CreateNear2Query(opQuery, locLatitude.c_str(), latitiude, locLongitude.c_str(), longitude, count, &errorMsg); - - if (query == 0) { - *err = v8::String::New(errorMsg); - return 0; - } - else { - return query; - } - } - - - // selection by example - else if (type == SelectQueryType) { - return opQuery; - } - - // skip the result set - else if (type == SkipQueryType) { - if (self->InternalFieldCount() <= SLOT_SKIP) { - *err = v8::String::New("corrupted query, missing skip value"); - return 0; - } - - double skip = ObjectToDouble(self->GetInternalField(SLOT_SKIP)); - - if (skip <= 0.0) { - return TRI_CreateSkipQuery(opQuery, 0); - } - else { - return TRI_CreateSkipQuery(opQuery, (TRI_voc_size_t) skip); - } - } - - *err = v8::String::New("corrupted query, unknown query type"); - return 0; - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief weak reference callback +/// @brief weak reference callback for result sets //////////////////////////////////////////////////////////////////////////////// static void WeakResultSetCallback (v8::Persistent object, void* parameter) { TRI_result_set_t* rs = (TRI_result_set_t*) parameter; - LOG_TRACE("weak result set callback called"); + LOG_TRACE("weak-callback for result-set called"); // find the persistent handle - v8::Persistent rsp = JSResultSets[rs]; + v8::Persistent persistent = JSResultSets[rs]; JSResultSets.erase(rs); - // clear the internal field holding the result set - rsp->SetInternalField(0, v8::Null()); - // dispose and clear the persistent handle - rsp.Dispose(); - rsp.Clear(); + persistent.Dispose(); + persistent.Clear(); // and free the result set rs->free(rs); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief stores a result set in a javascript object +//////////////////////////////////////////////////////////////////////////////// + +static void StoreResultSet (v8::Handle rsObject, + TRI_result_set_t* rs) { + if (JSResultSets.find(rs) == JSResultSets.end()) { + v8::Persistent persistent = v8::Persistent::New(v8::External::New(rs)); + + rsObject->SetInternalField(SLOT_RESULT_SET, persistent); + + JSResultSets[rs] = persistent; + + persistent.MakeWeak(rs, WeakResultSetCallback); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief weak reference callback for queries +//////////////////////////////////////////////////////////////////////////////// + +static void WeakQueryCallback (v8::Persistent object, void* parameter) { + TRI_query_t* query = (TRI_query_t*) parameter; + + LOG_TRACE("weak-callback for query called"); + + // find the persistent handle + v8::Persistent persistent = JSQueries[query]; + JSQueries.erase(query); + + // dispose and clear the persistent handle + persistent.Dispose(); + persistent.Clear(); + + // and free the result set + query->free(query); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief stores a query in a javascript object +//////////////////////////////////////////////////////////////////////////////// + +static void StoreQuery (v8::Handle queryObject, + TRI_query_t* query) { + if (JSQueries.find(query) == JSQueries.end()) { + v8::Persistent persistent = v8::Persistent::New(v8::External::New(query)); + + queryObject->SetInternalField(SLOT_QUERY, persistent); + queryObject->SetInternalField(SLOT_RESULT_SET, v8::Null()); + + JSQueries[query] = persistent; + + persistent.MakeWeak(query, WeakQueryCallback); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief extracts a query from a javascript object +//////////////////////////////////////////////////////////////////////////////// + +static TRI_query_t* ExtractQuery (v8::Handle queryObject, + v8::Handle* err) { + + // the internal fields QUERY and RESULT_SET must be present + if (queryObject->InternalFieldCount() <= SLOT_RESULT_SET) { + *err = v8::String::New("corrupted query"); + return 0; + } + + v8::Handle value = queryObject->GetInternalField(SLOT_QUERY); + + // ............................................................................. + // special case: the whole collection + // ............................................................................. + + if (value == CollectionQueryType) { + TRI_vocbase_col_t const* collection = LoadCollection(queryObject, err); + + if (collection == 0) { + return false; + } + + TRI_query_t* query = TRI_CreateCollectionQuery(collection); + + StoreQuery(queryObject, query); + + return query; + } + + // ............................................................................. + // standard case: a normal query + // ............................................................................. + + else { + TRI_query_t* query = static_cast(v8::Handle::Cast(value)->Value()); + + if (query == 0) { + *err = v8::String::New("corrupted query"); + return 0; + } + + return query; + } +} + //////////////////////////////////////////////////////////////////////////////// /// @brief executes a query or uses existing result set //////////////////////////////////////////////////////////////////////////////// -static v8::Handle CheckAndExecuteQuery (v8::Handle query, v8::Handle* err) { - v8::Handle self = query->ToObject(); +static TRI_result_set_t* ExecuteQuery (v8::Handle queryObject, + v8::Handle* err) { + v8::Handle self = queryObject->ToObject(); if (self->InternalFieldCount() <= SLOT_RESULT_SET) { *err = v8::String::New("corrupted query"); - return v8::Null(); + return 0; } v8::Handle value = self->GetInternalField(SLOT_RESULT_SET); + // ............................................................................. + // case: unexecuted query + // ............................................................................. + if (value->IsNull()) { - LOG_TRACE("creating query"); + LOG_TRACE("executing query"); - // create a TRI_query from the query description - TRI_query_t* q = CreateQuery(query, err); + TRI_query_t* query = ExtractQuery(self, err); - if (q == 0) { - return v8::Undefined(); + if (query == 0) { + return 0; } // execute the query and get a result set - TRI_result_set_t* rs = TRI_ExecuteQuery(q); + TRI_result_set_t* rs = TRI_ExecuteQuery(query); if (rs == 0) { *err = v8::String::New("cannot execute query"); - return v8::Undefined(); + return 0; } - // check if we know this set, otherwise construct a result set object - if (JSResultSets.find(rs) == JSResultSets.end()) { - v8::Persistent rsp = v8::Persistent::New(WrapClass(ResultSetTempl, rs)); + if (rs->_error != 0) { + *err = v8::String::New(rs->_error); + rs->free(rs); - self->SetInternalField(SLOT_RESULT_SET, rsp); - rsp.MakeWeak(rs, WeakResultSetCallback); - JSResultSets[rs] = rsp; + return 0; + } - return rsp; - } - else { - v8::Persistent rsp = JSResultSets[rs]; - self->SetInternalField(SLOT_RESULT_SET, rsp); - return rsp; - } + StoreResultSet(queryObject, rs); + + return rs; } + + // ............................................................................. + // case: already executed query + // ............................................................................. + else { - return value; + TRI_result_set_t* rs = static_cast(v8::Handle::Cast(value)->Value()); + + if (rs == 0) { + *err = v8::String::New("cannot extract result set"); + return 0; + } + + return rs; } } @@ -2026,7 +654,7 @@ static v8::Handle CheckAndExecuteQuery (v8::Handle query, /// @brief returns true if a query has been executed //////////////////////////////////////////////////////////////////////////////// -static bool QueryExecuted (v8::Handle query) { +static bool IsExecutedQuery (v8::Handle query) { if (query->InternalFieldCount() <= SLOT_RESULT_SET) { return false; } @@ -2037,25 +665,117 @@ static bool QueryExecuted (v8::Handle query) { } //////////////////////////////////////////////////////////////////////////////// -/// @brief counts an existing query +/// @brief stringifies a query //////////////////////////////////////////////////////////////////////////////// -static v8::Handle CountQuery (v8::Arguments const& argv) { - v8::HandleScope scope; - - v8::Handle query = argv.Holder(); - v8::Handle err; - - v8::Handle q = CheckAndExecuteQuery(argv.Holder(), &err); - - if (q->IsUndefined()) { - return scope.Close(v8::ThrowException(err)); +static string StringifyQuery (v8::Handle queryObject) { + if (! queryObject->IsObject()) { + return "[unknown]"; } - TRI_result_set_t* rs = UnwrapClass(q->ToObject()); + v8::Handle self = queryObject->ToObject(); + + if (self->InternalFieldCount() <= SLOT_RESULT_SET) { + return "[unknown]"; + } + + v8::Handle value = self->GetInternalField(SLOT_QUERY); + + // ............................................................................. + // special case: the whole collection + // ............................................................................. + + if (value == CollectionQueryType) { + TRI_vocbase_col_t const* col = UnwrapClass(self); + + if (col == 0) { + return "[unknown collection]"; + } + + return "[collection \"" + string(col->_name) + "\"]"; + } + + // ............................................................................. + // standard case: a normal query + // ............................................................................. + + else { + TRI_query_t* query = static_cast(v8::Handle::Cast(value)->Value()); + + if (query == 0) { + return "[corrupted query]"; + } + + return query->_string; + } + + return TRI_DuplicateString("[corrupted]"); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- javascript functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBasePrivate VocBase (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief calls the "printQuery" function +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_PrintQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + v8::Handle self = argv.Holder(); + v8::Handle print = v8::Handle::Cast(self->CreationContext()->Global()->Get(PrintQueryFuncName)); + + v8::Handle args[] = { self }; + print->Call(print, 1, args); + + return scope.Close(v8::Undefined()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a query into a fluent interface representation +/// +/// show() +/// +/// Shows the representation of the query. +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_ShowQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + v8::Handle self = argv.Holder(); + + string result = StringifyQuery(self); + + return scope.Close(v8::String::New(result.c_str())); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief counts the number of documents in a result set +/// +/// count() +/// +/// The "count" operator counts the number of document in the result set and +/// returns that number. +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_CountQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + v8::Handle err; + TRI_result_set_t* rs = ExecuteQuery(argv.Holder(), &err); if (rs == 0) { - return scope.Close(v8::ThrowException(v8::String::New("cannot execute query"))); + return scope.Close(v8::ThrowException(err)); } if (0 < argv.Length() && argv[0]->IsTrue()) { @@ -2067,62 +787,33 @@ static v8::Handle CountQuery (v8::Arguments const& argv) { } //////////////////////////////////////////////////////////////////////////////// -/// @brief looks up a document +/// @brief explains how a query was executed +/// +/// explain() +/// +/// In order to optimise queries you need to know how the storage engine +/// executed that type of query. You can use the "explain()" operator to +/// see how a query was executed. +/// +/// @verbinclude fluent9 +/// +/// The "explain" operator returns an object with the following attributes. +/// +/// - cursor: describes how the result set was computed. +/// - scannedIndexEntries: how many index entries were scanned +/// - scannedDocuments: how many documents were scanned +/// - matchedDocuments: the sum of all matched documents in each step +/// - runtime: the runtime in seconds //////////////////////////////////////////////////////////////////////////////// -static v8::Handle DocumentQuery (v8::Arguments const& argv) { +static v8::Handle JS_ExplainQuery (v8::Arguments const& argv) { v8::HandleScope scope; - v8::Handle query = argv.Holder(); - - // query should be un-executed - if (QueryExecuted(query)) { - return scope.Close(v8::ThrowException(v8::String::New("query already executed"))); - } - - // first and only argument schould be an integer - if (argv.Length() != 1) { - return scope.Close(v8::ThrowException(v8::String::New("usage: document()"))); - } - - v8::Handle arg1 = argv[0]; - TRI_voc_cid_t cid; - TRI_voc_did_t did; - - if (! IsDocumentId(arg1, cid, did)) { - return scope.Close(v8::ThrowException(v8::String::New(" must be a document identifier"))); - } - - v8::Handle result = QueryTempl->NewInstance(); - - result->SetInternalField(SLOT_TYPE, DocumentQueryType); - result->SetInternalField(SLOT_RESULT_SET, v8::Null()); - result->SetInternalField(SLOT_OPERAND, query); - result->SetInternalField(SLOT_DOCUMENT, arg1); - - return scope.Close(result); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief explains an existing query -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle ExplainQuery (v8::Arguments const& argv) { - v8::HandleScope scope; - - v8::Handle query = argv.Holder(); v8::Handle err; - - v8::Handle q = CheckAndExecuteQuery(argv.Holder(), &err); - - if (q->IsUndefined()) { - return scope.Close(v8::ThrowException(err)); - } - - TRI_result_set_t* rs = UnwrapClass(q->ToObject()); + TRI_result_set_t* rs = ExecuteQuery(argv.Holder(), &err); if (rs == 0) { - return scope.Close(v8::ThrowException(v8::String::New("cannot execute query"))); + return scope.Close(v8::ThrowException(err)); } v8::Handle result = v8::Object::New(); @@ -2137,94 +828,62 @@ static v8::Handle ExplainQuery (v8::Arguments const& argv) { } //////////////////////////////////////////////////////////////////////////////// -/// @brief executes a query and checks for next result +/// @brief checks for the next result document +/// +/// hasNext() +/// +/// The "hasNext" operator returns true, if the cursor still has documents. +/// In this case the next document can be accessed using the "next" operator, +/// which will advance the cursor. //////////////////////////////////////////////////////////////////////////////// -static v8::Handle HasNextQuery (v8::Arguments const& argv) { +static v8::Handle JS_HasNextQuery (v8::Arguments const& argv) { v8::HandleScope scope; - v8::Handle self = argv.Holder(); + v8::Handle err; + TRI_result_set_t* rs = ExecuteQuery(argv.Holder(), &err); - v8::Handle q = CheckAndExecuteQuery(argv.Holder(), &err); - - if (q->IsUndefined()) { + if (rs == 0) { return scope.Close(v8::ThrowException(err)); } - TRI_result_set_t* rs = UnwrapClass(q->ToObject()); + return scope.Close(rs->hasNext(rs) ? v8::True() : v8::False()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the next result document +/// +/// next() +/// +/// If the "hasNext" operator returns true, if the cursor still has documents. +/// In this case the next document can be accessed using the "next" operator, +/// which will advance the cursor. +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_NextQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + v8::Handle err; + TRI_result_set_t* rs = ExecuteQuery(argv.Holder(), &err); if (rs == 0) { - return scope.Close(v8::ThrowException(v8::String::New("cannot execute query"))); - } - else { - return scope.Close(rs->hasNext(rs) ? v8::True() : v8::False()); - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief limits an existing query -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle LimitQuery (v8::Arguments const& argv) { - v8::HandleScope scope; - - v8::Handle query = argv.Holder(); - - // query should be un-executed - if (QueryExecuted(query)) { - return scope.Close(v8::ThrowException(v8::String::New("query already executed"))); - } - - // first and only argument schould be an integer - if (argv.Length() < 1) { - return scope.Close(v8::ThrowException(v8::String::New("usage: limit()"))); - } - - v8::Handle arg1 = argv[0]; - - if (! arg1->IsNumber()) { - return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New(" must be a number")))); - } - - double limit = arg1->ToNumber()->Value(); - - v8::Handle result = QueryTempl->NewInstance(); - - result->SetInternalField(SLOT_TYPE, LimitQueryType); - result->SetInternalField(SLOT_RESULT_SET, v8::Null()); - result->SetInternalField(SLOT_OPERAND, query); - result->SetInternalField(SLOT_LIMIT, v8::Uint32::New(limit)); - - return scope.Close(result); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief executes a query and returns the next result -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle NextQuery (v8::Arguments const& argv) { - v8::HandleScope scope; - v8::Handle self = argv.Holder(); - v8::Handle err; - - v8::Handle q = CheckAndExecuteQuery(argv.Holder(), &err); - - if (q->IsUndefined()) { return scope.Close(v8::ThrowException(err)); } - TRI_result_set_t* rs = UnwrapClass(q->ToObject()); - - if (rs == 0) { - return scope.Close(v8::ThrowException(v8::String::New("cannot execute query"))); - } - else if (rs->hasNext(rs)) { + if (rs->hasNext(rs)) { TRI_doc_collection_t* collection = rs->_container->_collection; TRI_shaper_t* shaper = collection->_shaper; + TRI_voc_did_t did; - TRI_shaped_json_t* element = rs->next(rs, &did); + TRI_json_t const* augmented; + TRI_shaped_json_t* element = rs->next(rs, &did, &augmented); + v8::Handle object = ObjectShapedJson(collection, did, shaper, element); + if (augmented != NULL) { + AugmentObject(object, augmented); + } + return scope.Close(object); } else { @@ -2233,153 +892,258 @@ static v8::Handle NextQuery (v8::Arguments const& argv) { } //////////////////////////////////////////////////////////////////////////////// -/// @brief selects elements by example +/// @brief executes a query and returns the result als array //////////////////////////////////////////////////////////////////////////////// -static v8::Handle SelectQuery (v8::Arguments const& argv) { +static v8::Handle JS_ToArrayQuery (v8::Arguments const& argv) { v8::HandleScope scope; - v8::Handle query = argv.Holder(); + v8::Handle err; + TRI_result_set_t* rs = ExecuteQuery(argv.Holder(), &err); - // query should be un-executed - if (QueryExecuted(query)) { + if (rs == 0) { + return scope.Close(v8::ThrowException(err)); + } + + return scope.Close(ArrayResultSet(rs)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns all elements +/// +/// The "all" operator returns all documents of a collection. +/// +/// all() +/// +/// Selects all documents. +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_AllQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract the operand query + v8::Handle operand = argv.Holder(); + v8::Handle err; + TRI_query_t* opQuery = ExtractQuery(operand, &err); + + if (opQuery == 0) { + return scope.Close(v8::ThrowException(err)); + } + + if (IsExecutedQuery(operand)) { return scope.Close(v8::ThrowException(v8::String::New("query already executed"))); } + // ............................................................................. + // case: no arguments + // ............................................................................. + + if (argv.Length() == 0) { + v8::Handle result = QueryTempl->NewInstance(); + + StoreQuery(result, opQuery->clone(opQuery)); + + return scope.Close(result); + } + + // ............................................................................. + // error case + // ............................................................................. + + else { + return scope.Close(v8::ThrowException(v8::String::New("usage: all()"))); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief looks up a document +/// +/// The "document" operator finds a document given it's identifier. +/// +/// document(identifier) +/// +/// Returns the empty result set or a result set containing the document +/// with document identifier @c identifier. +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_DocumentQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract the operand query + v8::Handle operand = argv.Holder(); + v8::Handle err; + TRI_query_t* opQuery = ExtractQuery(operand, &err); + + if (opQuery == 0) { + return scope.Close(v8::ThrowException(err)); + } + + if (IsExecutedQuery(operand)) { + return scope.Close(v8::ThrowException(v8::String::New("query already executed"))); + } + + // first and only argument schould be an document idenfifier + if (argv.Length() != 1) { + return scope.Close(v8::ThrowException(v8::String::New("usage: document()"))); + } + + v8::Handle arg1 = argv[0]; + TRI_voc_cid_t cid; + TRI_voc_did_t did; + + if (! IsDocumentId(arg1, cid, did)) { + return scope.Close(v8::ThrowException(v8::String::New(" must be a document identifier"))); + } + + if (cid != 0 && cid != opQuery->_collection->_collection->base._cid) { + return scope.Close(v8::ThrowException(v8::String::New("cannot execute cross collection query"))); + } + + // ............................................................................. + // create new query + // ............................................................................. + v8::Handle result = QueryTempl->NewInstance(); - result->SetInternalField(SLOT_TYPE, SelectQueryType); - result->SetInternalField(SLOT_RESULT_SET, v8::Null()); - result->SetInternalField(SLOT_OPERAND, query); + StoreQuery(result, TRI_CreateDocumentQuery(opQuery->clone(opQuery), did)); return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// -/// @brief converts a query into a fluent interface representation +/// @brief limits an existing query +/// +/// limit(number) +/// +/// Limits a result to the first @c number documents. If @c number is negative, +/// the result set is limited to the last @c -number documents. //////////////////////////////////////////////////////////////////////////////// -static v8::Handle ShowQuery (v8::Arguments const& argv) { +static v8::Handle JS_LimitQuery (v8::Arguments const& argv) { v8::HandleScope scope; - v8::Handle self = argv.Holder(); + // extract the operand query + v8::Handle operand = argv.Holder(); + v8::Handle err; + TRI_query_t* opQuery = ExtractQuery(operand, &err); - string result = StringifyQuery(argv.Holder()); - - if (QueryExecuted(self)) { - result += ".execute()"; + if (opQuery == 0) { + return scope.Close(v8::ThrowException(err)); } - return scope.Close(v8::String::New(result.c_str())); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief skips an existing query -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle SkipQuery (v8::Arguments const& argv) { - v8::HandleScope scope; - - v8::Handle query = argv.Holder(); - - // query should be un-executed - if (QueryExecuted(query)) { + if (IsExecutedQuery(operand)) { return scope.Close(v8::ThrowException(v8::String::New("query already executed"))); } // first and only argument schould be an integer - if (argv.Length() < 1) { - return scope.Close(v8::ThrowException(v8::String::New("usage: skip()"))); + if (argv.Length() != 1) { + return scope.Close(v8::ThrowException(v8::String::New("usage: limit()"))); } - v8::Handle arg1 = argv[0]; + double limit = ObjectToDouble(argv[0]); - if (! arg1->IsNumber()) { - return scope.Close(v8::ThrowException(v8::Exception::TypeError(v8::String::New(" must be a number")))); - } - - double skip = arg1->ToNumber()->Value(); + // ............................................................................. + // create new query + // ............................................................................. v8::Handle result = QueryTempl->NewInstance(); - result->SetInternalField(SLOT_TYPE, SkipQueryType); - result->SetInternalField(SLOT_RESULT_SET, v8::Null()); - result->SetInternalField(SLOT_OPERAND, query); - result->SetInternalField(SLOT_SKIP, v8::Uint32::New(skip)); + StoreQuery(result, TRI_CreateLimitQuery(opQuery->clone(opQuery), (TRI_voc_ssize_t) limit)); return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// -/// @brief calls the "printQuery" function +/// @brief selects elements by example +/// +/// The "select" operator finds all documents which match a given example. +/// +/// select() +/// +/// Selects all documents. //////////////////////////////////////////////////////////////////////////////// -static v8::Handle PrintQuery (v8::Arguments const& argv) { +static v8::Handle JS_SelectQuery (v8::Arguments const& argv) { v8::HandleScope scope; - v8::Handle self = argv.Holder(); - v8::Handle print = v8::Handle::Cast(self->CreationContext()->Global()->Get(PrintQueryFuncName)); - - v8::Handle args[] = { self }; - print->Call(print, 1, args); - - return scope.Close(v8::Undefined()); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief executes a query and returns the result als array -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle ToArrayQuery (v8::Arguments const& argv) { - v8::HandleScope scope; - v8::Handle self = argv.Holder(); + // extract the operand query + v8::Handle operand = argv.Holder(); v8::Handle err; + TRI_query_t* opQuery = ExtractQuery(operand, &err); - v8::Handle q = CheckAndExecuteQuery(argv.Holder(), &err); - - if (q->IsUndefined()) { + if (opQuery == 0) { return scope.Close(v8::ThrowException(err)); } - TRI_result_set_t* rs = UnwrapClass(q->ToObject()); - - if (rs == 0) { - return scope.Close(v8::ThrowException(v8::String::New("cannot execute query"))); + if (IsExecutedQuery(operand)) { + return scope.Close(v8::ThrowException(v8::String::New("query already executed"))); } - else { - v8::Handle result = ArrayResultSet(rs); + + // ............................................................................. + // case: no arguments + // ............................................................................. + + if (argv.Length() == 0) { + v8::Handle result = QueryTempl->NewInstance(); + + StoreQuery(result, opQuery->clone(opQuery)); return scope.Close(result); } + + // ............................................................................. + // error case + // ............................................................................. + + else { + return scope.Close(v8::ThrowException(v8::String::New("usage: select([])"))); + } } //////////////////////////////////////////////////////////////////////////////// -/// @} +/// @brief skips an existing query +/// +/// skip(number) +/// +/// Skips the first @c number documents. //////////////////////////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------- -// --SECTION-- TRI_RESULT_SET_T FUNCTIONS -// ----------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -// --SECTION-- private functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup VocBasePrivate VocBase (Private) -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a TRI_result_set_t into a string -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle ToStringResultSet (v8::Arguments const& argv) { +static v8::Handle JS_SkipQuery (v8::Arguments const& argv) { v8::HandleScope scope; - string name = "[result-set]"; + // extract the operand query + v8::Handle operand = argv.Holder(); + v8::Handle err; + TRI_query_t* opQuery = ExtractQuery(operand, &err); - return scope.Close(v8::String::New(name.c_str())); + if (opQuery == 0) { + return scope.Close(v8::ThrowException(err)); + } + + if (IsExecutedQuery(operand)) { + return scope.Close(v8::ThrowException(v8::String::New("query already executed"))); + } + + // first and only argument schould be an integer + if (argv.Length() != 1) { + return scope.Close(v8::ThrowException(v8::String::New("usage: skip()"))); + } + + double skip = ObjectToDouble(argv[0]); + + if (skip < 0.0) { + skip = 0.0; + } + + // ............................................................................. + // create new query + // ............................................................................. + + v8::Handle result = QueryTempl->NewInstance(); + + StoreQuery(result, TRI_CreateSkipQuery(opQuery->clone(opQuery), (TRI_voc_size_t) skip)); + + return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// @@ -2391,7 +1155,7 @@ static v8::Handle ToStringResultSet (v8::Arguments const& argv) { // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- -// --SECTION-- private functions +// --SECTION-- javascript functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// @@ -2400,10 +1164,29 @@ static v8::Handle ToStringResultSet (v8::Arguments const& argv) { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief converts a TRI_vocbase_col_t into a string +/// @brief ensures that a geo index exists +/// +/// ensureGeoIndex(location) +/// +/// Creates a geo-spatial index on all documents using @c location as path the +/// coordinates. The value of the attribute must be a list with at least two +/// double values. All documents, which do not have the attribute path or which +/// value is not suitable, are ignored. +/// +/// @verbinclude fluent10 +/// +/// ensureGeoIndex(latitiude,longitude) +/// +/// Creates a geo-spatial index on all documents using @c latitiude and @c +/// longitude as paths the latitiude and the longitude. The value of the +/// attribute @c latitiude and of the attribute @c longitude must a double. All +/// documents, which do not have the attribute paths or which values are not +/// suitable, are ignored. +/// +/// @verbinclude fluent14 //////////////////////////////////////////////////////////////////////////////// -static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv) { +static v8::Handle JS_EnsureGeoIndexVocbaseCol (v8::Arguments const& argv) { v8::HandleScope scope; v8::Handle err; @@ -2421,6 +1204,10 @@ static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv TRI_sim_collection_t* sim = (TRI_sim_collection_t*) collection; + // ............................................................................. + // case: + // ............................................................................. + if (argv.Length() == 1) { v8::String::Utf8Value loc(argv[0]); @@ -2430,6 +1217,11 @@ static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv TRI_EnsureGeoIndexSimCollection(sim, *loc); } + + // ............................................................................. + // case: , + // ............................................................................. + else if (argv.Length() == 2) { v8::String::Utf8Value lat(argv[0]); v8::String::Utf8Value lon(argv[1]); @@ -2444,6 +1236,11 @@ static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv TRI_EnsureGeoIndex2SimCollection(sim, *lat, *lon); } + + // ............................................................................. + // error case + // ............................................................................. + else { return scope.Close(v8::ThrowException(v8::String::New("usage: ensureGeoIndex(, ) or ensureGeoIndex()"))); } @@ -2452,58 +1249,205 @@ static v8::Handle EnsureGeoIndexVocbaseCol (v8::Arguments const& argv } //////////////////////////////////////////////////////////////////////////////// -/// @brief limits an existing query +/// @brief deletes a document //////////////////////////////////////////////////////////////////////////////// -static v8::Handle NearVocbaseCol (v8::Arguments const& argv) { +static v8::Handle JS_DeleteVocbaseCol (v8::Arguments const& argv) { v8::HandleScope scope; - v8::Handle query = argv.Holder(); + v8::Handle err; + TRI_vocbase_col_t const* col = LoadCollection(argv.Holder(), &err); - // query should be un-executed - if (QueryExecuted(query)) { + if (col == 0) { + return scope.Close(v8::ThrowException(err)); + } + + TRI_doc_collection_t* collection = col->_collection; + + if (argv.Length() != 1) { + return scope.Close(v8::ThrowException(v8::String::New("usage: delete()"))); + } + + TRI_voc_cid_t cid; + TRI_voc_did_t did; + + if (IsDocumentId(argv[0], cid, did)) { + if (cid != 0 && cid != collection->base._cid) { + return scope.Close(v8::ThrowException(v8::String::New("cannot execute cross collection delete"))); + } + } + else { + return scope.Close(v8::ThrowException(v8::String::New("expecting a document identifier"))); + } + + // ............................................................................. + // inside a write transaction + // ............................................................................. + + collection->beginWrite(collection); + + bool ok = collection->destroy(collection, did); + + collection->endWrite(collection); + + // ............................................................................. + // outside a write transaction + // ............................................................................. + + if (! ok) { + string err = "cannot save document: "; + err += TRI_last_error(); + + return scope.Close(v8::ThrowException(v8::String::New(err.c_str()))); + } + + return scope.Close(v8::Undefined()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief finds points near a given coordinate +/// +/// The near operator is rather complex, because there are two variants (list or +/// array) and some optional parameters. If you have defined only one geo-index, +/// however, the call is simply +/// +/// near(latitiude, longitude) +/// +/// This will find at most 100 documents near the coordinate (@c latitiude, @c +/// longitude). The returned list is sorted according to the distance, with the +/// nearest document coming first. +/// +/// @verbinclude fluent10 +/// +/// If you need the distance as well, then you can use +/// +/// near(latitiude, longitude).distance() +/// +/// This will add an attribute "_distance" to all documents returned, which +/// contains the distance of the given point and the document in meter. +/// +/// @verbinclude fluent11 +/// +/// near(latitiude, longitude).distance(name) +/// +/// This will add an attribute "name" to all documents returned, which +/// contains the distance of the given point and the document in meter. +/// +/// @verbinclude fluent12 +/// +/// near(latitiude, longitude).limit(count) +/// +/// Limits the result to @c count documents. Note that @c count can be more than +/// 100. To get less or more than 100 documents with distances, use +/// +/// near(latitiude, longitude).distance().limit(count) +/// +/// This will return the first @c count documents together with their distances +/// in meters. +/// +/// @verbinclude fluent13 +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_NearVocbaseCol (v8::Arguments const& argv) { + static size_t const DEFAULT_LIMIT = 100; + + v8::HandleScope scope; + + // extract the operand query + v8::Handle operand = argv.Holder(); + v8::Handle err; + TRI_query_t* opQuery = ExtractQuery(operand, &err); + + if (opQuery == 0) { + return scope.Close(v8::ThrowException(err)); + } + + if (IsExecutedQuery(operand)) { return scope.Close(v8::ThrowException(v8::String::New("query already executed"))); } - // either ,,, or ,,,, - if (argv.Length() < 4) { - return scope.Close(v8::ThrowException(v8::String::New("usage: near([,,|,,,],)"))); - } + // ............................................................................. + // near(latitiude, longitude) + // ............................................................................. + + if (argv.Length() == 2) { + double latitiude = ObjectToDouble(argv[0]); + double longitude = ObjectToDouble(argv[1]); - if (argv.Length() == 4) { v8::Handle result = QueryTempl->NewInstance(); - result->SetInternalField(SLOT_TYPE, NearQueryType); - result->SetInternalField(SLOT_RESULT_SET, v8::Null()); - result->SetInternalField(SLOT_OPERAND, query); - result->SetInternalField(SLOT_LOCATION, argv[0]); - result->SetInternalField(SLOT_LATITUDE, argv[1]); - result->SetInternalField(SLOT_LONGITUDE, argv[2]); - result->SetInternalField(SLOT_LIMIT, argv[3]); + StoreQuery(result, TRI_CreateNearQuery(opQuery->clone(opQuery), latitiude, longitude, DEFAULT_LIMIT, NULL)); return scope.Close(result); } + + return scope.Close(v8::Undefined()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds distance to a result set +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_DistanceVocbaseCol (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract the operand query + v8::Handle operand = argv.Holder(); + v8::Handle err; + TRI_query_t* opQuery = ExtractQuery(operand, &err); + + if (opQuery == 0) { + return scope.Close(v8::ThrowException(err)); + } + + if (IsExecutedQuery(operand)) { + return scope.Close(v8::ThrowException(v8::String::New("query already executed"))); + } + + // ............................................................................. + // case: no arguments + // ............................................................................. + + if (argv.Length() == 0) { + v8::Handle result = QueryTempl->NewInstance(); + + StoreQuery(result, TRI_CreateDistanceQuery(opQuery->clone(opQuery), "_distance")); + + return scope.Close(result); + } + + // ............................................................................. + // case: + // ............................................................................. + + else if (argv.Length() == 1) { + string name = ObjectToString(argv[0]); + + if (name.empty()) { + return scope.Close(v8::ThrowException(v8::String::New(" must be non-empty"))); + } + + v8::Handle result = QueryTempl->NewInstance(); + + StoreQuery(result, TRI_CreateDistanceQuery(opQuery->clone(opQuery), name.c_str())); + + return scope.Close(result); + } + + // ............................................................................. + // error case + // ............................................................................. + else { - v8::Handle result = QueryTempl->NewInstance(); - - result->SetInternalField(SLOT_TYPE, Near2QueryType); - result->SetInternalField(SLOT_RESULT_SET, v8::Null()); - result->SetInternalField(SLOT_OPERAND, query); - result->SetInternalField(SLOT_LOC_LATITUDE, argv[0]); - result->SetInternalField(SLOT_LATITUDE, argv[1]); - result->SetInternalField(SLOT_LOC_LONGITUDE, argv[2]); - result->SetInternalField(SLOT_LONGITUDE, argv[3]); - result->SetInternalField(SLOT_LIMIT, argv[4]); - - return scope.Close(result); + return scope.Close(v8::ThrowException(v8::String::New("usage: distance([])"))); } } //////////////////////////////////////////////////////////////////////////////// -/// @brief gets or sets the parameters +/// @brief gets or sets the parameters of a collection //////////////////////////////////////////////////////////////////////////////// -static v8::Handle ParameterVocbaseCol (v8::Arguments const& argv) { +static v8::Handle JS_ParameterVocbaseCol (v8::Arguments const& argv) { v8::HandleScope scope; v8::Handle err; @@ -2604,11 +1548,75 @@ static v8::Handle ParameterVocbaseCol (v8::Arguments const& argv) { return scope.Close(result); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief replaces a document +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_ReplaceVocbaseCol (v8::Arguments const& argv) { + v8::HandleScope scope; + + v8::Handle err; + TRI_vocbase_col_t const* col = LoadCollection(argv.Holder(), &err); + + if (col == 0) { + return scope.Close(v8::ThrowException(err)); + } + + TRI_doc_collection_t* collection = col->_collection; + + if (argv.Length() != 2) { + return scope.Close(v8::ThrowException(v8::String::New("usage: replace(, )"))); + } + + TRI_voc_cid_t cid; + TRI_voc_did_t did; + + if (IsDocumentId(argv[0], cid, did)) { + if (cid != 0 && cid != collection->base._cid) { + return scope.Close(v8::ThrowException(v8::String::New("cannot execute cross collection update"))); + } + } + else { + return scope.Close(v8::ThrowException(v8::String::New("expecting a document identifier"))); + } + + TRI_shaped_json_t* shaped = ShapedJsonV8Object(argv[1], collection->_shaper); + + if (shaped == 0) { + return scope.Close(v8::ThrowException(v8::String::New(" cannot be converted into JSON shape"))); + } + + // ............................................................................. + // inside a write transaction + // ............................................................................. + + collection->beginWrite(collection); + + bool ok = collection->update(collection, shaped, did); + + collection->endWrite(collection); + + // ............................................................................. + // outside a write transaction + // ............................................................................. + + TRI_FreeShapedJson(shaped); + + if (! ok) { + string err = "cannot save document: "; + err += TRI_last_error(); + + return scope.Close(v8::ThrowException(v8::String::New(err.c_str()))); + } + + return scope.Close(v8::Undefined()); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief saves a new document //////////////////////////////////////////////////////////////////////////////// -static v8::Handle SaveVocbaseCol (v8::Arguments const& argv) { +static v8::Handle JS_SaveVocbaseCol (v8::Arguments const& argv) { v8::HandleScope scope; v8::Handle err; @@ -2624,35 +1632,28 @@ static v8::Handle SaveVocbaseCol (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(v8::String::New("usage: save()"))); } - v8::Handle val = argv[0]; + TRI_shaped_json_t* shaped = ShapedJsonV8Object(argv[0], collection->_shaper); - TRI_shape_value_t dst; - bool ok = FillShapeValueJson(collection->_shaper, &dst, val); - - if (! ok) { + if (shaped == 0) { return scope.Close(v8::ThrowException(v8::String::New(" cannot be converted into JSON shape"))); } - TRI_shaped_json_t shaped; - - shaped._sid = dst._sid; - shaped._data.length = dst._size; - shaped._data.data = dst._value; - // ............................................................................. - // inside a read transaction + // inside a write transaction // ............................................................................. collection->beginWrite(collection); - TRI_voc_did_t did = collection->create(collection, &shaped); + TRI_voc_did_t did = collection->create(collection, shaped); collection->endWrite(collection); // ............................................................................. - // outside a read transaction + // outside a write transaction // ............................................................................. + TRI_FreeShapedJson(shaped); + if (did == 0) { string err = "cannot save document: "; err += TRI_last_error(); @@ -2660,8 +1661,6 @@ static v8::Handle SaveVocbaseCol (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(v8::String::New(err.c_str()))); } - TRI_Free(dst._value); - char* cidStr = TRI_StringUInt64(collection->base._cid); char* didStr = TRI_StringUInt64(did); @@ -2677,7 +1676,7 @@ static v8::Handle SaveVocbaseCol (v8::Arguments const& argv) { /// @brief converts a TRI_vocbase_col_t into a string //////////////////////////////////////////////////////////////////////////////// -static v8::Handle ToStringVocbaseCol (v8::Arguments const& argv) { +static v8::Handle JS_ToStringVocbaseCol (v8::Arguments const& argv) { v8::HandleScope scope; TRI_vocbase_col_t const* collection = UnwrapClass(argv.Holder()); @@ -2721,24 +1720,6 @@ static v8::Handle ToStringVocbaseCol (v8::Arguments const& argv) { /// @{ //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @brief converts a TRI_vocbase_t into a string -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle ToStringVocBase (v8::Arguments const& argv) { - v8::HandleScope scope; - - TRI_vocbase_t* vocbase = UnwrapClass(argv.Holder()); - - if (vocbase == 0) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); - } - - string name = "[vocbase at \"" + string(vocbase->_path) + "\"]"; - - return scope.Close(v8::String::New(name.c_str())); -} - //////////////////////////////////////////////////////////////////////////////// /// @brief selects a collection from the vocbase //////////////////////////////////////////////////////////////////////////////// @@ -2763,10 +1744,17 @@ static v8::Handle MapGetVocBase (v8::Local name, return scope.Close(v8::ThrowException(v8::String::New("cannot load or create collection"))); } + // ............................................................................. // otherwise wrap collection into V8 object + // ............................................................................. + v8::Handle result = WrapClass(VocbaseColTempl, const_cast(collection)); - result->SetInternalField(SLOT_TYPE, CollectionQueryType); + // ............................................................................. + // create new query + // ............................................................................. + + result->SetInternalField(SLOT_QUERY, CollectionQueryType); result->SetInternalField(SLOT_RESULT_SET, v8::Null()); return scope.Close(result); @@ -2776,6 +1764,41 @@ static v8::Handle MapGetVocBase (v8::Local name, /// @} //////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- javascript functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBasePrivate VocBase (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts a TRI_vocbase_t into a string +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_ToStringVocBase (v8::Arguments const& argv) { + v8::HandleScope scope; + + TRI_vocbase_t* vocbase = UnwrapClass(argv.Holder()); + + if (vocbase == 0) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); + } + + string name = "[vocbase at \"" + string(vocbase->_path) + "\"]"; + + return scope.Close(v8::String::New(name.c_str())); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- MODULE +// ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- @@ -2814,103 +1837,124 @@ v8::Persistent InitV8VocBridge (TRI_vocbase_t* vocbase) { context->Enter(); - // create the various strings nedded - CountFuncName = v8::Persistent::New(v8::String::New("count")); - DocumentFuncName = v8::Persistent::New(v8::String::New("document")); - EnsureGeoIndexFuncName = v8::Persistent::New(v8::String::New("ensureGeoIndex")); - ExplainFuncName = v8::Persistent::New(v8::String::New("explain")); - HasNextFuncName = v8::Persistent::New(v8::String::New("hasNext")); - LimitFuncName = v8::Persistent::New(v8::String::New("limit")); - NearFuncName = v8::Persistent::New(v8::String::New("near")); - NextFuncName = v8::Persistent::New(v8::String::New("next")); + // ............................................................................. + // global function names + // ............................................................................. + OutputFuncName = v8::Persistent::New(v8::String::New("output")); - ParameterFuncName = v8::Persistent::New(v8::String::New("parameter")); - PrintFuncName = v8::Persistent::New(v8::String::New("print")); PrintQueryFuncName = v8::Persistent::New(v8::String::New("printQuery")); - SaveFuncName = v8::Persistent::New(v8::String::New("save")); - SelectFuncName = v8::Persistent::New(v8::String::New("select")); - ShowFuncName = v8::Persistent::New(v8::String::New("show")); - SkipFuncName = v8::Persistent::New(v8::String::New("skip")); - ToArrayFuncName = v8::Persistent::New(v8::String::New("toArray")); ToStringFuncName = v8::Persistent::New(v8::String::New("toString")); - CollectionQueryType = v8::Persistent::New(v8::String::New("collection")); - DocumentQueryType = v8::Persistent::New(v8::String::New("document")); - LimitQueryType = v8::Persistent::New(v8::String::New("limit")); - Near2QueryType = v8::Persistent::New(v8::String::New("near2")); - NearQueryType = v8::Persistent::New(v8::String::New("near")); - SelectQueryType = v8::Persistent::New(v8::String::New("select")); - SkipQueryType = v8::Persistent::New(v8::String::New("skip")); + // ............................................................................. + // local function names + // ............................................................................. + + v8::Handle AllFuncName = v8::Persistent::New(v8::String::New("all")); + v8::Handle CountFuncName = v8::Persistent::New(v8::String::New("count")); + v8::Handle DeleteFuncName = v8::Persistent::New(v8::String::New("delete")); + v8::Handle DistanceFuncName = v8::Persistent::New(v8::String::New("distance")); + v8::Handle DocumentFuncName = v8::Persistent::New(v8::String::New("document")); + v8::Handle EnsureGeoIndexFuncName = v8::Persistent::New(v8::String::New("ensureGeoIndex")); + v8::Handle ExplainFuncName = v8::Persistent::New(v8::String::New("explain")); + v8::Handle HasNextFuncName = v8::Persistent::New(v8::String::New("hasNext")); + v8::Handle LimitFuncName = v8::Persistent::New(v8::String::New("limit")); + v8::Handle NearFuncName = v8::Persistent::New(v8::String::New("near")); + v8::Handle NextFuncName = v8::Persistent::New(v8::String::New("next")); + v8::Handle ParameterFuncName = v8::Persistent::New(v8::String::New("parameter")); + v8::Handle PrintFuncName = v8::Persistent::New(v8::String::New("print")); + v8::Handle ReplaceFuncName = v8::Persistent::New(v8::String::New("replace")); + v8::Handle SaveFuncName = v8::Persistent::New(v8::String::New("save")); + v8::Handle SelectFuncName = v8::Persistent::New(v8::String::New("select")); + v8::Handle ShowFuncName = v8::Persistent::New(v8::String::New("show")); + v8::Handle SkipFuncName = v8::Persistent::New(v8::String::New("skip")); + v8::Handle ToArrayFuncName = v8::Persistent::New(v8::String::New("toArray")); + + // ............................................................................. + // query types + // ............................................................................. + + CollectionQueryType = v8::Persistent::New(v8::String::New("collection")); + + // ............................................................................. + // keys + // ............................................................................. - DidKey = v8::Persistent::New(v8::String::New("_id")); JournalSizeKey = v8::Persistent::New(v8::String::New("journalSize")); SyncAfterBytesKey = v8::Persistent::New(v8::String::New("syncAfterBytes")); SyncAfterObjectsKey = v8::Persistent::New(v8::String::New("syncAfterObjects")); SyncAfterTimeKey = v8::Persistent::New(v8::String::New("syncAfterTime")); + // ............................................................................. // generate the TRI_vocbase_t template + // ............................................................................. + rt = v8::ObjectTemplate::New(); rt->SetInternalFieldCount(1); - rt->Set(PrintFuncName, v8::FunctionTemplate::New(PrintUsingToString)); - rt->Set(ToStringFuncName, v8::FunctionTemplate::New(ToStringVocBase)); + rt->Set(PrintFuncName, v8::FunctionTemplate::New(JS_PrintUsingToString)); + rt->Set(ToStringFuncName, v8::FunctionTemplate::New(JS_ToStringVocBase)); rt->SetNamedPropertyHandler(MapGetVocBase); - VocBaseTempl = v8::Persistent::New(rt); + VocbaseTempl = v8::Persistent::New(rt); + // ............................................................................. // generate the TRI_vocbase_col_t template - rt = v8::ObjectTemplate::New(); + // ............................................................................. - rt->SetInternalFieldCount(5); - - rt->Set(CountFuncName, v8::FunctionTemplate::New(CountQuery)); - rt->Set(DocumentFuncName, v8::FunctionTemplate::New(DocumentQuery)); - rt->Set(EnsureGeoIndexFuncName, v8::FunctionTemplate::New(EnsureGeoIndexVocbaseCol)); - rt->Set(ExplainFuncName, v8::FunctionTemplate::New(ExplainQuery)); - rt->Set(LimitFuncName, v8::FunctionTemplate::New(LimitQuery)); - rt->Set(NearFuncName, v8::FunctionTemplate::New(NearVocbaseCol)); - rt->Set(ParameterFuncName, v8::FunctionTemplate::New(ParameterVocbaseCol)); - rt->Set(PrintFuncName, v8::FunctionTemplate::New(PrintUsingToString)); - rt->Set(SaveFuncName, v8::FunctionTemplate::New(SaveVocbaseCol)); - rt->Set(SelectFuncName, v8::FunctionTemplate::New(SelectQuery)); - rt->Set(SkipFuncName, v8::FunctionTemplate::New(SkipQuery)); - rt->Set(ToArrayFuncName, v8::FunctionTemplate::New(ToArrayQuery)); - rt->Set(ToStringFuncName, v8::FunctionTemplate::New(ToStringVocbaseCol)); - - VocbaseColTempl = v8::Persistent::New(rt); - - // generate the query template rt = v8::ObjectTemplate::New(); rt->SetInternalFieldCount(SLOT_END); - rt->Set(CountFuncName, v8::FunctionTemplate::New(CountQuery)); - rt->Set(DocumentFuncName, v8::FunctionTemplate::New(DocumentQuery)); - rt->Set(ExplainFuncName, v8::FunctionTemplate::New(ExplainQuery)); - rt->Set(HasNextFuncName, v8::FunctionTemplate::New(HasNextQuery)); - rt->Set(LimitFuncName, v8::FunctionTemplate::New(LimitQuery)); - rt->Set(NextFuncName, v8::FunctionTemplate::New(NextQuery)); - rt->Set(PrintFuncName, v8::FunctionTemplate::New(PrintQuery)); - rt->Set(SelectFuncName, v8::FunctionTemplate::New(SelectQuery)); - rt->Set(ShowFuncName, v8::FunctionTemplate::New(ShowQuery)); - rt->Set(SkipFuncName, v8::FunctionTemplate::New(SkipQuery)); - rt->Set(ToArrayFuncName, v8::FunctionTemplate::New(ToArrayQuery)); + rt->Set(AllFuncName, v8::FunctionTemplate::New(JS_AllQuery)); + rt->Set(CountFuncName, v8::FunctionTemplate::New(JS_CountQuery)); + rt->Set(DeleteFuncName, v8::FunctionTemplate::New(JS_DeleteVocbaseCol)); + rt->Set(DocumentFuncName, v8::FunctionTemplate::New(JS_DocumentQuery)); + rt->Set(EnsureGeoIndexFuncName, v8::FunctionTemplate::New(JS_EnsureGeoIndexVocbaseCol)); + rt->Set(ExplainFuncName, v8::FunctionTemplate::New(JS_ExplainQuery)); + rt->Set(LimitFuncName, v8::FunctionTemplate::New(JS_LimitQuery)); + rt->Set(NearFuncName, v8::FunctionTemplate::New(JS_NearVocbaseCol)); + rt->Set(ParameterFuncName, v8::FunctionTemplate::New(JS_ParameterVocbaseCol)); + rt->Set(PrintFuncName, v8::FunctionTemplate::New(JS_PrintUsingToString)); + rt->Set(ReplaceFuncName, v8::FunctionTemplate::New(JS_ReplaceVocbaseCol)); + rt->Set(SaveFuncName, v8::FunctionTemplate::New(JS_SaveVocbaseCol)); + rt->Set(SelectFuncName, v8::FunctionTemplate::New(JS_SelectQuery)); + rt->Set(SkipFuncName, v8::FunctionTemplate::New(JS_SkipQuery)); + rt->Set(ToArrayFuncName, v8::FunctionTemplate::New(JS_ToArrayQuery)); + rt->Set(ToStringFuncName, v8::FunctionTemplate::New(JS_ToStringVocbaseCol)); + + VocbaseColTempl = v8::Persistent::New(rt); + + // ............................................................................. + // generate the query template + // ............................................................................. + + rt = v8::ObjectTemplate::New(); + + rt->SetInternalFieldCount(SLOT_END); + + rt->Set(AllFuncName, v8::FunctionTemplate::New(JS_AllQuery)); + rt->Set(CountFuncName, v8::FunctionTemplate::New(JS_CountQuery)); + rt->Set(DistanceFuncName, v8::FunctionTemplate::New(JS_DistanceVocbaseCol)); + rt->Set(DocumentFuncName, v8::FunctionTemplate::New(JS_DocumentQuery)); + rt->Set(ExplainFuncName, v8::FunctionTemplate::New(JS_ExplainQuery)); + rt->Set(HasNextFuncName, v8::FunctionTemplate::New(JS_HasNextQuery)); + rt->Set(LimitFuncName, v8::FunctionTemplate::New(JS_LimitQuery)); + rt->Set(NextFuncName, v8::FunctionTemplate::New(JS_NextQuery)); + rt->Set(PrintFuncName, v8::FunctionTemplate::New(JS_PrintQuery)); + rt->Set(SelectFuncName, v8::FunctionTemplate::New(JS_SelectQuery)); + rt->Set(ShowFuncName, v8::FunctionTemplate::New(JS_ShowQuery)); + rt->Set(SkipFuncName, v8::FunctionTemplate::New(JS_SkipQuery)); + rt->Set(ToArrayFuncName, v8::FunctionTemplate::New(JS_ToArrayQuery)); QueryTempl = v8::Persistent::New(rt); - // generate the result-set template - rt = v8::ObjectTemplate::New(); - - rt->SetInternalFieldCount(1); - - rt->Set(ToStringFuncName, v8::FunctionTemplate::New(ToStringResultSet)); - - ResultSetTempl = v8::Persistent::New(rt); - + // ............................................................................. // create the global variables + // ............................................................................. + context->Global()->Set(v8::String::New("db"), - WrapClass(VocBaseTempl, vocbase), + WrapClass(VocbaseTempl, vocbase), v8::ReadOnly); // and return the context diff --git a/V8/v8-vocbase.h b/V8/v8-vocbase.h index ead0c9373a..e1ebcbde3e 100644 --- a/V8/v8-vocbase.h +++ b/V8/v8-vocbase.h @@ -35,6 +35,129 @@ #include +//////////////////////////////////////////////////////////////////////////////// +/// @page DurhamFluentInterface Fluent Interface +/// +/// A fluent interface is implemented by using method chaining to relay the +/// instruction context of a subsequent call. The AvocadoDB provides the +/// following methods: +/// +/// - selection by example +/// - field selection (aka projection) +/// - @ref GeoFI "geo coordinates" +/// - sorting +/// - cursors +/// - pagination of the result-set +/// +//////////////////////////////////////////////////////////////////////////////// +/// @section FirstStepsFI First Steps +//////////////////////////////////////////////////////////////////////////////// +/// +/// For instance, in order to select all elements of a collection "examples", +/// one can use the "all()" operator. +/// +/// @verbinclude fluent1 +/// +/// This will select all documents and prints the first 20 documents. If there +/// are more than 20 documents, then "...more results..." is printed and you +/// can use the variable "it" to access the next 20 document. +/// +/// @verbinclude fluent2 +/// +/// In the above examples "db.examples.all()" defines a query. Printing +/// that query, executes the query and returns a cursor to the result set. +/// The first 20 documents are printed and the query (resp. cursor) is +/// assigned to the variable "it". +/// +/// A cursor can also be queried using "hasNext()" and "next()". Calling +/// either of these functions also executes the query, turning it into +/// a cursor. +/// +/// @verbinclude fluent3 +/// +//////////////////////////////////////////////////////////////////////////////// +/// @section GeoFI Geo Coordinates +//////////////////////////////////////////////////////////////////////////////// +/// +/// The AvocadoDB allows to selects documents based on geographic +/// coordinates. In order for this to work a geo-spatial index must be defined. +/// This index will use a very elaborate algorithm to lookup neighbours that is +/// a magnitude faster than a simple R* index. +/// +/// In general a geo coordinate is a pair of latitude and longitude. This can +/// either be an list with two elements like "[ -10, +30 ]" (latitude first, +/// followed by longitude) or an object like "{ lon: -10, lat: +30 }". In order +/// to find all documents within a given radius around a coordinate use the @ref +/// WithinFI "within()" operator. In order to find all documents near a given +/// document use the @ref NearFI "near()" operator. +/// +/// It is possible to define more than one geo-spatial index per collection. In +/// this case you must give a hint which of indexes should be used in a query. +/// +//////////////////////////////////////////////////////////////////////////////// +/// @subsection EnsureGeoIndexFI Create a Geo-Spatial Index +//////////////////////////////////////////////////////////////////////////////// +/// +/// @copydetails JS_EnsureGeoIndexVocbaseCol +/// +//////////////////////////////////////////////////////////////////////////////// +/// @subsection NearFI The Near Operator +//////////////////////////////////////////////////////////////////////////////// +/// +/// @copydetails JS_NearVocbaseCol +/// +//////////////////////////////////////////////////////////////////////////////// +/// @subsection WithinFI The Within Operator +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +/// @section LimitFI The Limit Operator +//////////////////////////////////////////////////////////////////////////////// +/// +/// If, for example, you display the result of a user search, then you are in +/// general not interested in the completed result set, but only the first 10 +/// documents. In this case, you can the "limit()" operator. This operators +/// works like LIMIT in MySQL, it specifies a maximal number of documents to +/// return. +/// +/// @verbinclude fluent4 +/// +/// Specifying a limit of "0" returns no documents at all. If you do not need +/// a limit, just do not add the limit operator. If you specifiy a negtive +/// limit of -n, this will return the last n documents instead. +/// +/// @verbinclude fluent8 +/// +//////////////////////////////////////////////////////////////////////////////// +/// @section SkipFI The Skip Operator +//////////////////////////////////////////////////////////////////////////////// +/// +/// "skip" used together with "limit" can be used to implement pagination. +/// The "skip" operator skips over the first n documents. So, in order to +/// create result pages with 10 result documents per page, you can use +/// "skip(n * 10).limit(10)" to access the n.th page. +/// +/// @verbinclude fluent5 +/// +//////////////////////////////////////////////////////////////////////////////// +/// @section CountFI The Count Operator +//////////////////////////////////////////////////////////////////////////////// +/// +/// If you are implementation pagination, then you need the total amount of +/// documents a query returned. "count()" just does this. It returns the +/// total amount of documents regardless of any "limit()" and "skip()" +/// operator used before the "count()". If you really want these to be taken +/// into account, use "count(true)". +/// +/// @verbinclude fluent6 +/// +//////////////////////////////////////////////////////////////////////////////// +/// @section ExplainFI The Explain Operator +//////////////////////////////////////////////////////////////////////////////// +/// +/// @copydetails JS_ExplainQuery +//////////////////////////////////////////////////////////////////////////////// + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- diff --git a/VocBase/index.c b/VocBase/index.c index fbe5b71a52..f350d212af 100644 --- a/VocBase/index.c +++ b/VocBase/index.c @@ -249,7 +249,84 @@ static bool InsertGeoIndex (TRI_index_t* idx, TRI_doc_mptr_t const* doc) { shaper = geo->base._collection->_shaper; // lookup latitude and longitude - ok = ExtractDoubleList(shaper, &doc->_document, geo->_location, &latitude, &longitude); + if (geo->_location != 0) { + ok = ExtractDoubleList(shaper, &doc->_document, geo->_location, &latitude, &longitude); + } + else { + ok = ExtractDoubleArray(shaper, &doc->_document, geo->_latitude, &latitude); + ok = ok && ExtractDoubleArray(shaper, &doc->_document, geo->_longitude, &longitude); + } + + if (! ok) { + return false; + } + + // and insert into index + gc.latitude = latitude; + gc.longitude = longitude; + gc.data = doc; + + res = GeoIndex_insert(geo->_geoIndex, &gc); + + if (res == -1) { + LOG_WARNING("found duplicate entry in geo-index, should not happend"); + } + else if (res == -2) { + LOG_WARNING("out-of-memory in geo-index"); + } + else if (res == -3) { + LOG_DEBUG("illegal geo-coordinates, ignoring entry"); + } + + return res == 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief updates a document, location is a list +//////////////////////////////////////////////////////////////////////////////// + +static bool UpdateGeoIndex (TRI_index_t* idx, TRI_doc_mptr_t const* doc, TRI_shaped_json_t const* old) { + GeoCoordinate gc; + TRI_shaper_t* shaper; + bool ok; + double latitude; + double longitude; + TRI_geo_index_t* geo; + int res; + + geo = (TRI_geo_index_t*) idx; + shaper = geo->base._collection->_shaper; + + // lookup OLD latitude and longitude + if (geo->_location != 0) { + ok = ExtractDoubleList(shaper, old, geo->_location, &latitude, &longitude); + } + else { + ok = ExtractDoubleArray(shaper, old, geo->_latitude, &latitude); + ok = ok && ExtractDoubleArray(shaper, old, geo->_longitude, &longitude); + } + + // and remove old entry + if (ok) { + gc.latitude = latitude; + gc.longitude = longitude; + gc.data = doc; + + res = GeoIndex_remove(geo->_geoIndex, &gc); + + if (res != 0) { + LOG_WARNING("cannot remove old index entry: %d", res); + } + } + + // create new entry with new coordinates + if (geo->_location != 0) { + ok = ExtractDoubleList(shaper, &doc->_document, geo->_location, &latitude, &longitude); + } + else { + ok = ExtractDoubleArray(shaper, &doc->_document, geo->_latitude, &latitude); + ok = ok && ExtractDoubleArray(shaper, &doc->_document, geo->_longitude, &longitude); + } if (! ok) { return false; @@ -274,19 +351,46 @@ static bool InsertGeoIndex (TRI_index_t* idx, TRI_doc_mptr_t const* doc) { return res == 0; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief updates a document, location is a list -//////////////////////////////////////////////////////////////////////////////// - -static bool UpdateGeoIndex (TRI_index_t* idx, TRI_doc_mptr_t const* doc, TRI_shaped_json_t const* old) { - return true; -} - //////////////////////////////////////////////////////////////////////////////// /// @brief erases a document, location is a list //////////////////////////////////////////////////////////////////////////////// -static bool EraseGeoIndex (TRI_index_t* idx, TRI_doc_mptr_t const* doc) { +static bool RemoveGeoIndex (TRI_index_t* idx, TRI_doc_mptr_t const* doc) { + GeoCoordinate gc; + TRI_shaper_t* shaper; + bool ok; + double latitude; + double longitude; + TRI_geo_index_t* geo; + int res; + + geo = (TRI_geo_index_t*) idx; + shaper = geo->base._collection->_shaper; + + // lookup OLD latitude and longitude + if (geo->_location != 0) { + ok = ExtractDoubleList(shaper, &doc->_document, geo->_location, &latitude, &longitude); + } + else { + ok = ExtractDoubleArray(shaper, &doc->_document, geo->_latitude, &latitude); + ok = ok && ExtractDoubleArray(shaper, &doc->_document, geo->_longitude, &longitude); + } + + // and remove old entry + if (ok) { + gc.latitude = latitude; + gc.longitude = longitude; + gc.data = doc; + + res = GeoIndex_remove(geo->_geoIndex, &gc); + + if (res != 0) { + LOG_WARNING("cannot remove old index entry: %d", res); + } + + return res == 0; + } + return true; } @@ -318,55 +422,6 @@ static TRI_json_t* JsonGeoIndex (TRI_index_t* idx, TRI_doc_collection_t* collect return json; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief inserts a new document, location is an array -//////////////////////////////////////////////////////////////////////////////// - -static bool InsertGeoIndex2 (TRI_index_t* idx, TRI_doc_mptr_t const* doc) { - TRI_shaper_t* shaper; - bool ok; - double latitude; - double longitude; - TRI_geo_index_t* geo; - - geo = (TRI_geo_index_t*) idx; - shaper = geo->base._collection->_shaper; - - // lookup latitude - ok = ExtractDoubleArray(shaper, &doc->_document, geo->_latitude, &latitude); - - if (! ok) { - return false; - } - - // lookup longitude - ok = ExtractDoubleArray(shaper, &doc->_document, geo->_longitude, &longitude); - - if (! ok) { - return false; - } - - printf("%f %f\n", latitude, longitude); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief updates a document, location is an array -//////////////////////////////////////////////////////////////////////////////// - -static bool UpdateGeoIndex2 (TRI_index_t* idx, TRI_doc_mptr_t const* doc, TRI_shaped_json_t const* old) { - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief erases a document, location is an array -//////////////////////////////////////////////////////////////////////////////// - -static bool EraseGeoIndex2 (TRI_index_t* idx, TRI_doc_mptr_t const* doc) { - return true; -} - //////////////////////////////////////////////////////////////////////////////// /// @brief saves the index to file, location is an array //////////////////////////////////////////////////////////////////////////////// @@ -489,7 +544,7 @@ TRI_index_t* TRI_CreateGeoIndex (struct TRI_doc_collection_s* collection, geo->base._collection = collection; geo->base.insert = InsertGeoIndex; - geo->base.erase = EraseGeoIndex; + geo->base.remove = RemoveGeoIndex; geo->base.update = UpdateGeoIndex; geo->base.json = JsonGeoIndex; @@ -513,12 +568,13 @@ TRI_index_t* TRI_CreateGeoIndex2 (struct TRI_doc_collection_s* collection, geo = TRI_Allocate(sizeof(TRI_geo_index_t)); + geo->base._iid = TRI_NewTickVocBase(); geo->base._type = TRI_IDX_TYPE_GEO_INDEX; geo->base._collection = collection; - geo->base.insert = InsertGeoIndex2; - geo->base.erase = EraseGeoIndex2; - geo->base.update = UpdateGeoIndex2; + geo->base.insert = InsertGeoIndex; + geo->base.remove = RemoveGeoIndex; + geo->base.update = UpdateGeoIndex; geo->base.json = JsonGeoIndex2; geo->_geoIndex = GeoIndex_new(); diff --git a/VocBase/index.h b/VocBase/index.h index 11c7a9d598..17afc11918 100644 --- a/VocBase/index.h +++ b/VocBase/index.h @@ -81,7 +81,7 @@ typedef struct TRI_index_s { struct TRI_doc_collection_s* _collection; bool (*insert) (struct TRI_index_s*, struct TRI_doc_mptr_s const*); - bool (*erase) (struct TRI_index_s*, struct TRI_doc_mptr_s const*); + bool (*remove) (struct TRI_index_s*, struct TRI_doc_mptr_s const*); bool (*update) (struct TRI_index_s*, struct TRI_doc_mptr_s const*, struct TRI_shaped_json_s const*); TRI_json_t* (*json) (struct TRI_index_s*, struct TRI_doc_collection_s*); } diff --git a/VocBase/query.c b/VocBase/query.c index 29e652ab8d..c0d7d0bddf 100644 --- a/VocBase/query.c +++ b/VocBase/query.c @@ -29,6 +29,8 @@ #include #include +#include +#include #include #include @@ -36,353 +38,986 @@ // --SECTION-- forward declarations // ----------------------------------------------------------------------------- -static TRI_doc_mptr_t const** ExecuteQuery (TRI_query_t* query, - TRI_rs_info_t* info, - TRI_voc_size_t* length, - TRI_voc_size_t* total); +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBasePrivate VocBase (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief frees the old storage of a TRI_query_result_t +//////////////////////////////////////////////////////////////////////////////// + +static void FreeDocuments (TRI_query_result_t* result) { + if (result->_documents != NULL) { + TRI_Free(result->_documents); + result->_documents = NULL; + } + + if (result->_augmented != NULL) { + TRI_Free(result->_augmented); + result->_augmented = NULL; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- COLLECTION QUERY +// ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBasePrivate VocBase (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a full scan +//////////////////////////////////////////////////////////////////////////////// + +static void ExecuteCollectionQuery (TRI_query_t* qry, TRI_query_result_t* result) { + TRI_document_query_t* query; + TRI_doc_mptr_t const** qtr; + TRI_sim_collection_t* collection; + void** end; + void** ptr; + + // extract query + query = (TRI_document_query_t*) qry; + + // sanity check + if (! query->base._collection->_loaded) { + return; + } + + if (query->base._collection->_collection->base._type != TRI_COL_TYPE_SIMPLE_DOCUMENT) { + return; + } + + collection = (TRI_sim_collection_t*) query->base._collection->_collection; + + // append information about the execution plan + TRI_AppendString(&result->_cursor, ">collection"); + + // free any old storage + FreeDocuments(result); + + // add a new document list and copy all documents + result->_total = result->_length = collection->_primaryIndex._nrUsed; + + if (result->_length == 0) { + return; + } + + result->_documents = (qtr = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * (result->_length))); + + ptr = collection->_primaryIndex._table; + end = collection->_primaryIndex._table + collection->_primaryIndex._nrAlloc; + + for (; ptr < end; ++ptr) { + ++result->_scannedDocuments; + + if (*ptr) { + ++result->_matchedDocuments; + *qtr++ = *ptr; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clones a full scan +//////////////////////////////////////////////////////////////////////////////// + +static TRI_query_t* CloneCollectionQuery (TRI_query_t* query) { + return TRI_CreateCollectionQuery(query->_collection); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief frees a full scan +//////////////////////////////////////////////////////////////////////////////// + +static void FreeCollectionQuery (TRI_query_t* query) { + TRI_Free(query->_string); + TRI_Free(query); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- DOCUMENT QUERY +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBasePrivate VocBase (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a document lookup as primary index lookup +//////////////////////////////////////////////////////////////////////////////// + +static void ExecuteDocumentQueryPrimary (TRI_document_query_t* query, + TRI_vocbase_col_t const* collection, + TRI_query_result_t* result) { + TRI_doc_mptr_t const* document; + + // sanity check + if (! query->base._collection->_loaded) { + return; + } + + // look up the document + document = collection->_collection->read(collection->_collection, query->_did); + + // append information about the execution plan + TRI_AppendString(&result->_cursor, ">primary"); + + ++result->_scannedIndexEntries; + + // free any old storage + FreeDocuments(result); + + // the document is the result + if (document == NULL) { + result->_total = result->_length = 0; + } + else { + result->_total = result->_length = 1; + + ++result->_matchedDocuments; + + result->_documents = TRI_Allocate(sizeof(TRI_doc_mptr_t*)); + *result->_documents = document; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a document lookup as full scan +//////////////////////////////////////////////////////////////////////////////// + +static void ExecuteDocumentQueryFullScan (TRI_document_query_t* query, + TRI_query_result_t* result) { + TRI_doc_mptr_t const** ptr; + TRI_doc_mptr_t const** end; + TRI_json_t aug; + size_t pos; + + // execute the sub-query + query->_operand->execute(query->_operand, result); + + if (result->_error) { + return; + } + + // append information about the execution plan + TRI_AppendString(&result->_cursor, ">full-scan[document]"); + + // without any document there will be no match + if (result->_length == 0) { + return; + } + + // do a full scan + pos = 0; + ptr = result->_documents; + end = result->_documents + result->_length; + + for (; ptr < end; ++ptr, ++pos) { + ++result->_scannedDocuments; + + // found a match, create new result + if ((*ptr)->_did == query->_did) { + ++result->_matchedDocuments; + + if (result->_length == 1) { + return; + } + + TRI_Free(result->_documents); + result->_documents = TRI_Allocate(sizeof(TRI_doc_mptr_t*)); + + result->_total = result->_length = 1; + *result->_documents = *ptr; + + if (result->_augmented != NULL) { + aug = result->_augmented[pos]; + + TRI_Free(result->_augmented); + result->_augmented = TRI_Allocate(sizeof(TRI_json_t)); + + result->_augmented[0] = aug; + } + + return; + } + } + + // nothing found + result->_total = result->_length = 0; + + FreeDocuments(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a document lookup +//////////////////////////////////////////////////////////////////////////////// + +static void ExecuteDocumentQuery (TRI_query_t* qry, + TRI_query_result_t* result) { + TRI_document_query_t* query; + + query = (TRI_document_query_t*) qry; + + if (query->_operand != NULL && query->_operand->_type == TRI_QUE_TYPE_COLLECTION) { + ExecuteDocumentQueryPrimary(query, query->_operand->_collection, result); + } + else { + ExecuteDocumentQueryFullScan(query, result); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clones a document lookup +//////////////////////////////////////////////////////////////////////////////// + +static TRI_query_t* CloneDocumentQuery (TRI_query_t* qry) { + TRI_document_query_t* query; + + query = (TRI_document_query_t*) qry; + + return TRI_CreateDocumentQuery(query->_operand->clone(query->_operand), query->_did); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief frees a document lookup +//////////////////////////////////////////////////////////////////////////////// + +static void FreeDocumentQuery (TRI_query_t* qry) { + TRI_document_query_t* query; + + query = (TRI_document_query_t*) qry; + + query->_operand->free(query->_operand); + + TRI_Free(query->base._string); + TRI_Free(query); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- LIMIT QUERY +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBasePrivate VocBase (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a limit query +//////////////////////////////////////////////////////////////////////////////// + +static void ExecuteLimitQuery (TRI_query_t* qry, + TRI_query_result_t* result) { + TRI_doc_mptr_t const** copy; + TRI_json_t* aug = NULL; + TRI_limit_query_t* query; + TRI_voc_size_t limit; + TRI_voc_size_t offset; + + query = (TRI_limit_query_t*) qry; + + // execute the sub-query + query->_operand->execute(query->_operand, result); + + if (result->_error) { + return; + } + + // append information about the execution plan + TRI_AppendString(&result->_cursor, ">limit"); + + // without any documents there will be no limit + if (result->_length == 0) { + return; + } + + // get the last "limit" documents + if (query->_limit < 0) { + limit = -query->_limit; + + if (result->_length < limit) { + offset = 0; + } + else { + offset = result->_length - limit; + } + } + + // get the first "limit" documents + else { + limit = query->_limit; + offset = 0; + } + + // not enough documents to limit query + if (result->_length <= limit) { + result->_matchedDocuments += result->_length; + return; + } + + // shorten result list + result->_matchedDocuments += limit; + + copy = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * limit); + memcpy(copy, result->_documents + offset, sizeof(TRI_doc_mptr_t*) * limit); + + if (result->_augmented != NULL) { + aug = TRI_Allocate(sizeof(TRI_json_t) * limit); + memcpy(aug, result->_augmented + offset, sizeof(TRI_json_t) * limit); + } + + result->_length = limit; + + TRI_Free(result->_documents); + result->_documents = copy; + + if (result->_augmented != NULL) { + TRI_Free(result->_augmented); + result->_augmented = aug; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clones a limit query +//////////////////////////////////////////////////////////////////////////////// + +static TRI_query_t* CloneLimitQuery (TRI_query_t* qry) { + TRI_limit_query_t* query; + + query = (TRI_limit_query_t*) qry; + + return TRI_CreateLimitQuery(query->_operand->clone(query->_operand), query->_limit); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief frees a full scan +//////////////////////////////////////////////////////////////////////////////// + +static void FreeLimitQuery (TRI_query_t* qry) { + TRI_limit_query_t* query; + + query = (TRI_limit_query_t*) qry; + + query->_operand->free(query->_operand); + + TRI_Free(query->base._string); + TRI_Free(query); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- NEAR QUERY +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBasePrivate VocBase (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief locates a suitable geo index +//////////////////////////////////////////////////////////////////////////////// + +static TRI_geo_index_t* LocateGeoIndex (TRI_near_query_t* query) { + TRI_sim_collection_t* collection; + + // sanity check + if (! query->base._collection->_loaded) { + return NULL; + } + + if (query->base._collection->_collection->base._type != TRI_COL_TYPE_SIMPLE_DOCUMENT) { + return NULL; + } + + collection = (TRI_sim_collection_t*) query->base._collection->_collection; + + // return the geo index + return collection->_geoIndex; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief sorts geo coordinates +//////////////////////////////////////////////////////////////////////////////// + +typedef struct { + double _distance; + void const* _data; +} +geo_coordinate_distance_t; + +static int CompareGeoCoordinateDistance (geo_coordinate_distance_t* left, geo_coordinate_distance_t* right) { + if (left->_distance < right->_distance) { + return -1; + } + else if (left->_distance > right->_distance) { + return 1; + } + else { + return 0; + } +} + +#define FSRT_NAME SortGeoCoordinates +#define FSRT_TYPE geo_coordinate_distance_t + +#define FSRT_COMP(l,r,s) CompareGeoCoordinateDistance(l,r) + +uint32_t FSRT_Rand = 0; + +static uint32_t RandomGeoCoordinateDistance (void) { + return (FSRT_Rand = FSRT_Rand * 31415 + 27818); +} + +#define FSRT__RAND \ + ((fs_b) + FSRT__UNIT * (RandomGeoCoordinateDistance() % FSRT__DIST(fs_e,fs_b,FSRT__SIZE))) + +#include + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a near query +//////////////////////////////////////////////////////////////////////////////// + +static void ExecuteNearQuery (TRI_query_t* qry, + TRI_query_result_t* result) { + GeoCoordinate* end; + GeoCoordinate* ptr; + GeoCoordinates* cors; + TRI_doc_mptr_t const** wtr; + TRI_geo_index_t* idx; + TRI_near_query_t* query; + geo_coordinate_distance_t* tmp; + geo_coordinate_distance_t* gnd; + geo_coordinate_distance_t* gtr; + double* dtr; + size_t n; + + query = (TRI_near_query_t*) qry; + + // free any old results + FreeDocuments(result); + + // we need a suitable geo index + idx = LocateGeoIndex(query); + + if (idx == NULL) { + result->_error = TRI_DuplicateString("cannot locate suitable geo index"); + return; + } + + // sub-query must be the collection + if (query->_operand->_type != TRI_QUE_TYPE_COLLECTION) { + result->_error = TRI_DuplicateString("near query can only be executed for the complete collection"); + return; + } + + // append information about the execution plan + TRI_AppendString(&result->_cursor, ">near"); + + // execute geo search + cors = TRI_NearestGeoIndex(&idx->base, query->_latitude, query->_longitude, query->_count); + + if (cors == NULL) { + result->_error = TRI_DuplicateString("cannot execute geo index search"); + return; + } + + // sort the result + n = cors->length; + + gtr = tmp = TRI_Allocate(sizeof(geo_coordinate_distance_t) * n); + gnd = tmp + n; + + ptr = cors->coordinates; + end = cors->coordinates + n; + + dtr = cors->distances; + + for (; ptr < end; ++ptr, ++dtr, ++gtr) { + gtr->_distance = *dtr; + gtr->_data = ptr->data; + } + + GeoIndex_CoordinatesFree(cors); + + SortGeoCoordinates(tmp, gnd); + + // copy the documents + result->_documents = (TRI_doc_mptr_t const**) (wtr = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * n)); + + for (gtr = tmp; gtr < gnd; ++gtr, ++wtr) { + *wtr = gtr->_data; + } + + // copy the distance + if (query->_distance != NULL) { + TRI_json_t* atr; + + result->_augmented = (atr = TRI_Allocate(sizeof(TRI_json_t) * n)); + + for (gtr = tmp; gtr < gnd; ++gtr, ++atr) { + atr->_type = TRI_JSON_ARRAY; + TRI_InitVector(&atr->_value._objects, sizeof(TRI_json_t)); + + TRI_InsertArrayJson(atr, query->_distance, TRI_CreateNumberJson(gtr->_distance)); + } + } + + TRI_Free(tmp); + + result->_total = result->_length = n; + result->_scannedIndexEntries += n; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clones a near query +//////////////////////////////////////////////////////////////////////////////// + +static TRI_query_t* CloneNearQuery (TRI_query_t* qry) { + TRI_near_query_t* query; + + query = (TRI_near_query_t*) qry; + + return TRI_CreateNearQuery(query->_operand->clone(query->_operand), + query->_latitude, + query->_longitude, + query->_count, + query->_distance); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief frees a near query +//////////////////////////////////////////////////////////////////////////////// + +static void FreeNearQuery (TRI_query_t* qry) { + TRI_near_query_t* query; + + query = (TRI_near_query_t*) qry; + + TRI_Free(query->base._string); + TRI_Free(query); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- SKIP QUERY +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBasePrivate VocBase (Private) +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a skip +//////////////////////////////////////////////////////////////////////////////// + +static void ExecuteSkipQuery (TRI_query_t* qry, + TRI_query_result_t* result) { + TRI_doc_mptr_t const** copy; + TRI_json_t* aug = NULL; + TRI_skip_query_t* query; + TRI_voc_size_t newtotal; + + query = (TRI_skip_query_t*) qry; + + // execute the sub-query + query->_operand->execute(query->_operand, result); + + if (result->_error) { + return; + } + + TRI_AppendString(&result->_cursor, ">skip"); + + // no skip at all + if (query->_skip == 0) { + result->_matchedDocuments += result->_length; + return; + } + + // without any documents there will be no skipping + if (result->_length == 0) { + return; + } + + // not enough documents + if (result->_length <= query->_skip) { + result->_length = 0; + + FreeDocuments(result); + + return; + } + + // shorten result list + newtotal = (result->_length - query->_skip); + + result->_matchedDocuments += newtotal; + + copy = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * newtotal); + memcpy(copy, result->_documents + query->_skip, sizeof(TRI_doc_mptr_t*) * newtotal); + + if (result->_augmented != NULL) { + aug = TRI_Allocate(sizeof(TRI_json_t) * newtotal); + memcpy(aug, result->_augmented + query->_skip, sizeof(TRI_json_t) * newtotal); + } + + result->_length = newtotal; + + TRI_Free(result->_documents); + result->_documents = copy; + + if (result->_augmented != NULL) { + TRI_Free(result->_augmented); + result->_augmented = aug; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief clones a skip query +//////////////////////////////////////////////////////////////////////////////// + +static TRI_query_t* CloneSkipQuery (TRI_query_t* qry) { + TRI_skip_query_t* query; + + query = (TRI_skip_query_t*) qry; + + return TRI_CreateSkipQuery(query->_operand->clone(query->_operand), query->_skip); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief frees a full scan +//////////////////////////////////////////////////////////////////////////////// + +static void FreeSkipQuery (TRI_query_t* qry) { + TRI_skip_query_t* query; + + query = (TRI_skip_query_t*) qry; + + query->_operand->free(query->_operand); + + TRI_Free(query->base._string); + TRI_Free(query); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors and destructors +// ----------------------------------------------------------------------------- + //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief executes a full scan +/// @brief creates a full scan of a collection //////////////////////////////////////////////////////////////////////////////// -static TRI_doc_mptr_t const** ExecuteCollectionQuery (TRI_collection_query_t* query, - TRI_rs_info_t* info, - TRI_voc_size_t* length, - TRI_voc_size_t* total) { - TRI_doc_mptr_t const** result; - TRI_doc_mptr_t const** qtr; - TRI_sim_collection_t* collection; - void** end; - void** ptr; +TRI_query_t* TRI_CreateCollectionQuery (TRI_vocbase_col_t const* collection) { + TRI_collection_query_t* query; - TRI_AppendString(&info->_cursor, ":collection"); + query = TRI_Allocate(sizeof(TRI_collection_query_t)); - *length = 0; + query->base._type = TRI_QUE_TYPE_COLLECTION; + query->base._collection = collection; - if (query->base._collection->base._type != TRI_COL_TYPE_SIMPLE_DOCUMENT) { - return NULL; + query->base._string = TRI_Concatenate2String("db.", collection->_name); + + query->base.execute = ExecuteCollectionQuery; + query->base.clone = CloneCollectionQuery; + query->base.free = FreeCollectionQuery; + + return &query->base; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds the distance to a query +//////////////////////////////////////////////////////////////////////////////// + +TRI_query_t* TRI_CreateDistanceQuery (TRI_query_t* operand, char const* name) { + + // ............................................................................. + // operand is a NEAR + // ............................................................................. + + if (operand->_type == TRI_QUE_TYPE_NEAR) { + TRI_near_query_t* near; + + near = (TRI_near_query_t*) operand; + + return TRI_CreateNearQuery(near->_operand->clone(near->_operand), + near->_latitude, + near->_longitude, + near->_count, + name); } - collection = (TRI_sim_collection_t*) query->base._collection; + // ............................................................................. + // general case + // ............................................................................. - *total = *length = collection->_primaryIndex._nrUsed; + return operand->clone(operand); +} - if (*length == 0) { - return NULL; +//////////////////////////////////////////////////////////////////////////////// +/// @brief looks up a document +//////////////////////////////////////////////////////////////////////////////// + +TRI_query_t* TRI_CreateDocumentQuery (TRI_query_t* operand, TRI_voc_did_t did) { + TRI_document_query_t* query; + char* number; + + query = TRI_Allocate(sizeof(TRI_document_query_t)); + + query->base._type = TRI_QUE_TYPE_DOCUMENT; + query->base._collection = operand->_collection; + + number = TRI_StringUInt64(did); + query->base._string = TRI_Concatenate4String(operand->_string, ".document(", number, ")"); + TRI_FreeString(number); + + query->base.execute = ExecuteDocumentQuery; + query->base.clone = CloneDocumentQuery; + query->base.free = FreeDocumentQuery; + + query->_operand = operand; + query->_did = did; + + return &query->base; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief limits an existing query +//////////////////////////////////////////////////////////////////////////////// + +TRI_query_t* TRI_CreateLimitQuery (TRI_query_t* operand, TRI_voc_ssize_t limit) { + + // ............................................................................. + // operand is a NEAR + // ............................................................................. + + if (0 <= limit && operand->_type == TRI_QUE_TYPE_NEAR) { + TRI_near_query_t* near; + + near = (TRI_near_query_t*) operand; + + if (near->_count < limit) { + limit = near->_count; + } + + return TRI_CreateNearQuery(near->_operand->clone(near->_operand), + near->_latitude, + near->_longitude, + limit, + near->_distance); } - result = qtr = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * (*length)); + // ............................................................................. + // operand is a LIMIT + // ............................................................................. - ptr = collection->_primaryIndex._table; - end = collection->_primaryIndex._table + collection->_primaryIndex._nrAlloc; + if (operand->_type == TRI_QUE_TYPE_LIMIT) { + TRI_limit_query_t* lquery; + bool combine; - for (; ptr < end; ++ptr) { - ++info->_scannedDocuments; + lquery = (TRI_limit_query_t*) operand; + combine = true; - if (*ptr) { - ++info->_matchedDocuments; - *qtr++ = *ptr; + if (lquery->_limit < 0 && limit <= 0) { + if (limit < lquery->_limit) { + limit = lquery->_limit; + } + } + else if (lquery->_limit > 0 && limit >= 0) { + if (limit > lquery->_limit) { + limit = lquery->_limit; + } + } + else if (abs(lquery->_limit) <= abs(limit)) { + return lquery->base.clone(&lquery->base); + } + else { + combine = false; + } + + if (combine) { + return TRI_CreateLimitQuery(lquery->_operand->clone(lquery->_operand), + limit); } } - return result; + // ............................................................................. + // general case + // ............................................................................. + + TRI_limit_query_t* query; + char* number; + + query = TRI_Allocate(sizeof(TRI_limit_query_t)); + + query->base._type = TRI_QUE_TYPE_LIMIT; + query->base._collection = operand->_collection; + + number = TRI_StringInt64(limit); + query->base._string = TRI_Concatenate4String(operand->_string, ".limit(", number, ")"); + TRI_FreeString(number); + + query->base.execute = ExecuteLimitQuery; + query->base.clone = CloneLimitQuery; + query->base.free = FreeLimitQuery; + + query->_operand = operand; + query->_limit = limit; + + return &query->base; } //////////////////////////////////////////////////////////////////////////////// -/// @brief executes a document lookup as primary index lookup +/// @brief looks up documents near a given point //////////////////////////////////////////////////////////////////////////////// -static TRI_doc_mptr_t const** ExecuteDocumentQueryPrimary (TRI_document_query_t* query, - TRI_doc_collection_t* collection, - TRI_rs_info_t* info, - TRI_voc_size_t* length, - TRI_voc_size_t* total) { - TRI_doc_mptr_t const* document; - TRI_doc_mptr_t const** result; +TRI_query_t* TRI_CreateNearQuery (TRI_query_t* operand, + double latitude, + double longitude, + TRI_voc_size_t count, + char const* distance) { + TRI_string_buffer_t sb; + TRI_near_query_t* query; - document = collection->read(collection, query->_did); + query = TRI_Allocate(sizeof(TRI_near_query_t)); - TRI_AppendString(&info->_cursor, ":primary"); + query->base._type = TRI_QUE_TYPE_NEAR; + query->base._collection = operand->_collection; - ++info->_scannedIndexEntries; - if (document == NULL) { - *length = 0; - *total = 0; - return NULL; + TRI_InitStringBuffer(&sb); + + TRI_AppendStringStringBuffer(&sb, ".near("); + TRI_AppendDoubleStringBuffer(&sb, latitude); + TRI_AppendStringStringBuffer(&sb, ","); + TRI_AppendDoubleStringBuffer(&sb, longitude); + TRI_AppendStringStringBuffer(&sb, ")"); + + TRI_AppendStringStringBuffer(&sb, ".limit("); + TRI_AppendUInt64StringBuffer(&sb, count); + TRI_AppendStringStringBuffer(&sb, ")"); + + if (distance != NULL) { + TRI_AppendStringStringBuffer(&sb, ".distance(\""); + TRI_AppendStringStringBuffer(&sb, distance); + TRI_AppendStringStringBuffer(&sb, "\")"); } - else { - *length = 1; - *total = 1; - ++info->_matchedDocuments; + query->base._string = sb._buffer; - result = TRI_Allocate(sizeof(TRI_doc_mptr_t*)); - *result = document; + query->base.execute = ExecuteNearQuery; + query->base.clone = CloneNearQuery; + query->base.free = FreeNearQuery; - return result; - } + query->_operand = operand; + query->_location = NULL; + query->_latitude = latitude; + query->_longitude = longitude; + query->_count = count; + query->_distance = (distance == NULL ? NULL : TRI_DuplicateString(distance)); + + return &query->base; } //////////////////////////////////////////////////////////////////////////////// -/// @brief executes a document lookup as full scan +/// @brief skips elements of an existing query //////////////////////////////////////////////////////////////////////////////// -static TRI_doc_mptr_t const** ExecuteDocumentQuerySlow (TRI_document_query_t* query, - TRI_rs_info_t* info, - TRI_voc_size_t* length, - TRI_voc_size_t* total) { - TRI_doc_mptr_t const** result; - TRI_doc_mptr_t const** ptr; - TRI_doc_mptr_t const** sub; - TRI_doc_mptr_t const** end; - TRI_voc_size_t subtotal; +TRI_query_t* TRI_CreateSkipQuery (TRI_query_t* operand, TRI_voc_size_t skip) { + TRI_skip_query_t* query; + char* number; - sub = ExecuteQuery(query->_operand, info, &subtotal, total); + // ............................................................................. + // operand is a SKIP + // ............................................................................. - TRI_AppendString(&info->_cursor, ":full-scan"); + if (0 <= skip && operand->_type == TRI_QUE_TYPE_SKIP) { + TRI_skip_query_t* squery; - if (sub == NULL) { - *length = 0; - return NULL; + squery = (TRI_skip_query_t*) operand; + + return TRI_CreateSkipQuery(squery->_operand->clone(squery->_operand), + squery->_skip + skip); } - if (subtotal == 0) { - TRI_Free(sub); + // ............................................................................. + // general case + // ............................................................................. - *length = 0; - return NULL; - } + query = TRI_Allocate(sizeof(TRI_skip_query_t)); - end = sub + subtotal; + query->base._type = TRI_QUE_TYPE_SKIP; + query->base._collection = operand->_collection; - for (ptr = sub; ptr < end; ++ptr) { - ++info->_scannedDocuments; + number = TRI_StringInt64(skip); + query->base._string = TRI_Concatenate4String(operand->_string, ".skip(", number, ")"); + TRI_FreeString(number); - if ((*ptr)->_did == query->_did) { - ++info->_matchedDocuments; - result = TRI_Allocate(sizeof(TRI_doc_mptr_t*)); + query->base.execute = ExecuteSkipQuery; + query->base.clone = CloneSkipQuery; + query->base.free = FreeSkipQuery; - *result = *ptr; - *length = 1; + query->_operand = operand; + query->_skip = skip; - TRI_Free(sub); - - return result; - } - } - - TRI_Free(sub); - - *length = 0; - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief executes a document lookup -//////////////////////////////////////////////////////////////////////////////// - -static TRI_doc_mptr_t const** ExecuteDocumentQuery (TRI_document_query_t* query, - TRI_rs_info_t* info, - TRI_voc_size_t* length, - TRI_voc_size_t* total) { - if (query->_operand != NULL && query->_operand->_type == TRI_QUE_TYPE_COLLECTION) { - return ExecuteDocumentQueryPrimary(query, - query->_operand->_collection, - info, - length, - total); - } - else { - return ExecuteDocumentQuerySlow(query, info, length, total); - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief executes a limit -//////////////////////////////////////////////////////////////////////////////// - -static TRI_doc_mptr_t const** ExecuteLimitQuery (TRI_limit_query_t* query, - TRI_rs_info_t* info, - TRI_voc_size_t* length, - TRI_voc_size_t* total) { - TRI_doc_mptr_t const** result; - TRI_doc_mptr_t const** sub; - TRI_voc_size_t subtotal; - TRI_voc_size_t offset; - TRI_voc_size_t limit; - - sub = ExecuteQuery(query->_operand, info, &subtotal, total); - - TRI_AppendString(&info->_cursor, ":limit"); - - if (sub == NULL) { - *length = 0; - return NULL; - } - - // get the last "limit" documents - if (query->_limit < 0) { - limit = -query->_limit; - offset = subtotal + query->_limit; - } - else { - limit = query->_limit; - offset = 0; - } - - if (subtotal <= limit) { - info->_matchedDocuments += subtotal; - *length = subtotal; - return sub; - } - - info->_matchedDocuments += limit; - result = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * limit); - - memcpy(result, sub + offset, sizeof(TRI_doc_mptr_t*) * limit); - *length = limit; - - TRI_Free(sub); - - return result; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief executes a near -//////////////////////////////////////////////////////////////////////////////// - -static TRI_doc_mptr_t const** ExecuteNearQuery (TRI_near_query_t* query, - TRI_rs_info_t* info, - TRI_voc_size_t* length, - TRI_voc_size_t* total) { - GeoCoordinate* end; - GeoCoordinate* ptr; - GeoCoordinates* cors; - TRI_doc_mptr_t const** result; - TRI_doc_mptr_t const** wtr; - size_t n; - - TRI_AppendString(&info->_cursor, ":near"); - - cors = TRI_NearestGeoIndex(query->_index, query->_latitude, query->_longitude, query->_count); - - if (cors == NULL) { - LOG_WARNING("cannot execute geo-index"); - *length = 0; - return NULL; - } - - n = cors->length; - - ptr = cors->coordinates; - end = cors->coordinates + n; - - result = (TRI_doc_mptr_t const**) (wtr = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * n)); - - for (; ptr < end; ++ptr, ++wtr) { - *wtr = ptr->data; - } - - GeoIndex_CoordinatesFree(cors); - - *length = n; - *total = n; - - return result; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief executes a skip -//////////////////////////////////////////////////////////////////////////////// - -static TRI_doc_mptr_t const** ExecuteSkipQuery (TRI_skip_query_t* query, - TRI_rs_info_t* info, - TRI_voc_size_t* length, - TRI_voc_size_t* total) { - TRI_doc_mptr_t const** result; - TRI_doc_mptr_t const** sub; - TRI_voc_size_t subtotal; - TRI_voc_size_t newtotal; - - sub = ExecuteQuery(query->_operand, info, &subtotal, total); - - TRI_AppendString(&info->_cursor, ":skip"); - - if (sub == NULL) { - *length = 0; - return NULL; - } - - if (subtotal <= query->_skip) { - TRI_Free(sub); - *length = 0; - return NULL; - } - - if (query->_skip == 0) { - info->_matchedDocuments += subtotal; - *length = subtotal; - return sub; - } - - newtotal = (subtotal - query->_skip); - info->_matchedDocuments += newtotal; - result = TRI_Allocate(sizeof(TRI_doc_mptr_t*) * newtotal); - - memcpy(result, sub + query->_skip, sizeof(TRI_doc_mptr_t*) * newtotal); - *length = subtotal - query->_skip; - - TRI_Free(sub); - - return result; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief executes a query -//////////////////////////////////////////////////////////////////////////////// - -static TRI_doc_mptr_t const** ExecuteQuery (TRI_query_t* query, - TRI_rs_info_t* info, - TRI_voc_size_t* length, - TRI_voc_size_t* total) { - TRI_doc_mptr_t const** result; - - result = NULL; - *length = 0; - - switch (query->_type) { - case TRI_QUE_TYPE_COLLECTION: - result = ExecuteCollectionQuery((TRI_collection_query_t*) query, info, length, total); - break; - - case TRI_QUE_TYPE_DOCUMENT: - result = ExecuteDocumentQuery((TRI_document_query_t*) query, info, length, total); - break; - - case TRI_QUE_TYPE_LIMIT: - result = ExecuteLimitQuery((TRI_limit_query_t*) query, info, length, total); - break; - - case TRI_QUE_TYPE_NEAR: - result = ExecuteNearQuery((TRI_near_query_t*) query, info, length, total); - break; - - case TRI_QUE_TYPE_SKIP: - result = ExecuteSkipQuery((TRI_skip_query_t*) query, info, length, total); - break; - } - - return result; + return &query->base; } //////////////////////////////////////////////////////////////////////////////// @@ -398,159 +1033,29 @@ static TRI_doc_mptr_t const** ExecuteQuery (TRI_query_t* query, /// @{ //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @brief creates a full scan of a collection -//////////////////////////////////////////////////////////////////////////////// - -TRI_query_t* TRI_CreateCollectionQuery (struct TRI_doc_collection_s* collection) { - TRI_collection_query_t* query; - - query = TRI_Allocate(sizeof(TRI_collection_query_t)); - - query->base._type = TRI_QUE_TYPE_COLLECTION; - query->base._collection = collection; - - return &query->base; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief looks up a document -//////////////////////////////////////////////////////////////////////////////// - -TRI_query_t* TRI_CreateDocumentQuery (TRI_query_t* operand, TRI_voc_did_t did) { - TRI_document_query_t* query; - - query = TRI_Allocate(sizeof(TRI_document_query_t)); - - query->base._type = TRI_QUE_TYPE_DOCUMENT; - query->base._collection = operand->_collection; - - query->_operand = operand; - query->_did = did; - - return &query->base; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief limits an existing query -//////////////////////////////////////////////////////////////////////////////// - -TRI_query_t* TRI_CreateLimitQuery (TRI_query_t* operand, TRI_voc_ssize_t limit) { - TRI_limit_query_t* query; - - query = TRI_Allocate(sizeof(TRI_limit_query_t)); - - query->base._type = TRI_QUE_TYPE_LIMIT; - query->base._collection = operand->_collection; - - query->_operand = operand; - query->_limit = limit; - - return &query->base; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief looks up documents near a given point -//////////////////////////////////////////////////////////////////////////////// - -TRI_query_t* TRI_CreateNearQuery (TRI_query_t* operand, - char const* location, - double latitude, - double longitude, - TRI_voc_size_t count, - char const** error) { - TRI_index_t* geo; - TRI_near_query_t* query; - - if (operand->_type != TRI_QUE_TYPE_COLLECTION) { - *error = "'near' requires a collection as operand"; - return NULL; - } - -#warning LOOKUP MUST BE DONE WITH AT LEAST A READ TRANSACTION - if (operand->_collection->base._type == TRI_COL_TYPE_SIMPLE_DOCUMENT) { - geo = TRI_LookupGeoIndexSimCollection((TRI_sim_collection_t*) operand->_collection, location); - } - else { - *error = "unknown collection type"; - return NULL; - } - - if (geo == NULL) { - *error = "'near' requires a geo-index"; - return NULL; - } - - query = TRI_Allocate(sizeof(TRI_near_query_t)); - - query->base._type = TRI_QUE_TYPE_NEAR; - query->base._collection = operand->_collection; - - query->_index = geo; - query->_latitude = latitude; - query->_longitude = longitude; - query->_count = count; - - return &query->base; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief looks up documents near a given point -//////////////////////////////////////////////////////////////////////////////// - -TRI_query_t* TRI_CreateNear2Query (TRI_query_t* operand, - char const* locLatitude, - double latitude, - char const* locLongitude, - double longitude, - TRI_voc_size_t count, - char const** error) { - if (operand->_type != TRI_QUE_TYPE_COLLECTION) { - *error = "near requires a collection as operand"; - return 0; - } - - *error = "not yet"; - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief skips elements of an existing query -//////////////////////////////////////////////////////////////////////////////// - -TRI_query_t* TRI_CreateSkipQuery (TRI_query_t* operand, TRI_voc_size_t skip) { - TRI_skip_query_t* query; - - query = TRI_Allocate(sizeof(TRI_skip_query_t)); - - query->base._type = TRI_QUE_TYPE_SKIP; - query->base._collection = operand->_collection; - - query->_operand = operand; - query->_skip = skip; - - return &query->base; -} - //////////////////////////////////////////////////////////////////////////////// /// @brief executes a query //////////////////////////////////////////////////////////////////////////////// TRI_result_set_t* TRI_ExecuteQuery (TRI_query_t* query) { - TRI_doc_collection_t* collection = query->_collection; - TRI_doc_mptr_t const** result; - TRI_voc_size_t length; - TRI_voc_size_t total; + TRI_doc_collection_t* collection = query->_collection->_collection; + TRI_query_result_t result; TRI_result_set_t* rs; TRI_rs_info_t info; - result = NULL; - length = 0; + result._cursor = TRI_DuplicateString("query"); + result._error = NULL; + + result._length = 0; + result._total = 0; + + result._documents = NULL; + result._augmented = NULL; + + result._scannedIndexEntries = 0; + result._scannedDocuments = 0; + result._matchedDocuments = 0; - info._cursor = TRI_DuplicateString(""); - info._scannedIndexEntries = 0; - info._scannedDocuments = 0; - info._matchedDocuments = 0; info._runtime = -TRI_microtime(); // ............................................................................. @@ -559,11 +1064,21 @@ TRI_result_set_t* TRI_ExecuteQuery (TRI_query_t* query) { collection->beginRead(collection); - result = ExecuteQuery(query, &info, &length, &total); + query->execute(query, &result); - LOG_TRACE("query returned '%lu' documents", (unsigned long) length); - - rs = TRI_CreateRSVector(collection, result, length, total); + if (result._error) { + LOG_TRACE("query returned error: %s", result._error); + rs = TRI_CreateRSSingle(collection, NULL, 0); + rs->_error = result._error; + } + else { + LOG_TRACE("query returned '%lu' documents", (unsigned long) result._length); + rs = TRI_CreateRSVector(collection, + result._documents, + result._augmented, + result._length, + result._total); + } collection->endRead(collection); @@ -571,10 +1086,18 @@ TRI_result_set_t* TRI_ExecuteQuery (TRI_query_t* query) { // outside a read transaction // ............................................................................. + info._cursor = result._cursor; + info._runtime += TRI_microtime(); + info._scannedIndexEntries = result._scannedIndexEntries; + info._scannedDocuments = result._scannedDocuments; + info._matchedDocuments = result._matchedDocuments; + rs->_info = info; + FreeDocuments(&result); + return rs; } diff --git a/VocBase/query.h b/VocBase/query.h index c58dc73a3a..32b64ad8be 100644 --- a/VocBase/query.h +++ b/VocBase/query.h @@ -36,117 +36,6 @@ extern "C" { #endif -//////////////////////////////////////////////////////////////////////////////// -/// @page DurhamFluentInterface Fluent Interface -/// -/// A fluent interface is implemented by using method chaining to relay the -/// instruction context of a subsequent call. -/// -/// The Durham Storage Engine provides the following methods: -/// -/// - selection by example -/// - field selection aka projection -/// - @ref GeoFI "geo coordinates" -/// - sorting -/// - pagination of the result using @ref LimitFI "limit", @ref SkipFI "skip", -/// and @ref CountFI "count". -/// -/// @section FirstStepsFI First Steps -/// -/// For instance, in order to select all elements of a collection "examples", -/// one can use -/// -/// @verbinclude fluent1 -/// -/// This will select all documents and prints the first 20 documents. If there -/// are more than 20 documents, then "...more results..." is printed and you -/// can use the variable "it" to access the next 20 document. -/// -/// @verbinclude fluent2 -/// -/// In the above examples "db.examples.select()" defines a query. Printing -/// that query, executes the query and returns a cursor to the result set. -/// The first 20 documents are printed and the query (resp. cursor) is -/// assigned to the variable "it". -/// -/// A cursor can also be query using "hasNext()" and "next()". -/// -/// @verbinclude fluent3 -/// -/// @section GeoFI Geo Coordinates -/// -/// The Storage Engine allows to selects documents based on geographic -/// coordinates. In order for this to work a geo index must be defined. This -/// index will a very elaborate index to lookup neighbours that is magnitudes -/// faster than a simple R* index. -/// -/// In generale a geo coordinate is a pair of longitude and latitude. This can -/// either be an list with two elements like "[ -10, +30 ]" or an object like "{ -/// lon: -10, lat: +30 }". In order to find all documents within a given radius -/// around a coordinate use the @ref WithinFI "within()" operator. In order to -/// find all documents near a given document use the @ref NearFI "near()" -/// operator. -/// -/// @section WithinFI The Within Operator -/// -/// @section NearFI The Near Operator -/// -/// Assume that the geo coordinate is stored as list of size 2. The first -/// value being the latitude and the second value being the longitude. -/// -/// @section LimitFI The Limit Operator -/// -/// If, for example, you display the result of a user search, then you are in -/// general not interested in the completed result set, but only the first 10 -/// documents. In this case, you can the "limit()" operator. This operators -/// works like LIMIT in MySQL, it specifies a maximal number of documents to -/// return. -/// -/// @verbinclude fluent4 -/// -/// Specifying a limit of "0" returns no documents at all. If you do not need -/// a limit, just do not add the limit operator. If you specifiy a negtive -/// limit of -n, this will return the last n documents instead. -/// -/// @verbinclude fluent8 -/// -/// @section SkipFI The Skip Operator -/// -/// "skip" used together with "limit" can be used to implement pagination. -/// The "skip" operator skips over the first n documents. So, in order to -/// create result pages with 10 result documents per page, you can use -/// "skip(n * 10).limit(10)" to access the n.th page. -/// -/// @verbinclude fluent5 -/// -/// @section CountFI The Count Operator -/// -/// If you are implementation pagination, then you need the total amount of -/// documents a query returned. "count()" just does this. It returns the -/// total amount of documents regardless of any "limit()" and "skip()" -/// operator used before the "count()". If you really want these to be taken -/// into account, use "count(true)". -/// -/// @verbinclude fluent6 -/// -/// @section ExplainFI The Explain Operator -/// -/// In order to optimise queries you need to know how the storage engine -/// executed that type of query. You can use the "explain()" operator to -/// see how a query was executed. -/// -/// @verbinclude fluent9 -/// -/// The "explain()" operator returns an object with the following attributes. -/// -/// - cursor: describes how the result set was computed. -/// - scannedIndexEntries: how many index entries were scanned -/// - scannedDocuments: how many documents were scanned -/// - matchedDocuments: the sum of all matched documents in each step -/// - runtime: the runtime in seconds -/// -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- forward declarations // ----------------------------------------------------------------------------- @@ -176,13 +65,39 @@ typedef enum { } TRI_que_type_e; +//////////////////////////////////////////////////////////////////////////////// +/// @brief query result +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_query_result_s { + char* _cursor; + char* _error; + + TRI_voc_size_t _length; + TRI_voc_size_t _total; + + TRI_voc_size_t _scannedIndexEntries; + TRI_voc_size_t _scannedDocuments; + TRI_voc_size_t _matchedDocuments; + + struct TRI_doc_mptr_s const** _documents; + struct TRI_json_s* _augmented; +} +TRI_query_result_t; + //////////////////////////////////////////////////////////////////////////////// /// @brief query //////////////////////////////////////////////////////////////////////////////// typedef struct TRI_query_s { TRI_que_type_e _type; - struct TRI_doc_collection_s* _collection; + TRI_vocbase_col_t const* _collection; + + char* _string; + + void (*execute) (struct TRI_query_s*, TRI_query_result_t*); + struct TRI_query_s* (*clone) (struct TRI_query_s*); + void (*free) (struct TRI_query_s*); } TRI_query_t; @@ -226,9 +141,14 @@ TRI_limit_query_t; typedef struct TRI_near_query_s { TRI_query_t base; - struct TRI_index_s* _index; + TRI_query_t* _operand; + + char* _location; + char* _distance; + double _latitude; double _longitude; + TRI_voc_size_t _count; } TRI_near_query_t; @@ -250,7 +170,7 @@ TRI_skip_query_t; //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- -// --SECTION-- public functions +// --SECTION-- constructors and destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// @@ -262,7 +182,13 @@ TRI_skip_query_t; /// @brief creates a full scan of a collection //////////////////////////////////////////////////////////////////////////////// -TRI_query_t* TRI_CreateCollectionQuery (struct TRI_doc_collection_s*); +TRI_query_t* TRI_CreateCollectionQuery (TRI_vocbase_col_t const*); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds the distance to a query +//////////////////////////////////////////////////////////////////////////////// + +TRI_query_t* TRI_CreateDistanceQuery (TRI_query_t*, char const* name); //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a document @@ -281,23 +207,10 @@ TRI_query_t* TRI_CreateLimitQuery (TRI_query_t*, TRI_voc_ssize_t); //////////////////////////////////////////////////////////////////////////////// TRI_query_t* TRI_CreateNearQuery (TRI_query_t*, - char const* location, double latitude, double longitude, TRI_voc_size_t count, - char const** error); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief looks up documents near a given point -//////////////////////////////////////////////////////////////////////////////// - -TRI_query_t* TRI_CreateNear2Query (TRI_query_t*, - char const* locLatitude, - double latitude, - char const* locLongitude, - double longitude, - TRI_voc_size_t count, - char const** error); + char const* distance); //////////////////////////////////////////////////////////////////////////////// /// @brief skips elements of an existing query @@ -305,6 +218,19 @@ TRI_query_t* TRI_CreateNear2Query (TRI_query_t*, TRI_query_t* TRI_CreateSkipQuery (TRI_query_t*, TRI_voc_size_t); +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBase VocBase +/// @{ +//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// /// @brief executes a query //////////////////////////////////////////////////////////////////////////////// diff --git a/VocBase/result-set.c b/VocBase/result-set.c index 44c569ee65..059efa54b0 100644 --- a/VocBase/result-set.c +++ b/VocBase/result-set.c @@ -66,6 +66,7 @@ typedef struct doc_rs_vector_s { TRI_shaped_json_t* _elements; TRI_voc_did_t* _dids; + TRI_json_t* _augmented; TRI_voc_size_t _length; TRI_voc_size_t _current; @@ -100,9 +101,15 @@ static bool HasNextRSSingle (TRI_result_set_t* rs) { /// @brief returns the current element of a single result set //////////////////////////////////////////////////////////////////////////////// -static TRI_shaped_json_t* NextRSSingle (TRI_result_set_t* rs, TRI_voc_did_t* did) { +static TRI_shaped_json_t* NextRSSingle (TRI_result_set_t* rs, + TRI_voc_did_t* did, + TRI_json_t const** augmented) { doc_rs_single_t* rss = (doc_rs_single_t*) rs; + if (augmented != NULL) { + *augmented = NULL; + } + if (rss->_empty) { *did = 0; @@ -167,12 +174,23 @@ static bool HasNextRSVector (TRI_result_set_t* rs) { /// @brief returns the current element of a vector result set //////////////////////////////////////////////////////////////////////////////// -static TRI_shaped_json_t* NextRSVector (TRI_result_set_t* rs, TRI_voc_did_t* did) { +static TRI_shaped_json_t* NextRSVector (TRI_result_set_t* rs, + TRI_voc_did_t* did, + TRI_json_t const** augmented) { doc_rs_vector_t* rss = (doc_rs_vector_t*) rs; if (rss->_current < rss->_length) { *did = rss->_dids[rss->_current]; + if (augmented != NULL) { + if (rss->_augmented == NULL) { + *augmented = NULL; + } + else { + *augmented = &rss->_augmented[rss->_current]; + } + } + return &rss->_elements[rss->_current++]; } else { @@ -206,13 +224,17 @@ static void FreeRSVector (TRI_result_set_t* rs) { void* f; + f = TRI_RemoveKeyAssociativeSynced(&rs->_container->_resultSets, &rs->_id); + assert(f != NULL); + if (rss->_elements != NULL) { TRI_Free(rss->_dids); TRI_Free(rss->_elements); } - f = TRI_RemoveKeyAssociativeSynced(&rs->_container->_resultSets, &rs->_id); - assert(f != NULL); + if (rss->_augmented != NULL) { + TRI_Free(rss->_augmented); + } TRI_Free(rs->_info._cursor); TRI_Free(rs); @@ -308,6 +330,8 @@ TRI_result_set_t* TRI_CreateRSSingle (TRI_doc_collection_t* collection, rs = TRI_Allocate(sizeof(doc_rs_single_t)); + rs->base._error = NULL; + rs->base.hasNext = HasNextRSSingle; rs->base.next = NextRSSingle; rs->base.count = CountRSSingle; @@ -338,6 +362,7 @@ TRI_result_set_t* TRI_CreateRSSingle (TRI_doc_collection_t* collection, TRI_result_set_t* TRI_CreateRSVector (TRI_doc_collection_t* collection, TRI_doc_mptr_t const** header, + TRI_json_t const* augmented, TRI_voc_size_t length, TRI_voc_size_t total) { doc_rs_vector_t* rs; @@ -348,6 +373,8 @@ TRI_result_set_t* TRI_CreateRSVector (TRI_doc_collection_t* collection, rs = TRI_Allocate(sizeof(doc_rs_vector_t)); + rs->base._error = NULL; + rs->base.hasNext = HasNextRSVector; rs->base.next = NextRSVector; rs->base.count = CountRSVector; @@ -361,6 +388,7 @@ TRI_result_set_t* TRI_CreateRSVector (TRI_doc_collection_t* collection, rs->_current = 0; rs->_dids = NULL; rs->_elements = NULL; + rs->_augmented = NULL; } else { rs->_length = length; @@ -377,6 +405,14 @@ TRI_result_set_t* TRI_CreateRSVector (TRI_doc_collection_t* collection, *wtr = (*qtr)->_did; *ptr = (*qtr)->_document; } + + if (augmented == NULL) { + rs->_augmented = NULL; + } + else { + rs->_augmented = TRI_Allocate(length * sizeof(TRI_json_t)); + memcpy(rs->_augmented, augmented, length * sizeof(TRI_json_t)); + } } rs->_total = total; diff --git a/VocBase/result-set.h b/VocBase/result-set.h index db995bf411..e091cd7501 100644 --- a/VocBase/result-set.h +++ b/VocBase/result-set.h @@ -39,8 +39,9 @@ extern "C" { // ----------------------------------------------------------------------------- struct TRI_doc_collection_s; -struct TRI_shaped_json_s; struct TRI_doc_mptr_s; +struct TRI_json_s; +struct TRI_shaped_json_s; // ----------------------------------------------------------------------------- // --SECTION-- public types @@ -92,8 +93,10 @@ typedef struct TRI_result_set_s { TRI_rs_container_t* _container; TRI_rs_info_t _info; + char* _error; + bool (*hasNext) (struct TRI_result_set_s*); - struct TRI_shaped_json_s* (*next) (struct TRI_result_set_s*, TRI_voc_did_t*); + struct TRI_shaped_json_s* (*next) (struct TRI_result_set_s*, TRI_voc_did_t*, struct TRI_json_s const**); TRI_voc_size_t (*count) (struct TRI_result_set_s*, bool current); void (*free) (struct TRI_result_set_s*); @@ -139,6 +142,7 @@ TRI_result_set_t* TRI_CreateRSSingle (struct TRI_doc_collection_s* collection, TRI_result_set_t* TRI_CreateRSVector (struct TRI_doc_collection_s* collection, struct TRI_doc_mptr_s const** header, + struct TRI_json_s const* augmented, TRI_voc_size_t length, TRI_voc_size_t total); diff --git a/VocBase/simple-collection.c b/VocBase/simple-collection.c index b113a24e66..076fbeb2d7 100644 --- a/VocBase/simple-collection.c +++ b/VocBase/simple-collection.c @@ -55,7 +55,8 @@ static bool DeleteImmediateIndexes (TRI_sim_collection_t* collection, static TRI_index_t* CreateGeoIndexSimCollection (TRI_sim_collection_t* collection, char const* location, char const* latitude, - char const* longitude); + char const* longitude, + TRI_idx_iid iid); static uint64_t HashKeyHeader (TRI_associative_pointer_t* array, void const* key); @@ -351,8 +352,8 @@ static bool UpdateDocument (TRI_sim_collection_t* collection, update._rid = marker->_rid; update._data = *result; update._document._sid = ((TRI_doc_document_marker_t*) marker)->_shape; - update._document._data.length = ((TRI_df_marker_t const*) (header->_data))->_size - sizeof(TRI_doc_document_marker_t); - update._document._data.data = ((char*) header->_data) + sizeof(TRI_doc_document_marker_t); + update._document._data.length = ((TRI_df_marker_t const*) (update._data))->_size - sizeof(TRI_doc_document_marker_t); + update._document._data.data = ((char*) update._data) + sizeof(TRI_doc_document_marker_t); UpdateImmediateIndexes(collection, header, &update); } @@ -650,7 +651,9 @@ static void OpenIterator (TRI_df_marker_t const* marker, void* data, bool journa } } else if (marker->_type == TRI_DOC_MARKER_DELETION) { - TRI_doc_deletion_marker_t const* d = (TRI_doc_deletion_marker_t const*) marker; + TRI_doc_deletion_marker_t const* d; + + d = (TRI_doc_deletion_marker_t const*) marker; LOG_TRACE("deletion did %lu, rid %lu, deletion %lu", (unsigned long) d->_did, @@ -692,11 +695,13 @@ static void OpenIterator (TRI_df_marker_t const* marker, void* data, bool journa static void OpenIndex (char const* filename, void* data) { TRI_sim_collection_t* doc; + TRI_idx_iid iid; TRI_json_t* json; TRI_json_t* loc; TRI_json_t* lat; TRI_json_t* lon; TRI_json_t* type; + TRI_json_t* iis; char const* typeStr; char* error; @@ -729,12 +734,18 @@ static void OpenIndex (char const* filename, void* data) { loc = TRI_LookupArrayJson(json, "location"); lat = TRI_LookupArrayJson(json, "latitude"); lon = TRI_LookupArrayJson(json, "longitude"); + iis = TRI_LookupArrayJson(json, "iid"); + iid = 0; + + if (iis != NULL && loc->_type == TRI_JSON_NUMBER) { + iid = loc->_value._number; + } if (loc != NULL && loc->_type == TRI_JSON_STRING) { - CreateGeoIndexSimCollection(doc, loc->_value._string.data, NULL, NULL); + CreateGeoIndexSimCollection(doc, loc->_value._string.data, NULL, NULL, iid); } else if (lat != NULL && lat->_type == TRI_JSON_STRING && lon != NULL && lon ->_type == TRI_JSON_STRING) { - CreateGeoIndexSimCollection(doc, NULL, lat->_value._string.data, lon->_value._string.data); + CreateGeoIndexSimCollection(doc, NULL, lat->_value._string.data, lon->_value._string.data, iid); } else { LOG_WARNING("ignore geo-index, need either 'location' or 'latitude' and 'longitude'"); @@ -770,6 +781,7 @@ static void InitSimCollection (TRI_sim_collection_t* collection, TRI_InitCondition(&collection->_journalsCondition); TRI_InitVectorPointer(&collection->_indexes); + collection->_geoIndex = NULL; collection->base.beginRead = BeginRead; collection->base.endRead = EndRead; @@ -1058,7 +1070,43 @@ static bool CreateImmediateIndexes (TRI_sim_collection_t* collection, static bool UpdateImmediateIndexes (TRI_sim_collection_t* collection, TRI_doc_mptr_t const* header, TRI_doc_mptr_t const* update) { -#warning TODO + + union { TRI_doc_mptr_t const* c; TRI_doc_mptr_t* v; } change; + TRI_shaped_json_t old; + bool ok; + bool result; + size_t i; + size_t n; + + // get the old document + old = header->_document; + + // update all fields, the document identifier stays the same + change.c = header; + + change.v->_rid = update->_rid; + change.v->_eid = update->_eid; + change.v->_deletion = update->_deletion; + + change.v->_data = update->_data; + change.v->_document = update->_document; + + // update all other indexes + n = TRI_SizeVectorPointer(&collection->_indexes); + result = true; + + for (i = 0; i < n; ++i) { + TRI_index_t* idx; + + idx = TRI_AtVectorPointer(&collection->_indexes, i); + + ok = idx->update(idx, header, &old); + + if (! ok) { + result = false; + } + } + return true; } @@ -1069,7 +1117,45 @@ static bool UpdateImmediateIndexes (TRI_sim_collection_t* collection, static bool DeleteImmediateIndexes (TRI_sim_collection_t* collection, TRI_doc_mptr_t const* header, TRI_voc_tick_t deletion) { -#warning TODO + union { TRI_doc_mptr_t const* c; TRI_doc_mptr_t* v; } change; + TRI_doc_mptr_t* found; + size_t n; + size_t i; + bool result; + bool ok; + + // set the deletion flag + change.c = header; + change.v->_deletion = deletion; + + // remove from main index + found = TRI_RemoveKeyAssociativePointer(&collection->_primaryIndex, &header->_did); + + if (found == NULL) { + TRI_set_errno(TRI_VOC_ERROR_DOCUMENT_NOT_FOUND); + return false; + } + + // remove all other indexes + n = TRI_SizeVectorPointer(&collection->_indexes); + result = true; + + for (i = 0; i < n; ++i) { + TRI_index_t* idx; + + idx = TRI_AtVectorPointer(&collection->_indexes, i); + + ok = idx->remove(idx, header); + + if (! ok) { + result = false; + } + } + + // and release the header pointer + collection->_headers->release(collection->_headers, change.v); + + // that's it return true; } @@ -1176,7 +1262,8 @@ static bool EqualKeyDocument (TRI_associative_pointer_t* array, void const* key, static TRI_index_t* CreateGeoIndexSimCollection (TRI_sim_collection_t* collection, char const* location, char const* latitude, - char const* longitude) { + char const* longitude, + TRI_idx_iid iid) { TRI_index_t* idx; TRI_shape_pid_t lat; TRI_shape_pid_t loc; @@ -1204,10 +1291,10 @@ static TRI_index_t* CreateGeoIndexSimCollection (TRI_sim_collection_t* collectio // check, if we know the index if (location != NULL) { - idx = TRI_LookupGeoIndexSimCollection(collection, location); + idx = TRI_LookupGeoIndexSimCollection(collection, loc); } else if (longitude != NULL && latitude != NULL) { - idx = TRI_LookupGeoIndex2SimCollection(collection, latitude, longitude); + idx = TRI_LookupGeoIndex2SimCollection(collection, lat, lon); } else { TRI_set_errno(TRI_VOC_ERROR_ILLEGAL_PARAMETER); @@ -1242,12 +1329,23 @@ static TRI_index_t* CreateGeoIndexSimCollection (TRI_sim_collection_t* collectio (unsigned long) lon); } + if (iid) { + idx->_iid = iid; + } + // initialises the index with all existing documents FillIndex(collection, idx); // and store index TRI_PushBackVectorPointer(&collection->_indexes, idx); + if (collection->_geoIndex == NULL) { + collection->_geoIndex = (TRI_geo_index_t*) idx; + } + else if (collection->_geoIndex->base._iid > idx->_iid) { + collection->_geoIndex = (TRI_geo_index_t*) idx; + } + return idx; } @@ -1269,15 +1367,11 @@ static TRI_index_t* CreateGeoIndexSimCollection (TRI_sim_collection_t* collectio //////////////////////////////////////////////////////////////////////////////// TRI_index_t* TRI_LookupGeoIndexSimCollection (TRI_sim_collection_t* collection, - char const* location) { - TRI_shape_pid_t loc; - TRI_shaper_t* shaper; + TRI_shape_pid_t location) { size_t n; size_t i; n = TRI_SizeVectorPointer(&collection->_indexes); - shaper = collection->base._shaper; - loc = shaper->findAttributePathByName(shaper, location); for (i = 0; i < n; ++i) { TRI_index_t* idx; @@ -1287,7 +1381,7 @@ TRI_index_t* TRI_LookupGeoIndexSimCollection (TRI_sim_collection_t* collection, if (idx->_type == TRI_IDX_TYPE_GEO_INDEX) { TRI_geo_index_t* geo = (TRI_geo_index_t*) idx; - if (geo->_location == loc) { + if (geo->_location != 0 && geo->_location == location) { return idx; } } @@ -1301,18 +1395,12 @@ TRI_index_t* TRI_LookupGeoIndexSimCollection (TRI_sim_collection_t* collection, //////////////////////////////////////////////////////////////////////////////// TRI_index_t* TRI_LookupGeoIndex2SimCollection (TRI_sim_collection_t* collection, - char const* latitude, - char const* longitude) { - TRI_shape_pid_t lat; - TRI_shape_pid_t lon; - TRI_shaper_t* shaper; + TRI_shape_pid_t latitude, + TRI_shape_pid_t longitude) { size_t n; size_t i; n = TRI_SizeVectorPointer(&collection->_indexes); - shaper = collection->base._shaper; - lat = shaper->findAttributePathByName(shaper, latitude); - lon = shaper->findAttributePathByName(shaper, longitude); for (i = 0; i < n; ++i) { TRI_index_t* idx; @@ -1322,7 +1410,7 @@ TRI_index_t* TRI_LookupGeoIndex2SimCollection (TRI_sim_collection_t* collection, if (idx->_type == TRI_IDX_TYPE_GEO_INDEX) { TRI_geo_index_t* geo = (TRI_geo_index_t*) idx; - if (geo->_latitude == lat && geo->_longitude == lon) { + if (geo->_latitude != 0 && geo->_longitude != 0 && geo->_latitude == latitude && geo->_longitude == longitude) { return idx; } } @@ -1346,7 +1434,7 @@ bool TRI_EnsureGeoIndexSimCollection (TRI_sim_collection_t* collection, TRI_WriteLockReadWriteLock(&collection->_lock); - idx = CreateGeoIndexSimCollection(collection, location, NULL, NULL); + idx = CreateGeoIndexSimCollection(collection, location, NULL, NULL, 0); if (idx == NULL) { TRI_WriteUnlockReadWriteLock(&collection->_lock); @@ -1385,7 +1473,7 @@ bool TRI_EnsureGeoIndex2SimCollection (TRI_sim_collection_t* collection, TRI_WriteLockReadWriteLock(&collection->_lock); - idx = CreateGeoIndexSimCollection(collection, NULL, latitude, longitude); + idx = CreateGeoIndexSimCollection(collection, NULL, latitude, longitude, 0); if (idx == NULL) { TRI_WriteUnlockReadWriteLock(&collection->_lock); diff --git a/VocBase/simple-collection.h b/VocBase/simple-collection.h index fc1a5effe7..bebcc35da4 100644 --- a/VocBase/simple-collection.h +++ b/VocBase/simple-collection.h @@ -41,6 +41,7 @@ extern "C" { // ----------------------------------------------------------------------------- struct TRI_index_s; +struct TRI_geo_index_s; // ----------------------------------------------------------------------------- // --SECTION-- public types @@ -71,6 +72,7 @@ typedef struct TRI_sim_collection_s { TRI_headers_t* _headers; TRI_associative_pointer_t _primaryIndex; TRI_vector_pointer_t _indexes; + struct TRI_geo_index_s* _geoIndex; // ............................................................................. // this condition variable protects the _journals @@ -176,7 +178,7 @@ bool TRI_CloseSimCollection (TRI_sim_collection_t* collection); //////////////////////////////////////////////////////////////////////////////// struct TRI_index_s* TRI_LookupGeoIndexSimCollection (struct TRI_sim_collection_s* collection, - char const* location); + TRI_shape_pid_t location); //////////////////////////////////////////////////////////////////////////////// /// @brief finds a geo index @@ -185,8 +187,8 @@ struct TRI_index_s* TRI_LookupGeoIndexSimCollection (struct TRI_sim_collection_s //////////////////////////////////////////////////////////////////////////////// struct TRI_index_s* TRI_LookupGeoIndex2SimCollection (struct TRI_sim_collection_s* collection, - char const* latitude, - char const* longitude); + TRI_shape_pid_t latitude, + TRI_shape_pid_t longitude); //////////////////////////////////////////////////////////////////////////////// /// @brief ensures that a geo index exists diff --git a/VocBase/write-slots.c b/VocBase/write-slots.c deleted file mode 100644 index 4828a33746..0000000000 --- a/VocBase/write-slots.c +++ /dev/null @@ -1,192 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// @brief write slots -/// -/// @file -/// -/// DISCLAIMER -/// -/// Copyright 2010-2011 triagens GmbH, Cologne, Germany -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// -/// Copyright holder is triAGENS GmbH, Cologne, Germany -/// -/// @author Dr. Frank Celler -/// @author Copyright 2011-2010, triAGENS GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -#include "write-slots.h" - -// ----------------------------------------------------------------------------- -// --SECTION-- public functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup VocBase VocBase -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief initialses a write slots -//////////////////////////////////////////////////////////////////////////////// - -void TRI_InitWriteSlots (TRI_col_write_slots_t* slots) { - size_t i; - TRI_col_write_slot_t* next; - TRI_col_write_slot_t* last = NULL; - - for (i = 0; i < TRI_COL_WRITE_SLOTS; ++i) { - next = TRI_Allocate(sizeof(TRI_col_write_slot_t)); - - next->_begin = NULL; - next->_end = NULL; - next->_backward = NULL; - next->_forward = last; - - last = next; - } - - slots->_free = last; - slots->_used = NULL; - - slots->_position = NULL; - slots->_synced = NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief destroys the writes slots, but does not free the pointer -//////////////////////////////////////////////////////////////////////////////// - -void TRI_DestroyWriteSlots (TRI_col_write_slots_t* slots) { - TRI_col_write_slot_t* next; - TRI_col_write_slot_t* current; - - // clear free list - current = slots->_free; - - while (current != NULL) { - next = current->_forward; - TRI_Free(current); - current = next; - } - - // clear used list - current = slots->_used; - - while (current != 0) { - next = current->_forward; - TRI_Free(current); - current = next; - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief destroys the writes slots -//////////////////////////////////////////////////////////////////////////////// - -void TRI_FreeWriteSlots (TRI_col_write_slots_t* slots) { - TRI_DestroyWriteSlots(slots); - TRI_Free(slots); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief acquires a free write slot, returns 0 if none is free -//////////////////////////////////////////////////////////////////////////////// - -TRI_col_write_slot_t* TRI_AcquireWriteSlots (TRI_col_write_slots_t* slots, void* begin, void* end) { - TRI_col_write_slot_t* slot; - TRI_col_write_slot_t* used; - - if (slots->_free == NULL) { - return NULL; - } - - slot = slots->_free; - slots->_free = slot->_forward; - - slot->_forward = NULL; - slot->_begin = begin; - slot->_end = end; - - if (slots->_used == NULL) { - slot->_backward = NULL; - - slots->_used = slot; - } - else { - used = slots->_used; - - while (used->_forward != NULL) { - used = used->_forward; - } - - used->_forward = slot; - slot->_backward = used; - } - - if (slots->_synced == NULL) { - slots->_synced = begin; - } - - return slot; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief releases a write slot -//////////////////////////////////////////////////////////////////////////////// - -void* TRI_ReleaseWriteSlots (TRI_col_write_slots_t* slots, TRI_col_write_slot_t* slot) { - void* position; - - // correct the predecessor - if (slot->_backward == NULL) { - slots->_used = slot->_forward; - - // correct the position - if (slots->_used == NULL) { - slots->_position = slot->_end; - } - else { - slots->_position = slots->_used->_begin; - } - } - else { - slot->_backward->_forward = slot->_forward; - } - - // correct the successor - if (slot->_forward != NULL) { - slot->_forward->_backward = slot->_backward; - } - - // put back onto free list - slot->_backward = NULL; - slot->_begin = NULL; - slot->_end = NULL; - - slot->_forward = slots->_free; - slots->_free = slot; - - position = slots->_position; - - return position; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// Local Variables: -// mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\)" -// End: diff --git a/VocBase/write-slots.h b/VocBase/write-slots.h deleted file mode 100644 index 7f4cf7e381..0000000000 --- a/VocBase/write-slots.h +++ /dev/null @@ -1,148 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// @brief write slots -/// -/// @file -/// -/// DISCLAIMER -/// -/// Copyright 2010-2011 triagens GmbH, Cologne, Germany -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// -/// Copyright holder is triAGENS GmbH, Cologne, Germany -/// -/// @author Dr. Frank Celler -/// @author Copyright 2011-2010, triAGENS GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -#ifndef TRIAGENS_DURHAM_VOCBASE_WRITE_SLOTS_H -#define TRIAGENS_DURHAM_VOCBASE_WRITE_SLOTS_H 1 - -#include - -#include -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// ----------------------------------------------------------------------------- -// --SECTION-- public constants -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup VocBase VocBase -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief default write slots -//////////////////////////////////////////////////////////////////////////////// - -#define TRI_COL_WRITE_SLOTS (10) - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -// ----------------------------------------------------------------------------- -// --SECTION-- public types -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup VocBase VocBase -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief single write slot -//////////////////////////////////////////////////////////////////////////////// - -typedef struct TRI_col_write_slot_s { - struct TRI_col_write_slot_s* _forward; - struct TRI_col_write_slot_s* _backward; - - void* _begin; - void* _end; // excluding -} -TRI_col_write_slot_t; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief write slots -//////////////////////////////////////////////////////////////////////////////// - -typedef struct TRI_col_write_slots_s { - TRI_col_write_slot_t* _free; - TRI_col_write_slot_t* _used; - - void* _position; // position of finished writes (excluding) - void* _synced; // position of synced writes (excluding) -} -TRI_col_write_slots_t; - -// ----------------------------------------------------------------------------- -// --SECTION-- public functions -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup VocBase VocBase -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief initialses a write slots -//////////////////////////////////////////////////////////////////////////////// - -void TRI_InitWriteSlots (TRI_col_write_slots_t*); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief destroys the writes slots, but does not free the pointer -//////////////////////////////////////////////////////////////////////////////// - -void TRI_DestroyWriteSlots (TRI_col_write_slots_t*); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief destroys the writes slots -//////////////////////////////////////////////////////////////////////////////// - -void TRI_FreeWriteSlots (TRI_col_write_slots_t*); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief acquires a free write slot, returns 0 if none is free -//////////////////////////////////////////////////////////////////////////////// - -TRI_col_write_slot_t* TRI_AcquireWriteSlots (TRI_col_write_slots_t*, void* begin, void* end); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief releases a write slot -//////////////////////////////////////////////////////////////////////////////// - -void* TRI_ReleaseWriteSlots (TRI_col_write_slots_t*, TRI_col_write_slot_t*); - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -} -#endif - -#endif - -// Local Variables: -// mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\)" -// End: diff --git a/build.h b/build.h index 8821702714..065342a506 100644 --- a/build.h +++ b/build.h @@ -1 +1 @@ -#define TRIAGENS_VERSION "0.0.1 (9334:9405)" +#define TRIAGENS_VERSION "0.0.1 (9427)"