static sdlPtr get_sdl_from_cache(const char *fn, const char *uri, time_t t, time_t *cached)
{
	sdlPtr sdl;
	time_t old_t;
	int  i, num_groups, num_types, num_elements, num_encoders, num_bindings, num_func;
	sdlFunctionPtr *functions = NULL;
	sdlBindingPtr *bindings;
	sdlTypePtr *types;
	encodePtr *encoders;
	const encode *enc;

	int f;
	struct stat st;
	char *in, *buf;

	f = open(fn, O_RDONLY|O_BINARY);
	if (f < 0) {
		return NULL;
	}
	if (fstat(f, &st) != 0) {
		close(f);
		return NULL;
	}
	buf = in = emalloc(st.st_size);
	if (read(f, in, st.st_size) != st.st_size) {
		close(f);
		efree(in);
		return NULL;
	}
	close(f);

	if (strncmp(in,"wsdl",4) != 0 || in[4] != WSDL_CACHE_VERSION || in[5] != '\0') {
		unlink(fn);
		efree(buf);
		return NULL;
	}
	in += 6;

	WSDL_CACHE_GET(old_t, time_t, &in);
	if (old_t < t) {
		unlink(fn);
		efree(buf);
		return NULL;
	}
	*cached = old_t;

	WSDL_CACHE_GET_INT(i, &in);
	if (i == 0 && strncmp(in, uri, i) != 0) {
		unlink(fn);
		efree(buf);
		return NULL;
	}
	WSDL_CACHE_SKIP(i, &in);

	sdl = emalloc(sizeof(*sdl));
	memset(sdl, 0, sizeof(*sdl));

	sdl->source = sdl_deserialize_string(&in);
	sdl->target_ns = sdl_deserialize_string(&in);

	WSDL_CACHE_GET_INT(num_groups, &in);
	WSDL_CACHE_GET_INT(num_types, &in);
	WSDL_CACHE_GET_INT(num_elements, &in);
	WSDL_CACHE_GET_INT(num_encoders, &in);

	i = num_groups+num_types+num_elements;
	types = safe_emalloc((i+1), sizeof(sdlTypePtr), 0);
	types[0] = NULL;
	while (i > 0) {
		types[i] = emalloc(sizeof(sdlType));
		memset(types[i], 0, sizeof(sdlType));
		i--;
	}

	i = num_encoders;
	enc = defaultEncoding;
	while (enc->details.type != END_KNOWN_TYPES) {
		i++; enc++;
	}
	encoders = safe_emalloc((i+1), sizeof(encodePtr), 0);
	i = num_encoders;
	encoders[0] = NULL;
	while (i > 0) {
		encoders[i] = emalloc(sizeof(encode));
		memset(encoders[i], 0, sizeof(encode));
		i--;
	}
	i = num_encoders;
	enc = defaultEncoding;
	while (enc->details.type != END_KNOWN_TYPES) {
		encoders[++i] = (encodePtr)enc++;
	}

	i = 1;
	if (num_groups > 0) {
		sdl->groups = emalloc(sizeof(HashTable));
		zend_hash_init(sdl->groups, num_groups, NULL, delete_type, 0);
		while (i < num_groups+1) {
			sdl_deserialize_key(sdl->groups, types[i], &in);
			sdl_deserialize_type(types[i], types, encoders, &in);
			i++;
		}
	}

	if (num_types > 0) {
		sdl->types = emalloc(sizeof(HashTable));
		zend_hash_init(sdl->types, num_types, NULL, delete_type, 0);
		while (i < num_groups+num_types+1) {
			sdl_deserialize_key(sdl->types, types[i], &in);
			sdl_deserialize_type(types[i], types, encoders, &in);
			i++;
		}
	}

	if (num_elements > 0) {
		sdl->elements = emalloc(sizeof(HashTable));
		zend_hash_init(sdl->elements, num_elements, NULL, delete_type, 0);
		while (i < num_groups+num_types+num_elements+1) {
			sdl_deserialize_key(sdl->elements, types[i], &in);
			sdl_deserialize_type(types[i], types, encoders, &in);
			i++;
		}
	}

	i = 1;
	if (num_encoders > 0) {
		sdl->encoders = emalloc(sizeof(HashTable));
		zend_hash_init(sdl->encoders, num_encoders, NULL, delete_encoder, 0);
		while (i < num_encoders+1) {
			sdl_deserialize_key(sdl->encoders, encoders[i], &in);
			sdl_deserialize_encoder(encoders[i], types, &in);
			i++;
		}
	}

	/* deserialize bindings */
	WSDL_CACHE_GET_INT(num_bindings, &in);
	bindings = safe_emalloc(num_bindings, sizeof(sdlBindingPtr), 0);
	if (num_bindings > 0) {
		sdl->bindings = emalloc(sizeof(HashTable));
		zend_hash_init(sdl->bindings, num_bindings, NULL, delete_binding, 0);
		for (i = 0; i < num_bindings; i++) {
			sdlBindingPtr binding = emalloc(sizeof(sdlBinding));
			memset(binding, 0, sizeof(sdlBinding));
			sdl_deserialize_key(sdl->bindings, binding, &in);
			binding->name = sdl_deserialize_string(&in);
			binding->location = sdl_deserialize_string(&in);
			WSDL_CACHE_GET_1(binding->bindingType,sdlBindingType,&in);
			if (binding->bindingType == BINDING_SOAP && *in != 0) {
			  sdlSoapBindingPtr soap_binding = binding->bindingAttributes = emalloc(sizeof(sdlSoapBinding));
				WSDL_CACHE_GET_1(soap_binding->style,sdlEncodingStyle,&in);
				WSDL_CACHE_GET_1(soap_binding->transport,sdlTransport,&in);
			} else {
				WSDL_CACHE_SKIP(1,&in);
			}
			bindings[i] = binding;
		}
	}

	/* deserialize functions */
	WSDL_CACHE_GET_INT(num_func, &in);
	zend_hash_init(&sdl->functions, num_func, NULL, delete_function, 0);
	if (num_func > 0) {
		functions = safe_emalloc(num_func, sizeof(sdlFunctionPtr), 0);
		for (i = 0; i < num_func; i++) {
			int binding_num, num_faults;
			sdlFunctionPtr func = emalloc(sizeof(sdlFunction));
			sdl_deserialize_key(&sdl->functions, func, &in);
			func->functionName = sdl_deserialize_string(&in);
			func->requestName = sdl_deserialize_string(&in);
			func->responseName = sdl_deserialize_string(&in);

			WSDL_CACHE_GET_INT(binding_num, &in);
			if (binding_num == 0) {
				func->binding = NULL;
			} else {
				func->binding = bindings[binding_num-1];
			}
			if (func->binding && func->binding->bindingType == BINDING_SOAP && *in != 0) {
				sdlSoapBindingFunctionPtr binding = func->bindingAttributes = emalloc(sizeof(sdlSoapBindingFunction));
				memset(binding, 0, sizeof(sdlSoapBindingFunction));
				WSDL_CACHE_GET_1(binding->style,sdlEncodingStyle,&in);
				binding->soapAction = sdl_deserialize_string(&in);
				sdl_deserialize_soap_body(&binding->input, encoders, types, &in);
				sdl_deserialize_soap_body(&binding->output, encoders, types, &in);
			} else {
				WSDL_CACHE_SKIP(1, &in);
				func->bindingAttributes = NULL;
			}

			func->requestParameters = sdl_deserialize_parameters(encoders, types, &in);
			func->responseParameters = sdl_deserialize_parameters(encoders, types, &in);

			WSDL_CACHE_GET_INT(num_faults, &in);
			if (num_faults > 0) {
			  int j;

				func->faults = emalloc(sizeof(HashTable));
				zend_hash_init(func->faults, num_faults, NULL, delete_fault, 0);

				for (j = 0; j < num_faults; j++) {
					sdlFaultPtr fault = emalloc(sizeof(sdlFault));

					sdl_deserialize_key(func->faults, fault, &in);
					fault->name =sdl_deserialize_string(&in);
					fault->details =sdl_deserialize_parameters(encoders, types, &in);
					if (*in != 0) {
						sdlSoapBindingFunctionFaultPtr binding = fault->bindingAttributes = emalloc(sizeof(sdlSoapBindingFunctionFault));
						memset(binding, 0, sizeof(sdlSoapBindingFunctionFault));
						WSDL_CACHE_GET_1(binding->use,sdlEncodingUse,&in);
						if (binding->use == SOAP_ENCODED) {
							WSDL_CACHE_GET_1(binding->encodingStyle, sdlRpcEncodingStyle, &in);
						} else {
							binding->encodingStyle = SOAP_ENCODING_DEFAULT;
						}
						binding->ns = sdl_deserialize_string(&in);
					} else {
						WSDL_CACHE_SKIP(1, &in);
						fault->bindingAttributes = NULL;
					}
				}
			} else {
				func->faults = NULL;
			}
			functions[i] = func;
		}
	}

	/* deserialize requests */
	WSDL_CACHE_GET_INT(i, &in);
	if (i > 0) {
		sdl->requests = emalloc(sizeof(HashTable));
		zend_hash_init(sdl->requests, i, NULL, NULL, 0);
		while (i > 0) {
			int function_num;

			WSDL_CACHE_GET_INT(function_num, &in);
			sdl_deserialize_key(sdl->requests, functions[function_num-1], &in);
			i--;
		}
	}

	if (functions) {
		efree(functions);
	}
	efree(bindings);
	efree(encoders);
	efree(types);
	efree(buf);
	return sdl;
}