1 /**
2  * Copyright 2015-2017 DataStax, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "php_driver.h"
18 #include "php_driver_types.h"
19 #include "util/hash.h"
20 
21 static inline cass_int64_t
double_to_bits(cass_double_t value)22 double_to_bits(cass_double_t value) {
23   cass_int64_t bits;
24   if (zend_isnan(value)) return 0x7ff8000000000000LL; /* A canonical NaN value */
25   memcpy(&bits, &value, sizeof(cass_int64_t));
26   return bits;
27 }
28 
29 static inline unsigned
double_hash(cass_double_t value)30 double_hash(cass_double_t value) {
31   return php_driver_bigint_hash(double_to_bits(value));
32 }
33 
34 unsigned
php_driver_value_hash(zval * zvalue TSRMLS_DC)35 php_driver_value_hash(zval* zvalue TSRMLS_DC) {
36   switch (Z_TYPE_P(zvalue)) {
37   case IS_LONG:
38 #if SIZEOF_LONG == 4
39     return Z_LVAL_P(zvalue);
40 #elif SIZEOF_LONG == 8
41     return php_driver_bigint_hash(Z_LVAL_P(zvalue));
42 #else
43 #error "Unexpected sizeof(long)"
44 #endif
45 
46   case IS_DOUBLE: return double_hash(Z_DVAL_P(zvalue));
47 
48 #if PHP_MAJOR_VERSION >= 7
49   case IS_TRUE: return 1;
50   case IS_FALSE: return 0;
51 #else
52   case IS_BOOL: return Z_BVAL_P(zvalue);
53 #endif
54 
55   case IS_STRING:
56     return zend_inline_hash_func(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue));
57 
58 #if PHP_MAJOR_VERSION >= 7
59   case IS_OBJECT:
60     return ((php_driver_value_handlers *)Z_OBJ_P(zvalue)->handlers)->hash_value(zvalue TSRMLS_CC);
61 #else
62   case IS_OBJECT:
63     return ((php_driver_value_handlers *)Z_OBJVAL_P(zvalue).handlers)->hash_value(zvalue TSRMLS_CC);
64 #endif
65 
66   default:
67     break;
68   }
69 
70   return 0;
71 }
72 
73 static inline int
double_compare(cass_double_t d1,cass_double_t d2)74 double_compare(cass_double_t d1, cass_double_t d2) {
75   cass_int64_t bits1, bits2;
76   if (d1 < d2) return -1;
77   if (d1 > d2) return  1;
78   bits1 = double_to_bits(d1);
79   bits2 = double_to_bits(d2);
80   /* Handle NaNs and negative and positive 0.0 */
81   return PHP_DRIVER_COMPARE(bits1, bits2);
82 }
83 
84 int
php_driver_value_compare(zval * zvalue1,zval * zvalue2 TSRMLS_DC)85 php_driver_value_compare(zval* zvalue1, zval* zvalue2 TSRMLS_DC) {
86   if (zvalue1 == zvalue2) return 0;
87 
88   if (Z_TYPE_P(zvalue1) != Z_TYPE_P(zvalue2)) {
89     return Z_TYPE_P(zvalue1)  < Z_TYPE_P(zvalue2) ? -1 : 1;
90   }
91 
92   switch (Z_TYPE_P(zvalue1)) {
93   case IS_NULL:
94       return 0;
95 
96   case IS_LONG:
97     return PHP_DRIVER_COMPARE(Z_LVAL_P(zvalue1), Z_LVAL_P(zvalue2));
98 
99   case IS_DOUBLE:
100     return double_compare(Z_DVAL_P(zvalue1), Z_DVAL_P(zvalue2));
101 
102 #if PHP_MAJOR_VERSION >= 7
103   case IS_TRUE:
104     return Z_TYPE_P(zvalue2) == IS_TRUE ? 0 : 1;
105 
106   case IS_FALSE:
107     return Z_TYPE_P(zvalue2) == IS_FALSE ? 0 : -1;
108 #else
109   case IS_BOOL:
110     return PHP_DRIVER_COMPARE(Z_BVAL_P(zvalue1), Z_BVAL_P(zvalue2));
111 #endif
112 
113   case IS_STRING:
114     return zend_binary_zval_strcmp(zvalue1, zvalue2);
115 
116 #if PHP_MAJOR_VERSION >= 7
117   case IS_OBJECT:
118     return Z_OBJ_P(zvalue1)->handlers->compare_objects(zvalue1, zvalue2 TSRMLS_CC);
119 #else
120   case IS_OBJECT:
121     return Z_OBJVAL_P(zvalue1).handlers->compare_objects(zvalue1, zvalue2 TSRMLS_CC);
122 #endif
123 
124   default:
125     break;
126   }
127 
128   return 1;
129 }
130 
php_driver_data_compare(const void * a,const void * b TSRMLS_DC)131 int php_driver_data_compare(const void* a, const void* b TSRMLS_DC) {
132   Bucket *f, *s;
133   zval *first, *second;
134 
135 #if PHP_MAJOR_VERSION >= 7
136   f = (Bucket *)a;
137   s = (Bucket *)b;
138   first = &f->val;
139   second = &s->val;
140 #else
141   f = *((Bucket **) a);
142   s = *((Bucket **) b);
143   first = *((zval **) f->pData);
144   second = *((zval **) s->pData);
145 #endif
146 
147   return php_driver_value_compare(first, second TSRMLS_CC);
148 }
149 
150 unsigned
php_driver_mpz_hash(unsigned seed,mpz_t n)151 php_driver_mpz_hash(unsigned seed, mpz_t n) {
152   size_t i;
153   size_t size = mpz_size(n);
154   unsigned hashv = seed;
155 #if GMP_LIMB_BITS == 32
156     for (i = 0; i < size; ++i) {
157       hashv = php_driver_combine_hash(hashv, mpz_getlimbn(n, i));
158     }
159 #elif GMP_LIMB_BITS == 64
160     for (i = 0; i < size; ++i) {
161       hashv = php_driver_combine_hash(hashv, php_driver_bigint_hash(mpz_getlimbn(n, i)));
162     }
163 #else
164 #error "Unexpected GMP limb bits size"
165 #endif
166     return hashv;
167 }
168