partial fix for 687105 setcolorspace optimization problem
"Dan Coby" <[email protected]> Sat, 19 Jun 2004 00:35:26 -0700
| Newsgroups | gmane.comp.printing.ghostscript.patches |
|---|---|
| Message-ID | <[email protected]> |
Partial fix for 687105 setcolorspace optimization problem. DETAILS: Currently the PostScript interpreter checks to see if a color space is changed whenever a call is made to setcolorspace. This greatly increase throughput on some files which set the same color space before every drawing operation. However the logic for comparing if two color spaces are the same is not perfect. It misses the case where a color space is specified by an array and one or more of the elements in the array is changed afterward. This change creates a new PostScript operator which calculates an MD5 sum for any PostScript object. The description of this operator is in src/zmd5.c. See the comments prior to zobjectmd5sum. Two PostScript objects can be compared for equality by first creating an MD5 sum for each object and then comparing the two sums. Since this operator may be used for other purposes besides comparing color spaces, I am submitting just this operator to code review. Dan _______________________________________________ gs-code-review mailing list [email protected] http://www.ghostscript.com/mailman/listinfo/gs-code-review
687105.txt
(text/plain, 1.9 KB)
Index: src/int.mak =================================================================== RCS file: /cvs/ghostscript/gs/src/int.mak,v retrieving revision 1.126 diff -u -r1.126 int.mak --- src/int.mak 20 May 2004 07:34:10 -0000 1.126 +++ src/int.mak 19 Jun 2004 06:41:38 -0000 @@ -367,6 +367,12 @@ $(PSOBJ)zmath.$(OBJ) : $(PSSRC)zmath.c $(OP) $(math__h) $(gxfarith_h) $(store_h) $(PSCC) $(PSO_)zmath.$(OBJ) $(C_) $(PSSRC)zmath.c +# MD5 object operator +$(PSOBJ)zmd5.$(OBJ) : $(PSSRC)zmd5.c $(OP) $(memory__h) $(gsstruct_h)\ + $(dstack_h) $(estack_h) $(iddict_h) $(iname_h) $(ipacked_h) $(ivmspace_h)\ + $(store_h) $(md5_h) + $(PSCC) $(PSO_)zmd5.$(OBJ) $(C_) $(PSSRC)zmd5.c + $(PSOBJ)zmisc.$(OBJ) : $(PSSRC)zmisc.c $(OP) $(gscdefs_h) $(gp_h)\ $(errno__h) $(memory__h) $(string__h)\ $(ialloc_h) $(idict_h) $(dstack_h) $(iname_h) $(ivmspace_h) $(ipacked_h) $(store_h) @@ -508,7 +514,7 @@ Z2=$(PSOBJ)zdict.$(OBJ) $(PSOBJ)zfile.$(OBJ) $(PSOBJ)zfile1.$(OBJ) $(PSOBJ)zfileio.$(OBJ) Z3=$(PSOBJ)zfilter.$(OBJ) $(PSOBJ)zfproc.$(OBJ) $(PSOBJ)zgeneric.$(OBJ) Z4=$(PSOBJ)ziodev.$(OBJ) $(PSOBJ)ziodevs$(STDIO_IMPLEMENTATION).$(OBJ) $(PSOBJ)zmath.$(OBJ) -Z5=$(PSOBJ)zmisc.$(OBJ) $(PSOBJ)zpacked.$(OBJ) $(PSOBJ)zrelbit.$(OBJ) +Z5=$(PSOBJ)zmd5.$(OBJ) $(PSOBJ)zmisc.$(OBJ) $(PSOBJ)zpacked.$(OBJ) $(PSOBJ)zrelbit.$(OBJ) Z6=$(PSOBJ)zstack.$(OBJ) $(PSOBJ)zstring.$(OBJ) $(PSOBJ)zsysvm.$(OBJ) Z7=$(PSOBJ)ztoken.$(OBJ) $(PSOBJ)ztype.$(OBJ) $(PSOBJ)zvmem.$(OBJ) Z8=$(PSOBJ)zbfont.$(OBJ) $(PSOBJ)zchar.$(OBJ) $(PSOBJ)zcolor.$(OBJ) @@ -518,7 +524,7 @@ Z1OPS=zarith zarray zcontrol1 zcontrol2 zcontrol3 Z2OPS=zdict1 zdict2 zfile zfile1 zfileio1 zfileio2 Z3_4OPS=zfilter zfproc zgeneric ziodev zmath -Z5_6OPS=zmisc zpacked zrelbit zstack zstring zsysvm +Z5_6OPS=zmd5 zmisc zpacked zrelbit zstack zstring zsysvm Z7_8OPS=ztoken ztype zvmem zbfont zchar zcolor Z9OPS=zdevice zfont zfontenum zgstate1 zgstate2 zgstate3 Z10OPS=zdfilter zht zimage zmatrix
zmd5.c
(text/plain, 9.2 KB)
/* Copyright (C) Artifex Software Inc. and artofcode LLC. All rights reserved.
This software is provided AS-IS with no warranty, either express or
implied.
This software is distributed under license and may not be copied,
modified or distributed except as expressly authorized under the terms
of the license contained in the file LICENSE in this distribution.
For more information about licensing, please refer to
http://www.ghostscript.com/licensing/. For information on
commercial licensing, go to http://www.artifex.com/licensing/ or
contact Artifex Software, Inc., 101 Lucas Valley Road #110,
San Rafael, CA 94903, U.S.A., +1(415)492-9861.
*/
/* $Id$ */
/* MD5 sum related operators */
#include "memory_.h"
#include "ghost.h"
#include "oper.h"
#include "iddict.h"
#include "store.h"
#include "md5.h"
#include "gsutil.h"
#include <stdlib.h>
/*
* Enable a debug printout of the object.
*
* Note: When debugging, DEBUG_MD5 = 1, due to the simple handling of
* the t_string type, strings are not shown with escape sequences, etc.
*/
#define DEBUG_MD5 0
/* Forward references. */
private int calc_objectmd5sum(s_ptr pobj, md5_state_t *pms,
int depth, gs_memory_t *mem);
/*
* We want to be able to sort keys in a dictionary. qsort only passes
* a pair of pointers to the compare routine. We need both the index
* and the pointer to the dictionary inside of the compare routine.
* Thus we are creating a strcture with both of these. We will pass
* pointers to the structure.
*/
typedef struct {
int index;
ref * pdictionary;
} dict_sort_record;
/*
* Compare dictionary key names. Since it is not necessary to use any
* special criteria for sorting, we are using a very simple compare.
*/
private int
compare_key_names(const void *p1, const void *p2)
{
ref * pdict = ((const dict_sort_record *)p1)->pdictionary;
int index1 = ((const dict_sort_record *)p1)->index;
int index2 = ((const dict_sort_record *)p2)->index;
uint length1, length2;
byte * ptr1, * ptr2;
ref rvalue[2];
dict_index_entry(pdict, index1, rvalue);
obj_string_data(&rvalue[0], (const byte **)&ptr1, &length1);
dict_index_entry(pdict, index2, rvalue);
obj_string_data(&rvalue[0], (const byte **)&ptr2, &length2);
return bytes_compare(ptr1, length1, ptr2, length2);
}
/*
* This pair of routines serve as common points for debugging our MD5
* sum. The first accepts a zero terminated string. The second accepts
* a data pointer and length.
*/
private void
md5_append_str(md5_state_t * pms, const char * str)
{
#if DEBUG_MD5
dprintf(str);
#endif
md5_append(pms, (const md5_byte_t *)str, strlen(str));
}
private void
md5_append_data(md5_state_t *pms, const char * data, int length)
{
#if DEBUG_MD5
{
int i;
for(i = 0; i < length; i++)
dprintf1("%c", data[i]);
}
#endif
md5_append(pms, (const md5_byte_t *)data, length);
}
#undef DEBUG_MD5
/*
* Calculate MD5 sum for a PostScript dictionary. Dictionaries are a
* special case of the Postscript objects. The ordering of dictionary
* objects are based upon a hash. Thus separate dictionaries with the
* same keys and object values may have different orders. To prevent
* differences in the MD5 sum, we are forcing an ordering on dicts
* based upon sorting the key names.
*
* See comments before calc_objectmd5sum.
*/
private int
calc_dict_md5sum(s_ptr pdict, md5_state_t *pms, int depth, gs_memory_t *mem)
{
int size, code, i, n;
ref rvalue[2];
#define LOCAL_STORAGE 20
dict_sort_record local_indexes[LOCAL_STORAGE];
dict_sort_record * pindexlist = local_indexes;
size = dict_length(pdict);
/* Use fixed list for small dicts. Allocate list for large dicts. */
if (size > LOCAL_STORAGE) {
pindexlist = (dict_sort_record *) gs_alloc_byte_array(mem, size,
sizeof(dict_sort_record), "calc_dict_md5sum");
if (!pindexlist)
return_error(e_VMerror);
}
/* Initialize our list of dictionary indexes */
for (i = n = 0; i < size; i++) {
/* Skip any entries which are not defined */
code = dict_index_entry(pdict, i, rvalue);
if (code != e_undefined) {
pindexlist[n].index = i;
pindexlist[n++].pdictionary = pdict;
}
}
size = n;
/* Sort the dictionary items by key name */
qsort(pindexlist, size, sizeof(dict_sort_record), compare_key_names);
/* Add dictionary items to the MD5 sum */
md5_append_str(pms, "<<");
for (i = 0; i < size; i++) {
if (i > 0)
md5_append_str(pms, " ");
code = dict_index_entry(pdict, pindexlist[i].index, rvalue);
if (code < 0)
return code;
code = calc_objectmd5sum(&rvalue[0], pms, depth - 1, mem); /* key */
if (code < 0)
return code;
md5_append_str(pms, " ");
code = calc_objectmd5sum(&rvalue[1], pms, depth - 1, mem); /* value */
if (code < 0)
return code;
}
md5_append_str(pms, ">>");
/* Release sort list if we had to allocate it */
if (pindexlist != local_indexes)
gs_free_object(mem, pindexlist, "calc_dict_md5sum");
return 0;
}
/*
* Calculate MD5 sum for a PostScript object. This routine is called
* recursively to process each object within a dictionary or an array.
*
* See comments before zobjectmd5sum.
*
* For most of the work of this routine, we call upon obj_cvp to give us
* a string version of the object. We then use that string to calculate
* the MD5 sum for the object. We treat arrays, strings, and dictionaries
* as special cases. For arrays and strings, it is simplier to not have
* to worry about the complex protocol required with obj_cvp if the text
* version of the object is larger than the given buffer. Thus we give
* obj_cvp only 'simple' objects. With dictionaries we have the added
* complication that we want repeatable results no matter what the
* order of the objects in the dictionary. See calc_dict_md5sum.
*
* Note: Bound operators in executable arrays are considered different
* from the unbound names of the same operators. I.e. the following
* two objects will have different MD5 sums.
* { pop }
* { pop } bind
*/
private int
calc_objectmd5sum(s_ptr pobj, md5_state_t *pms, int depth, gs_memory_t *mem)
{
uint size, i;
int code;
ref rvalue;
char buf[50]; /* big enough for any float, double, or struct name */
bool executable;
/* if we have reached the maximum depth then simply exit. */
if (depth <= 0)
return 0;
/* Calculate MD5 sum for this object element */
switch (r_type(pobj)) {
case t_dictionary:
check_dict_read(*pobj);
calc_dict_md5sum(pobj, pms, depth, mem);
break;
case t_array:
case t_mixedarray:
case t_shortarray:
executable = r_has_attr(pobj, a_executable);
md5_append_str(pms, executable ? "{" :"[");
size = r_size(pobj);
for (i = 0; i < size; i++) {
if (i > 0)
md5_append_str(pms, " ");
code = array_get(pobj, i, &rvalue);
if (code < 0)
return code;
calc_objectmd5sum(&rvalue, pms, depth - 1, mem);
}
md5_append_str(pms, executable ? "}" :"]");
break;
case t_string:
md5_append_str(pms, "(");
md5_append_data(pms, (char *)(pobj->value.bytes), r_size(pobj));
md5_append_str(pms, ")");
break;
default:
code = obj_cvp(pobj, (byte *)buf, sizeof(buf), &size, 2, 0, mem);
md5_append_data(pms, buf, size);
break;
}
return 0;
}
/*
* This operator calculates an MD5 sum for a Postscript object. It takes
* three parameters.
* string - This must be at least 32 characters. This will contain
* The resulting MD5 sum in ASCII hex.
* int - This specifies a maximum iteration depth. The purpose of this
* value is to prevent infinite recursion with dictionaries
* or arrays which contain elements which refer to the
* dictionary or array.
* object - This is the PostScript object for which the MD5 sum is
* to be calculated.
*/
/* <string> <int> <array|dict|name|packedarray|string> objectmd5sum <string> */
private int
zobjectmd5sum(i_ctx_t *i_ctx_p)
{
os_ptr op = osp;
os_ptr pint = osp - 1;
os_ptr pstring = pint - 1;
#define MD5_SUM_SIZE 16
md5_state_t ms;
md5_byte_t digest[MD5_SUM_SIZE];
int code, depth;
/* Verify that the string size is at least 32 characters. */
check_type(*pstring, t_string);
check_write(*pstring);
if (r_size(pstring) < 2 * MD5_SUM_SIZE)
return_error(e_rangecheck);
/* Verify that we have an integer value */
check_type(*pint, t_integer);
depth = pint->value.intval;
/* Calculate MD5 sum for the object */
md5_init(&ms); /* Initialize the algorithm. */
code = calc_objectmd5sum(op, &ms, depth, imemory);
if (code < 0)
return code;
md5_finish(&ms, digest); /* Finish the message and return the digest. */
/* Place the sum in our output string */
{
const char *const hex_digits = "0123456789abcdef";
md5_byte_t * src = digest;
byte * dest = pstring->value.bytes;
md5_byte_t data;
int i;
for (i = 0; i < MD5_SUM_SIZE; i++) {
data = *src++;
*dest++ = hex_digits[(data >> 4) & 0xf];
*dest++ = hex_digits[data & 0xf];
}
}
pop(2); /* Remove object and count */
return 0;
}
/* ------ Initialization procedure ------ */
const op_def zmd5_op_defs[] =
{
{"3.objectmd5sum", zobjectmd5sum},
op_def_end(0)
};