@@ -185,7 +185,44 @@ vips_value_hash(GParamSpec *pspec, const GValue *value)
185185 else if (generic == G_TYPE_PARAM_BOXED ) {
186186 void * p = g_value_get_boxed (value );
187187
188- return p ? g_direct_hash (p ) : 0 ;
188+ // array_object is an internal type, don't have a case for this
189+ if (!p )
190+ return 0 ;
191+ else if (G_VALUE_TYPE (value ) == VIPS_TYPE_ARRAY_INT ) {
192+ int n ;
193+ int * array = (int * )
194+ vips_area_get_data (VIPS_AREA (p ), NULL , & n , NULL , NULL );
195+
196+ guint hash = 0 ;
197+ for (int i = 0 ; i < n ; i ++ )
198+ hash = (hash << 1 ) ^ g_int_hash (& array [i ]);
199+
200+ return hash ;
201+ }
202+ else if (G_VALUE_TYPE (value ) == VIPS_TYPE_ARRAY_DOUBLE ) {
203+ int n ;
204+ double * array = (double * )
205+ vips_area_get_data (VIPS_AREA (p ), NULL , & n , NULL , NULL );
206+
207+ guint hash = 0 ;
208+ for (int i = 0 ; i < n ; i ++ )
209+ hash = (hash << 1 ) ^ g_double_hash (& array [i ]);
210+
211+ return hash ;
212+ }
213+ else if (G_VALUE_TYPE (value ) == VIPS_TYPE_ARRAY_IMAGE ) {
214+ int n ;
215+ void * * array = (void * * )
216+ vips_area_get_data (VIPS_AREA (p ), NULL , & n , NULL , NULL );
217+
218+ guint hash = 0 ;
219+ for (int i = 0 ; i < n ; i ++ )
220+ hash = (hash << 1 ) ^ g_direct_hash (array [i ]);
221+
222+ return hash ;
223+ }
224+ else
225+ return g_direct_hash (p );
189226 }
190227 else if (generic == G_TYPE_PARAM_POINTER ) {
191228 void * p = g_value_get_pointer (value );
@@ -275,8 +312,77 @@ vips_value_equal(GParamSpec *pspec, const GValue *v1, const GValue *v2)
275312 else
276313 return s1 && s2 && strcmp (s1 , s2 ) == 0 ;
277314 }
278- if (generic == G_TYPE_PARAM_BOXED )
279- return g_value_get_boxed (v1 ) == g_value_get_boxed (v2 );
315+ if (generic == G_TYPE_PARAM_BOXED ) {
316+ void * p1 = g_value_get_boxed (v1 );
317+ void * p2 = g_value_get_boxed (v2 );
318+
319+ if (p1 == p2 )
320+ return TRUE;
321+ else if (!p1 || !p2 )
322+ return FALSE;
323+ else if (t1 == VIPS_TYPE_ARRAY_INT ) {
324+ int n1 ;
325+ int * array1 = (int * )
326+ vips_area_get_data (VIPS_AREA (p1 ), NULL , & n1 , NULL , NULL );
327+ int n2 ;
328+ int * array2 = (int * )
329+ vips_area_get_data (VIPS_AREA (p2 ), NULL , & n2 , NULL , NULL );
330+
331+ if (n1 != n2 )
332+ return FALSE;
333+ else if (array1 == array2 )
334+ return TRUE;
335+ else {
336+ for (int i = 0 ; i < n1 ; i ++ )
337+ if (array1 [i ] != array2 [i ])
338+ return FALSE;
339+
340+ return TRUE;
341+ }
342+ }
343+ else if (t1 == VIPS_TYPE_ARRAY_DOUBLE ) {
344+ int n1 ;
345+ double * array1 = (double * )
346+ vips_area_get_data (VIPS_AREA (p1 ), NULL , & n1 , NULL , NULL );
347+ int n2 ;
348+ double * array2 = (double * )
349+ vips_area_get_data (VIPS_AREA (p2 ), NULL , & n2 , NULL , NULL );
350+
351+ if (n1 != n2 )
352+ return FALSE;
353+ else if (array1 == array2 )
354+ return TRUE;
355+ else {
356+ for (int i = 0 ; i < n1 ; i ++ )
357+ if (array1 [i ] != array2 [i ])
358+ return FALSE;
359+
360+ return TRUE;
361+ }
362+ }
363+ else if (t1 == VIPS_TYPE_ARRAY_IMAGE ) {
364+ int n1 ;
365+ void * * array1 = (void * * )
366+ vips_area_get_data (VIPS_AREA (p1 ), NULL , & n1 , NULL , NULL );
367+ int n2 ;
368+ void * * array2 = (void * * )
369+ vips_area_get_data (VIPS_AREA (p2 ), NULL , & n2 , NULL , NULL );
370+
371+ if (n1 != n2 )
372+ return FALSE;
373+ else if (array1 == array2 )
374+ return TRUE;
375+ else {
376+ for (int i = 0 ; i < n1 ; i ++ )
377+ if (array1 [i ] != array2 [i ])
378+ return FALSE;
379+
380+ return TRUE;
381+ }
382+ }
383+ else
384+ return p1 == p2 ;
385+ }
280386 if (generic == G_TYPE_PARAM_POINTER )
281387 return g_value_get_pointer (v1 ) == g_value_get_pointer (v2 );
282388 if (generic == G_TYPE_PARAM_OBJECT )
@@ -815,6 +921,49 @@ vips_cache_trim(void)
815921 g_mutex_unlock (& vips_cache_lock );
816922}
817923
924+ #ifdef DEBUG_LEAK
925+ static void *
926+ vips_cache_find_differences (VipsObject * object ,
927+ GParamSpec * pspec ,
928+ VipsArgumentClass * argument_class ,
929+ VipsArgumentInstance * argument_instance ,
930+ void * a , void * b )
931+ {
932+ VipsOperation * operation_before = VIPS_OPERATION (a );
933+
934+ if ((argument_class -> flags & VIPS_ARGUMENT_CONSTRUCT ) &&
935+ (argument_class -> flags & VIPS_ARGUMENT_INPUT ) &&
936+ argument_instance -> assigned ) {
937+ const char * name = g_param_spec_get_name (pspec );
938+
939+ GValue value_before = G_VALUE_INIT ;
940+ g_object_get_property (G_OBJECT (operation_before ), name , & value_before );
941+ unsigned int hash_before = vips_value_hash (pspec , & value_before );
942+
943+ GValue value_after = G_VALUE_INIT ;
944+ g_object_get_property (G_OBJECT (object ), name , & value_after );
945+ unsigned int hash_after = vips_value_hash (pspec , & value_after );
946+
947+ if (hash_before != hash_after ) {
948+ g_warning ("arg \"%s\" has changed value during build" , name );
949+
950+ char * str_before = g_strdup_value_contents (& value_before );
951+ g_warning ("\tvalue before: %s" , str_before );
952+ g_free (str_before );
953+
954+ char * str_after = g_strdup_value_contents (& value_after );
955+ g_warning ("\tvalue after: %s" , str_after );
956+ g_free (str_after );
957+ }
958+
959+ g_value_unset (& value_before );
960+ g_value_unset (& value_after );
961+ }
962+
963+ return NULL ;
964+ }
965+ #endif /*DEBUG_LEAK*/
966+
818967/**
819968 * vips_cache_operation_buildp: (skip)
820969 * @operation: pointer to operation to lookup
@@ -901,6 +1050,11 @@ vips_cache_operation_buildp(VipsOperation **operation)
9011050 */
9021051 if (vips__leak ) {
9031052 hash_before = vips_operation_hash (* operation );
1053+
1054+ /* This isn't a deep copy, so it won't detect eg.
1055+ * operations modifying compound objects like VipsArrayInt.
1056+ * However, the hash_before value we save will.
1057+ */
9041058 operation_before = vips_operation_copy (* operation );
9051059 }
9061060#endif /*DEBUG_LEAK*/
@@ -912,25 +1066,14 @@ vips_cache_operation_buildp(VipsOperation **operation)
9121066 if (vips__leak &&
9131067 !(flags & VIPS_OPERATION_NOCACHE ) &&
9141068 hash_before != vips_operation_hash (* operation )) {
915- const char * name = (const char * )
916- vips_argument_map (VIPS_OBJECT (* operation ),
917- vips_object_equal_arg , operation_before , NULL );
918- VipsObject * object = VIPS_OBJECT (* operation );
919- VipsObjectClass * class = VIPS_OBJECT_GET_CLASS (object );
920-
9211069 char txt [256 ];
9221070 VipsBuf buf = VIPS_BUF_STATIC (txt );
1071+ vips_object_summary (VIPS_OBJECT (operation_before ), & buf );
1072+ g_warning ("vips_cache_operation_buildp: "
1073+ "arg mismatch on build of: %s" , vips_buf_all (& buf ));
9231074
924- VIPS_UNREF (operation_before );
925-
926- vips_object_summary_class (class , & buf );
927- vips_buf_appends (& buf , ", " );
928- vips_object_summary (object , & buf );
929- vips_buf_appends (& buf , ", " );
930- vips_error (class -> nickname , "arg \"%s\" changed during build, %s" ,
931- name , vips_buf_all (& buf ));
932-
933- return -1 ;
1075+ vips_argument_map (VIPS_OBJECT (* operation ),
1076+ vips_cache_find_differences , operation_before , NULL );
9341077 }
9351078
9361079 VIPS_UNREF (operation_before );
0 commit comments