diff options
Diffstat (limited to 'dep')
84 files changed, 6924 insertions, 3291 deletions
diff --git a/dep/PackageList.txt b/dep/PackageList.txt index f932591ae32..3fa0143a584 100644 --- a/dep/PackageList.txt +++ b/dep/PackageList.txt @@ -14,7 +14,7 @@ G3D (a commercial-grade C++ 3D engine available as Open Source (BSD License) jemalloc (a general-purpose scalable concurrent malloc-implementation) http://www.canonware.com/jemalloc/ - Version: 3.3.1 + Version: 3.6.0 libMPQ (a library for reading MPQ files) https://libmpq.org/ @@ -38,8 +38,8 @@ zlib (A Massively Spiffy Yet Delicately Unobtrusive Compression Library) gSOAP (a portable development toolkit for C and C++ XML Web services and XML data bindings) http://gsoap2.sourceforge.net/ - Version: 2.8.10 + Version: 2.8.17r recastnavigation (Recast is state of the art navigation mesh construction toolset for games) - http://code.google.com/p/recastnavigation/ - Version: 1.4 + https://github.com/memononen/recastnavigation + Version: 42b96b7306d39bb7680ddb0f89d480a8296c83ff diff --git a/dep/acelite/ace/OS_NS_Thread.cpp b/dep/acelite/ace/OS_NS_Thread.cpp index 8e36eb9e4f7..c450bc81371 100644 --- a/dep/acelite/ace/OS_NS_Thread.cpp +++ b/dep/acelite/ace/OS_NS_Thread.cpp @@ -885,8 +885,12 @@ ACE_TSS_Cleanup::thread_detach_key (ACE_thread_key_t key) ACE_TSS_CLEANUP_GUARD u_int key_index = key; - ACE_ASSERT (key_index < sizeof(this->table_)/sizeof(this->table_[0]) - && this->table_[key_index].key_ == key); + ACE_ASSERT (key_index < sizeof(this->table_)/sizeof(this->table_[0])); + // If this entry was never set, just bug out. If it is set, but is the + // wrong key, assert. + if (this->table_[key_index].key_ == 0) + return 0; + ACE_ASSERT(this->table_[key_index].key_ == key); ACE_TSS_Info &info = this->table_ [key_index]; // sanity check diff --git a/dep/gsoap/soapC.cpp b/dep/gsoap/soapC.cpp index 2c675cc339d..321cae0299b 100644 --- a/dep/gsoap/soapC.cpp +++ b/dep/gsoap/soapC.cpp @@ -1,9 +1,9 @@ /* soapC.cpp - Generated by gSOAP 2.8.10 from gsoap.stub + Generated by gSOAP 2.8.17r from gsoap.stub -Copyright(C) 2000-2012, Robert van Engelen, Genivia Inc. All Rights Reserved. +Copyright(C) 2000-2013, Robert van Engelen, Genivia Inc. All Rights Reserved. The generated code is released under one of the following licenses: -1) GPL or 2) Genivia's license for commercial use. +GPL or Genivia's license for commercial use. This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. */ @@ -15,7 +15,7 @@ compiling, linking, and/or using OpenSSL is allowed. #include "soapH.h" -SOAP_SOURCE_STAMP("@(#) soapC.cpp ver 2.8.10 2012-09-02 20:48:00 GMT") +SOAP_SOURCE_STAMP("@(#) soapC.cpp ver 2.8.17r 2014-06-21 21:43:17 GMT") #ifndef WITH_NOGLOBAL @@ -30,7 +30,7 @@ SOAP_FMAC3 int SOAP_FMAC4 soap_getheader(struct soap *soap) SOAP_FMAC3 int SOAP_FMAC4 soap_putheader(struct soap *soap) { - if (soap->header) + if (soap->version && soap->header) { soap->part = SOAP_IN_HEADER; if (soap_out_SOAP_ENV__Header(soap, "SOAP-ENV:Header", 0, soap->header, NULL)) return soap->error; @@ -41,13 +41,13 @@ SOAP_FMAC3 int SOAP_FMAC4 soap_putheader(struct soap *soap) SOAP_FMAC3 void SOAP_FMAC4 soap_serializeheader(struct soap *soap) { - if (soap->header) + if (soap->version && soap->header) soap_serialize_SOAP_ENV__Header(soap, soap->header); } SOAP_FMAC3 void SOAP_FMAC4 soap_header(struct soap *soap) { - if (!soap->header) + if (soap->header == NULL) { if ((soap->header = soap_new_SOAP_ENV__Header(soap, -1))) soap_default_SOAP_ENV__Header(soap, soap->header); } @@ -55,9 +55,9 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_header(struct soap *soap) SOAP_FMAC3 void SOAP_FMAC4 soap_fault(struct soap *soap) { - if (!soap->fault) + if (soap->fault == NULL) { soap->fault = soap_new_SOAP_ENV__Fault(soap, -1); - if (!soap->fault) + if (soap->fault == NULL) return; soap_default_SOAP_ENV__Fault(soap, soap->fault); } @@ -102,7 +102,7 @@ SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultsubcode(struct soap *soap) { soap_fault(soap); if (soap->version == 2) - { if (!soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode) + { if (soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode == NULL) { soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode = soap_new_SOAP_ENV__Code(soap, -1); soap_default_SOAP_ENV__Code(soap, soap->fault->SOAP_ENV__Code->SOAP_ENV__Subcode); } @@ -133,18 +133,18 @@ SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultstring(struct soap *soap) SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultdetail(struct soap *soap) { soap_fault(soap); - if (soap->version == 1) - { if (!soap->fault->detail) - { soap->fault->detail = (struct SOAP_ENV__Detail*)soap_malloc(soap, sizeof(struct SOAP_ENV__Detail)); - soap_default_SOAP_ENV__Detail(soap, soap->fault->detail); + if (soap->version == 2) + { if (soap->fault->SOAP_ENV__Detail == NULL) + { soap->fault->SOAP_ENV__Detail = soap_new_SOAP_ENV__Detail(soap, -1); + soap_default_SOAP_ENV__Detail(soap, soap->fault->SOAP_ENV__Detail); } - return (const char**)&soap->fault->detail->__any; + return (const char**)&soap->fault->SOAP_ENV__Detail->__any; } - if (!soap->fault->SOAP_ENV__Detail) - { soap->fault->SOAP_ENV__Detail = soap_new_SOAP_ENV__Detail(soap, -1); - soap_default_SOAP_ENV__Detail(soap, soap->fault->SOAP_ENV__Detail); + if (soap->fault->detail == NULL) + { soap->fault->detail = soap_new_SOAP_ENV__Detail(soap, -1); + soap_default_SOAP_ENV__Detail(soap, soap->fault->detail); } - return (const char**)&soap->fault->SOAP_ENV__Detail->__any; + return (const char**)&soap->fault->detail->__any; } SOAP_FMAC3 const char * SOAP_FMAC4 soap_check_faultdetail(struct soap *soap) @@ -176,8 +176,6 @@ SOAP_FMAC3 int SOAP_FMAC4 soap_getindependent(struct soap *soap) } #endif -#ifndef WITH_NOIDREF - #ifdef __cplusplus extern "C" { #endif @@ -185,6 +183,7 @@ SOAP_FMAC3 void * SOAP_FMAC4 soap_getelement(struct soap *soap, int *type) { (void)type; if (soap_peek_element(soap)) return NULL; +#ifndef WITH_NOIDREF if (!*soap->id || !(*type = soap_lookup_type(soap, soap->id))) *type = soap_lookup_type(soap, soap->href); switch (*type) @@ -210,6 +209,9 @@ SOAP_FMAC3 void * SOAP_FMAC4 soap_getelement(struct soap *soap, int *type) return s ? *s : NULL; } default: +#else + *type = 0; +#endif { const char *t = soap->type; if (!*t) t = soap->tag; @@ -242,7 +244,9 @@ SOAP_FMAC3 void * SOAP_FMAC4 soap_getelement(struct soap *soap, int *type) return s ? *s : NULL; } t = soap->tag; +#ifndef WITH_NOIDREF } +#endif } soap->error = SOAP_TAG_MISMATCH; return NULL; @@ -251,7 +255,6 @@ SOAP_FMAC3 void * SOAP_FMAC4 soap_getelement(struct soap *soap, int *type) #ifdef __cplusplus } #endif -#endif SOAP_FMAC3 int SOAP_FMAC4 soap_ignore_element(struct soap *soap) { @@ -298,8 +301,6 @@ SOAP_FMAC3 int SOAP_FMAC4 soap_putindependent(struct soap *soap) } #endif -#ifndef WITH_NOIDREF - #ifdef __cplusplus extern "C" { #endif @@ -328,7 +329,6 @@ SOAP_FMAC3 int SOAP_FMAC4 soap_putelement(struct soap *soap, const void *ptr, co #ifdef __cplusplus } #endif -#endif #ifndef WITH_NOIDREF @@ -551,6 +551,7 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Fault(struct soap *soap, struc SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Fault(struct soap *soap, const struct SOAP_ENV__Fault *a) { +#ifndef WITH_NOIDREF (void)soap; (void)a; /* appease -Wall -Werror */ soap_serialize__QName(soap, &a->faultcode); soap_serialize_string(soap, &a->faultstring); @@ -561,6 +562,7 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Fault(struct soap *soap, con soap_serialize_string(soap, &a->SOAP_ENV__Node); soap_serialize_string(soap, &a->SOAP_ENV__Role); soap_serialize_PointerToSOAP_ENV__Detail(soap, &a->SOAP_ENV__Detail); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Fault(struct soap *soap, const char *tag, int id, const struct SOAP_ENV__Fault *a, const char *type) @@ -703,15 +705,13 @@ SOAP_FMAC1 struct SOAP_ENV__Fault * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Fault( *size = sizeof(struct SOAP_ENV__Fault); } else - { cp->ptr = (void*)SOAP_NEW(struct SOAP_ENV__Fault[n]); - if (!cp->ptr) - { soap->error = SOAP_EOM; - return NULL; - } + { cp->ptr = (void*)SOAP_NEW_ARRAY(struct SOAP_ENV__Fault, n); if (size) *size = n * sizeof(struct SOAP_ENV__Fault); } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Instantiated location=%p\n", cp->ptr)); + if (!cp->ptr) + soap->error = SOAP_EOM; return (struct SOAP_ENV__Fault*)cp->ptr; } @@ -734,8 +734,10 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Reason(struct soap *soap, stru SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Reason(struct soap *soap, const struct SOAP_ENV__Reason *a) { +#ifndef WITH_NOIDREF (void)soap; (void)a; /* appease -Wall -Werror */ soap_serialize_string(soap, &a->SOAP_ENV__Text); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Reason(struct soap *soap, const char *tag, int id, const struct SOAP_ENV__Reason *a, const char *type) @@ -815,15 +817,13 @@ SOAP_FMAC1 struct SOAP_ENV__Reason * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Reaso *size = sizeof(struct SOAP_ENV__Reason); } else - { cp->ptr = (void*)SOAP_NEW(struct SOAP_ENV__Reason[n]); - if (!cp->ptr) - { soap->error = SOAP_EOM; - return NULL; - } + { cp->ptr = (void*)SOAP_NEW_ARRAY(struct SOAP_ENV__Reason, n); if (size) *size = n * sizeof(struct SOAP_ENV__Reason); } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Instantiated location=%p\n", cp->ptr)); + if (!cp->ptr) + soap->error = SOAP_EOM; return (struct SOAP_ENV__Reason*)cp->ptr; } @@ -848,8 +848,10 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Detail(struct soap *soap, stru SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Detail(struct soap *soap, const struct SOAP_ENV__Detail *a) { +#ifndef WITH_NOIDREF (void)soap; (void)a; /* appease -Wall -Werror */ soap_markelement(soap, a->fault, a->__type); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Detail(struct soap *soap, const char *tag, int id, const struct SOAP_ENV__Detail *a, const char *type) @@ -934,15 +936,13 @@ SOAP_FMAC1 struct SOAP_ENV__Detail * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Detai *size = sizeof(struct SOAP_ENV__Detail); } else - { cp->ptr = (void*)SOAP_NEW(struct SOAP_ENV__Detail[n]); - if (!cp->ptr) - { soap->error = SOAP_EOM; - return NULL; - } + { cp->ptr = (void*)SOAP_NEW_ARRAY(struct SOAP_ENV__Detail, n); if (size) *size = n * sizeof(struct SOAP_ENV__Detail); } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Instantiated location=%p\n", cp->ptr)); + if (!cp->ptr) + soap->error = SOAP_EOM; return (struct SOAP_ENV__Detail*)cp->ptr; } @@ -966,9 +966,11 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Code(struct soap *soap, struct SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Code(struct soap *soap, const struct SOAP_ENV__Code *a) { +#ifndef WITH_NOIDREF (void)soap; (void)a; /* appease -Wall -Werror */ soap_serialize__QName(soap, &a->SOAP_ENV__Value); soap_serialize_PointerToSOAP_ENV__Code(soap, &a->SOAP_ENV__Subcode); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Code(struct soap *soap, const char *tag, int id, const struct SOAP_ENV__Code *a, const char *type) @@ -1055,15 +1057,13 @@ SOAP_FMAC1 struct SOAP_ENV__Code * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Code(st *size = sizeof(struct SOAP_ENV__Code); } else - { cp->ptr = (void*)SOAP_NEW(struct SOAP_ENV__Code[n]); - if (!cp->ptr) - { soap->error = SOAP_EOM; - return NULL; - } + { cp->ptr = (void*)SOAP_NEW_ARRAY(struct SOAP_ENV__Code, n); if (size) *size = n * sizeof(struct SOAP_ENV__Code); } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Instantiated location=%p\n", cp->ptr)); + if (!cp->ptr) + soap->error = SOAP_EOM; return (struct SOAP_ENV__Code*)cp->ptr; } @@ -1085,7 +1085,9 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_SOAP_ENV__Header(struct soap *soap, stru SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Header(struct soap *soap, const struct SOAP_ENV__Header *a) { +#ifndef WITH_NOIDREF (void)soap; (void)a; /* appease -Wall -Werror */ +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Header(struct soap *soap, const char *tag, int id, const struct SOAP_ENV__Header *a, const char *type) @@ -1155,15 +1157,13 @@ SOAP_FMAC1 struct SOAP_ENV__Header * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Heade *size = sizeof(struct SOAP_ENV__Header); } else - { cp->ptr = (void*)SOAP_NEW(struct SOAP_ENV__Header[n]); - if (!cp->ptr) - { soap->error = SOAP_EOM; - return NULL; - } + { cp->ptr = (void*)SOAP_NEW_ARRAY(struct SOAP_ENV__Header, n); if (size) *size = n * sizeof(struct SOAP_ENV__Header); } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Instantiated location=%p\n", cp->ptr)); + if (!cp->ptr) + soap->error = SOAP_EOM; return (struct SOAP_ENV__Header*)cp->ptr; } @@ -1184,8 +1184,10 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__executeCommand(struct soap *soap, s SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__executeCommand(struct soap *soap, const struct ns1__executeCommand *a) { +#ifndef WITH_NOIDREF (void)soap; (void)a; /* appease -Wall -Werror */ soap_serialize_string(soap, &a->command); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__executeCommand(struct soap *soap, const char *tag, int id, const struct ns1__executeCommand *a, const char *type) @@ -1263,15 +1265,13 @@ SOAP_FMAC1 struct ns1__executeCommand * SOAP_FMAC2 soap_instantiate_ns1__execute *size = sizeof(struct ns1__executeCommand); } else - { cp->ptr = (void*)SOAP_NEW(struct ns1__executeCommand[n]); - if (!cp->ptr) - { soap->error = SOAP_EOM; - return NULL; - } + { cp->ptr = (void*)SOAP_NEW_ARRAY(struct ns1__executeCommand, n); if (size) *size = n * sizeof(struct ns1__executeCommand); } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Instantiated location=%p\n", cp->ptr)); + if (!cp->ptr) + soap->error = SOAP_EOM; return (struct ns1__executeCommand*)cp->ptr; } @@ -1290,8 +1290,10 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_ns1__executeCommandResponse(struct soap SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__executeCommandResponse(struct soap *soap, const struct ns1__executeCommandResponse *a) { +#ifndef WITH_NOIDREF (void)soap; (void)a; /* appease -Wall -Werror */ soap_serialize_PointerTostring(soap, &a->result); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__executeCommandResponse(struct soap *soap, const char *tag, int id, const struct ns1__executeCommandResponse *a, const char *type) @@ -1369,15 +1371,13 @@ SOAP_FMAC1 struct ns1__executeCommandResponse * SOAP_FMAC2 soap_instantiate_ns1_ *size = sizeof(struct ns1__executeCommandResponse); } else - { cp->ptr = (void*)SOAP_NEW(struct ns1__executeCommandResponse[n]); - if (!cp->ptr) - { soap->error = SOAP_EOM; - return NULL; - } + { cp->ptr = (void*)SOAP_NEW_ARRAY(struct ns1__executeCommandResponse, n); if (size) *size = n * sizeof(struct ns1__executeCommandResponse); } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Instantiated location=%p\n", cp->ptr)); + if (!cp->ptr) + soap->error = SOAP_EOM; return (struct ns1__executeCommandResponse*)cp->ptr; } @@ -1392,8 +1392,10 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_copy_ns1__executeCommandResponse(struct soap *so SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Reason(struct soap *soap, struct SOAP_ENV__Reason *const*a) { +#ifndef WITH_NOIDREF if (!soap_reference(soap, *a, SOAP_TYPE_SOAP_ENV__Reason)) soap_serialize_SOAP_ENV__Reason(soap, *a); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Reason(struct soap *soap, const char *tag, int id, struct SOAP_ENV__Reason *const*a, const char *type) @@ -1414,8 +1416,6 @@ SOAP_FMAC3 struct SOAP_ENV__Reason ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Reas *a = NULL; if (!soap->null && *soap->href != '#') { soap_revert(soap); - *a = (struct SOAP_ENV__Reason *)soap_malloc(soap, sizeof(struct SOAP_ENV__Reason)); - soap_default_SOAP_ENV__Reason(soap, *a); if (!(*a = soap_in_SOAP_ENV__Reason(soap, tag, *a, type))) return NULL; } @@ -1449,8 +1449,10 @@ SOAP_FMAC3 struct SOAP_ENV__Reason ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Rea SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Detail(struct soap *soap, struct SOAP_ENV__Detail *const*a) { +#ifndef WITH_NOIDREF if (!soap_reference(soap, *a, SOAP_TYPE_SOAP_ENV__Detail)) soap_serialize_SOAP_ENV__Detail(soap, *a); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Detail(struct soap *soap, const char *tag, int id, struct SOAP_ENV__Detail *const*a, const char *type) @@ -1471,8 +1473,6 @@ SOAP_FMAC3 struct SOAP_ENV__Detail ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Deta *a = NULL; if (!soap->null && *soap->href != '#') { soap_revert(soap); - *a = (struct SOAP_ENV__Detail *)soap_malloc(soap, sizeof(struct SOAP_ENV__Detail)); - soap_default_SOAP_ENV__Detail(soap, *a); if (!(*a = soap_in_SOAP_ENV__Detail(soap, tag, *a, type))) return NULL; } @@ -1506,8 +1506,10 @@ SOAP_FMAC3 struct SOAP_ENV__Detail ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Det SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Code(struct soap *soap, struct SOAP_ENV__Code *const*a) { +#ifndef WITH_NOIDREF if (!soap_reference(soap, *a, SOAP_TYPE_SOAP_ENV__Code)) soap_serialize_SOAP_ENV__Code(soap, *a); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Code(struct soap *soap, const char *tag, int id, struct SOAP_ENV__Code *const*a, const char *type) @@ -1528,8 +1530,6 @@ SOAP_FMAC3 struct SOAP_ENV__Code ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Code(s *a = NULL; if (!soap->null && *soap->href != '#') { soap_revert(soap); - *a = (struct SOAP_ENV__Code *)soap_malloc(soap, sizeof(struct SOAP_ENV__Code)); - soap_default_SOAP_ENV__Code(soap, *a); if (!(*a = soap_in_SOAP_ENV__Code(soap, tag, *a, type))) return NULL; } @@ -1561,8 +1561,10 @@ SOAP_FMAC3 struct SOAP_ENV__Code ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Code( SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerTostring(struct soap *soap, char **const*a) { +#ifndef WITH_NOIDREF if (!soap_reference(soap, *a, SOAP_TYPE_string)) soap_serialize_string(soap, *a); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerTostring(struct soap *soap, const char *tag, int id, char **const*a, const char *type) @@ -1610,6 +1612,13 @@ SOAP_FMAC3 char *** SOAP_FMAC4 soap_get_PointerTostring(struct soap *soap, char return p; } +SOAP_FMAC3 void SOAP_FMAC4 soap_serialize__QName(struct soap *soap, char *const*a) +{ +#ifndef WITH_NOIDREF + soap_reference(soap, *a, SOAP_TYPE__QName); +#endif +} + SOAP_FMAC3 int SOAP_FMAC4 soap_out__QName(struct soap *soap, const char *tag, int id, char *const*a, const char *type) { return soap_outstring(soap, tag, id, a, type, SOAP_TYPE__QName); @@ -1649,7 +1658,9 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_string(struct soap *soap, char **a) SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_string(struct soap *soap, char *const*a) { +#ifndef WITH_NOIDREF soap_reference(soap, *a, SOAP_TYPE_string); +#endif } SOAP_FMAC3 int SOAP_FMAC4 soap_out_string(struct soap *soap, const char *tag, int id, char *const*a, const char *type) diff --git a/dep/gsoap/soapH.h b/dep/gsoap/soapH.h index 06e9ec21239..a73b369fe90 100644 --- a/dep/gsoap/soapH.h +++ b/dep/gsoap/soapH.h @@ -1,9 +1,9 @@ /* soapH.h - Generated by gSOAP 2.8.10 from gsoap.stub + Generated by gSOAP 2.8.17r from gsoap.stub -Copyright(C) 2000-2012, Robert van Engelen, Genivia Inc. All Rights Reserved. +Copyright(C) 2000-2013, Robert van Engelen, Genivia Inc. All Rights Reserved. The generated code is released under one of the following licenses: -1) GPL or 2) Genivia's license for commercial use. +GPL or Genivia's license for commercial use. This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. */ @@ -17,8 +17,6 @@ compiling, linking, and/or using OpenSSL is allowed. extern "C" { #endif SOAP_FMAC3 void SOAP_FMAC4 soap_markelement(struct soap*, const void*, int); -SOAP_FMAC3 int SOAP_FMAC4 soap_putelement(struct soap*, const void*, const char*, int, int); -SOAP_FMAC3 void *SOAP_FMAC4 soap_getelement(struct soap*, int*); #ifdef __cplusplus } @@ -26,8 +24,20 @@ SOAP_FMAC3 void *SOAP_FMAC4 soap_getelement(struct soap*, int*); SOAP_FMAC3 int SOAP_FMAC4 soap_putindependent(struct soap*); SOAP_FMAC3 int SOAP_FMAC4 soap_getindependent(struct soap*); #endif + +#ifdef __cplusplus +extern "C" { +#endif +SOAP_FMAC3 void *SOAP_FMAC4 soap_getelement(struct soap*, int*); +SOAP_FMAC3 int SOAP_FMAC4 soap_putelement(struct soap*, const void*, const char*, int, int); + +#ifdef __cplusplus +} +#endif SOAP_FMAC3 int SOAP_FMAC4 soap_ignore_element(struct soap*); +SOAP_FMAC3 const char ** SOAP_FMAC4 soap_faultcode(struct soap *soap); + SOAP_FMAC3 void * SOAP_FMAC4 soap_instantiate(struct soap*, int, const char*, const char*, size_t*); SOAP_FMAC3 int SOAP_FMAC4 soap_fdelete(struct soap_clist*); SOAP_FMAC3 void* SOAP_FMAC4 soap_class_id_enter(struct soap*, const char*, void*, int, size_t, const char*, const char*); @@ -39,18 +49,18 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_byte(struct soap*, char *); SOAP_FMAC3 int SOAP_FMAC4 soap_out_byte(struct soap*, const char*, int, const char *, const char*); SOAP_FMAC3 char * SOAP_FMAC4 soap_in_byte(struct soap*, const char*, char *, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_byte(struct soap*, const char *, const char*, const char*); + #ifndef soap_write_byte -#define soap_write_byte(soap, data) ( soap_begin_send(soap) || soap_put_byte(soap, data, "byte", NULL) || soap_end_send(soap) ) +#define soap_write_byte(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_byte(soap, data),0) || soap_put_byte(soap, data, "byte", NULL) || soap_end_send(soap), (soap)->error ) #endif - -SOAP_FMAC3 int SOAP_FMAC4 soap_put_byte(struct soap*, const char *, const char*, const char*); +SOAP_FMAC3 char * SOAP_FMAC4 soap_get_byte(struct soap*, char *, const char*, const char*); #ifndef soap_read_byte -#define soap_read_byte(soap, data) ( soap_begin_recv(soap) || !soap_get_byte(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_byte(soap, data) ( soap_begin_recv(soap) || !soap_get_byte(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 char * SOAP_FMAC4 soap_get_byte(struct soap*, char *, const char*, const char*); #ifndef SOAP_TYPE_int #define SOAP_TYPE_int (1) @@ -59,18 +69,18 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_int(struct soap*, int *); SOAP_FMAC3 int SOAP_FMAC4 soap_out_int(struct soap*, const char*, int, const int *, const char*); SOAP_FMAC3 int * SOAP_FMAC4 soap_in_int(struct soap*, const char*, int *, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_int(struct soap*, const int *, const char*, const char*); + #ifndef soap_write_int -#define soap_write_int(soap, data) ( soap_begin_send(soap) || soap_put_int(soap, data, "int", NULL) || soap_end_send(soap) ) +#define soap_write_int(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_int(soap, data),0) || soap_put_int(soap, data, "int", NULL) || soap_end_send(soap), (soap)->error ) #endif - -SOAP_FMAC3 int SOAP_FMAC4 soap_put_int(struct soap*, const int *, const char*, const char*); +SOAP_FMAC3 int * SOAP_FMAC4 soap_get_int(struct soap*, int *, const char*, const char*); #ifndef soap_read_int -#define soap_read_int(soap, data) ( soap_begin_recv(soap) || !soap_get_int(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_int(soap, data) ( soap_begin_recv(soap) || !soap_get_int(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 int * SOAP_FMAC4 soap_get_int(struct soap*, int *, const char*, const char*); #ifndef WITH_NOGLOBAL @@ -82,25 +92,28 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Fault(struct soap*, const st SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Fault(struct soap*, const char*, int, const struct SOAP_ENV__Fault *, const char*); SOAP_FMAC3 struct SOAP_ENV__Fault * SOAP_FMAC4 soap_in_SOAP_ENV__Fault(struct soap*, const char*, struct SOAP_ENV__Fault *, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Fault(struct soap*, const struct SOAP_ENV__Fault *, const char*, const char*); + #ifndef soap_write_SOAP_ENV__Fault -#define soap_write_SOAP_ENV__Fault(soap, data) ( soap_begin_send(soap) || (soap_serialize_SOAP_ENV__Fault(soap, data), 0) || soap_put_SOAP_ENV__Fault(soap, data, "SOAP-ENV:Fault", NULL) || soap_end_send(soap) ) +#define soap_write_SOAP_ENV__Fault(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_SOAP_ENV__Fault(soap, data),0) || soap_put_SOAP_ENV__Fault(soap, data, "SOAP-ENV:Fault", NULL) || soap_end_send(soap), (soap)->error ) #endif - -SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Fault(struct soap*, const struct SOAP_ENV__Fault *, const char*, const char*); +SOAP_FMAC3 struct SOAP_ENV__Fault * SOAP_FMAC4 soap_get_SOAP_ENV__Fault(struct soap*, struct SOAP_ENV__Fault *, const char*, const char*); #ifndef soap_read_SOAP_ENV__Fault -#define soap_read_SOAP_ENV__Fault(soap, data) ( soap_begin_recv(soap) || !soap_get_SOAP_ENV__Fault(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_SOAP_ENV__Fault(soap, data) ( soap_begin_recv(soap) || !soap_get_SOAP_ENV__Fault(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 struct SOAP_ENV__Fault * SOAP_FMAC4 soap_get_SOAP_ENV__Fault(struct soap*, struct SOAP_ENV__Fault *, const char*, const char*); +SOAP_FMAC1 struct SOAP_ENV__Fault * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Fault(struct soap*, int, const char*, const char*, size_t*); -#define soap_new_SOAP_ENV__Fault(soap, n) soap_instantiate_SOAP_ENV__Fault(soap, n, NULL, NULL, NULL) +inline struct SOAP_ENV__Fault * soap_new_SOAP_ENV__Fault(struct soap *soap, int n = -1) { return soap_instantiate_SOAP_ENV__Fault(soap, n, NULL, NULL, NULL); } +inline struct SOAP_ENV__Fault * soap_new_req_SOAP_ENV__Fault(struct soap *soap) { struct SOAP_ENV__Fault *_p = soap_instantiate_SOAP_ENV__Fault(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_SOAP_ENV__Fault(soap, _p); } return _p; } -#define soap_delete_SOAP_ENV__Fault(soap, p) soap_delete(soap, p) +inline struct SOAP_ENV__Fault * soap_new_set_SOAP_ENV__Fault(struct soap *soap, char *faultcode, char *faultstring, char *faultactor, struct SOAP_ENV__Detail *detail, struct SOAP_ENV__Code *SOAP_ENV__Code, struct SOAP_ENV__Reason *SOAP_ENV__Reason, char *SOAP_ENV__Node, char *SOAP_ENV__Role, struct SOAP_ENV__Detail *SOAP_ENV__Detail) { struct SOAP_ENV__Fault *_p = soap_instantiate_SOAP_ENV__Fault(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_SOAP_ENV__Fault(soap, _p); _p->faultcode = faultcode; _p->faultstring = faultstring; _p->faultactor = faultactor; _p->detail = detail; _p->SOAP_ENV__Code = SOAP_ENV__Code; _p->SOAP_ENV__Reason = SOAP_ENV__Reason; _p->SOAP_ENV__Node = SOAP_ENV__Node; _p->SOAP_ENV__Role = SOAP_ENV__Role; _p->SOAP_ENV__Detail = SOAP_ENV__Detail; } return _p; } + +inline void soap_delete_SOAP_ENV__Fault(struct soap *soap, struct SOAP_ENV__Fault *p) { soap_delete(soap, p); } -SOAP_FMAC1 struct SOAP_ENV__Fault * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Fault(struct soap*, int, const char*, const char*, size_t*); SOAP_FMAC3 void SOAP_FMAC4 soap_copy_SOAP_ENV__Fault(struct soap*, int, int, void*, size_t, const void*, size_t); #endif @@ -115,25 +128,28 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Reason(struct soap*, const s SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Reason(struct soap*, const char*, int, const struct SOAP_ENV__Reason *, const char*); SOAP_FMAC3 struct SOAP_ENV__Reason * SOAP_FMAC4 soap_in_SOAP_ENV__Reason(struct soap*, const char*, struct SOAP_ENV__Reason *, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Reason(struct soap*, const struct SOAP_ENV__Reason *, const char*, const char*); + #ifndef soap_write_SOAP_ENV__Reason -#define soap_write_SOAP_ENV__Reason(soap, data) ( soap_begin_send(soap) || (soap_serialize_SOAP_ENV__Reason(soap, data), 0) || soap_put_SOAP_ENV__Reason(soap, data, "SOAP-ENV:Reason", NULL) || soap_end_send(soap) ) +#define soap_write_SOAP_ENV__Reason(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_SOAP_ENV__Reason(soap, data),0) || soap_put_SOAP_ENV__Reason(soap, data, "SOAP-ENV:Reason", NULL) || soap_end_send(soap), (soap)->error ) #endif - -SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Reason(struct soap*, const struct SOAP_ENV__Reason *, const char*, const char*); +SOAP_FMAC3 struct SOAP_ENV__Reason * SOAP_FMAC4 soap_get_SOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason *, const char*, const char*); #ifndef soap_read_SOAP_ENV__Reason -#define soap_read_SOAP_ENV__Reason(soap, data) ( soap_begin_recv(soap) || !soap_get_SOAP_ENV__Reason(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_SOAP_ENV__Reason(soap, data) ( soap_begin_recv(soap) || !soap_get_SOAP_ENV__Reason(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 struct SOAP_ENV__Reason * SOAP_FMAC4 soap_get_SOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason *, const char*, const char*); +SOAP_FMAC1 struct SOAP_ENV__Reason * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Reason(struct soap*, int, const char*, const char*, size_t*); -#define soap_new_SOAP_ENV__Reason(soap, n) soap_instantiate_SOAP_ENV__Reason(soap, n, NULL, NULL, NULL) +inline struct SOAP_ENV__Reason * soap_new_SOAP_ENV__Reason(struct soap *soap, int n = -1) { return soap_instantiate_SOAP_ENV__Reason(soap, n, NULL, NULL, NULL); } +inline struct SOAP_ENV__Reason * soap_new_req_SOAP_ENV__Reason(struct soap *soap) { struct SOAP_ENV__Reason *_p = soap_instantiate_SOAP_ENV__Reason(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_SOAP_ENV__Reason(soap, _p); } return _p; } -#define soap_delete_SOAP_ENV__Reason(soap, p) soap_delete(soap, p) +inline struct SOAP_ENV__Reason * soap_new_set_SOAP_ENV__Reason(struct soap *soap, char *SOAP_ENV__Text) { struct SOAP_ENV__Reason *_p = soap_instantiate_SOAP_ENV__Reason(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_SOAP_ENV__Reason(soap, _p); _p->SOAP_ENV__Text = SOAP_ENV__Text; } return _p; } + +inline void soap_delete_SOAP_ENV__Reason(struct soap *soap, struct SOAP_ENV__Reason *p) { soap_delete(soap, p); } -SOAP_FMAC1 struct SOAP_ENV__Reason * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Reason(struct soap*, int, const char*, const char*, size_t*); SOAP_FMAC3 void SOAP_FMAC4 soap_copy_SOAP_ENV__Reason(struct soap*, int, int, void*, size_t, const void*, size_t); #endif @@ -148,25 +164,28 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Detail(struct soap*, const s SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Detail(struct soap*, const char*, int, const struct SOAP_ENV__Detail *, const char*); SOAP_FMAC3 struct SOAP_ENV__Detail * SOAP_FMAC4 soap_in_SOAP_ENV__Detail(struct soap*, const char*, struct SOAP_ENV__Detail *, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Detail(struct soap*, const struct SOAP_ENV__Detail *, const char*, const char*); + #ifndef soap_write_SOAP_ENV__Detail -#define soap_write_SOAP_ENV__Detail(soap, data) ( soap_begin_send(soap) || (soap_serialize_SOAP_ENV__Detail(soap, data), 0) || soap_put_SOAP_ENV__Detail(soap, data, "SOAP-ENV:Detail", NULL) || soap_end_send(soap) ) +#define soap_write_SOAP_ENV__Detail(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_SOAP_ENV__Detail(soap, data),0) || soap_put_SOAP_ENV__Detail(soap, data, "SOAP-ENV:Detail", NULL) || soap_end_send(soap), (soap)->error ) #endif - -SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Detail(struct soap*, const struct SOAP_ENV__Detail *, const char*, const char*); +SOAP_FMAC3 struct SOAP_ENV__Detail * SOAP_FMAC4 soap_get_SOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail *, const char*, const char*); #ifndef soap_read_SOAP_ENV__Detail -#define soap_read_SOAP_ENV__Detail(soap, data) ( soap_begin_recv(soap) || !soap_get_SOAP_ENV__Detail(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_SOAP_ENV__Detail(soap, data) ( soap_begin_recv(soap) || !soap_get_SOAP_ENV__Detail(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 struct SOAP_ENV__Detail * SOAP_FMAC4 soap_get_SOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail *, const char*, const char*); +SOAP_FMAC1 struct SOAP_ENV__Detail * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Detail(struct soap*, int, const char*, const char*, size_t*); -#define soap_new_SOAP_ENV__Detail(soap, n) soap_instantiate_SOAP_ENV__Detail(soap, n, NULL, NULL, NULL) +inline struct SOAP_ENV__Detail * soap_new_SOAP_ENV__Detail(struct soap *soap, int n = -1) { return soap_instantiate_SOAP_ENV__Detail(soap, n, NULL, NULL, NULL); } +inline struct SOAP_ENV__Detail * soap_new_req_SOAP_ENV__Detail(struct soap *soap, int __type, void *fault) { struct SOAP_ENV__Detail *_p = soap_instantiate_SOAP_ENV__Detail(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_SOAP_ENV__Detail(soap, _p); _p->__type = __type; _p->fault = fault; } return _p; } -#define soap_delete_SOAP_ENV__Detail(soap, p) soap_delete(soap, p) +inline struct SOAP_ENV__Detail * soap_new_set_SOAP_ENV__Detail(struct soap *soap, char *__any, int __type, void *fault) { struct SOAP_ENV__Detail *_p = soap_instantiate_SOAP_ENV__Detail(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_SOAP_ENV__Detail(soap, _p); _p->__any = __any; _p->__type = __type; _p->fault = fault; } return _p; } + +inline void soap_delete_SOAP_ENV__Detail(struct soap *soap, struct SOAP_ENV__Detail *p) { soap_delete(soap, p); } -SOAP_FMAC1 struct SOAP_ENV__Detail * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Detail(struct soap*, int, const char*, const char*, size_t*); SOAP_FMAC3 void SOAP_FMAC4 soap_copy_SOAP_ENV__Detail(struct soap*, int, int, void*, size_t, const void*, size_t); #endif @@ -181,25 +200,28 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Code(struct soap*, const str SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Code(struct soap*, const char*, int, const struct SOAP_ENV__Code *, const char*); SOAP_FMAC3 struct SOAP_ENV__Code * SOAP_FMAC4 soap_in_SOAP_ENV__Code(struct soap*, const char*, struct SOAP_ENV__Code *, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Code(struct soap*, const struct SOAP_ENV__Code *, const char*, const char*); + #ifndef soap_write_SOAP_ENV__Code -#define soap_write_SOAP_ENV__Code(soap, data) ( soap_begin_send(soap) || (soap_serialize_SOAP_ENV__Code(soap, data), 0) || soap_put_SOAP_ENV__Code(soap, data, "SOAP-ENV:Code", NULL) || soap_end_send(soap) ) +#define soap_write_SOAP_ENV__Code(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_SOAP_ENV__Code(soap, data),0) || soap_put_SOAP_ENV__Code(soap, data, "SOAP-ENV:Code", NULL) || soap_end_send(soap), (soap)->error ) #endif - -SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Code(struct soap*, const struct SOAP_ENV__Code *, const char*, const char*); +SOAP_FMAC3 struct SOAP_ENV__Code * SOAP_FMAC4 soap_get_SOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code *, const char*, const char*); #ifndef soap_read_SOAP_ENV__Code -#define soap_read_SOAP_ENV__Code(soap, data) ( soap_begin_recv(soap) || !soap_get_SOAP_ENV__Code(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_SOAP_ENV__Code(soap, data) ( soap_begin_recv(soap) || !soap_get_SOAP_ENV__Code(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 struct SOAP_ENV__Code * SOAP_FMAC4 soap_get_SOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code *, const char*, const char*); +SOAP_FMAC1 struct SOAP_ENV__Code * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Code(struct soap*, int, const char*, const char*, size_t*); -#define soap_new_SOAP_ENV__Code(soap, n) soap_instantiate_SOAP_ENV__Code(soap, n, NULL, NULL, NULL) +inline struct SOAP_ENV__Code * soap_new_SOAP_ENV__Code(struct soap *soap, int n = -1) { return soap_instantiate_SOAP_ENV__Code(soap, n, NULL, NULL, NULL); } +inline struct SOAP_ENV__Code * soap_new_req_SOAP_ENV__Code(struct soap *soap) { struct SOAP_ENV__Code *_p = soap_instantiate_SOAP_ENV__Code(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_SOAP_ENV__Code(soap, _p); } return _p; } -#define soap_delete_SOAP_ENV__Code(soap, p) soap_delete(soap, p) +inline struct SOAP_ENV__Code * soap_new_set_SOAP_ENV__Code(struct soap *soap, char *SOAP_ENV__Value, struct SOAP_ENV__Code *SOAP_ENV__Subcode) { struct SOAP_ENV__Code *_p = soap_instantiate_SOAP_ENV__Code(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_SOAP_ENV__Code(soap, _p); _p->SOAP_ENV__Value = SOAP_ENV__Value; _p->SOAP_ENV__Subcode = SOAP_ENV__Subcode; } return _p; } + +inline void soap_delete_SOAP_ENV__Code(struct soap *soap, struct SOAP_ENV__Code *p) { soap_delete(soap, p); } -SOAP_FMAC1 struct SOAP_ENV__Code * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Code(struct soap*, int, const char*, const char*, size_t*); SOAP_FMAC3 void SOAP_FMAC4 soap_copy_SOAP_ENV__Code(struct soap*, int, int, void*, size_t, const void*, size_t); #endif @@ -214,25 +236,28 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_SOAP_ENV__Header(struct soap*, const s SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV__Header(struct soap*, const char*, int, const struct SOAP_ENV__Header *, const char*); SOAP_FMAC3 struct SOAP_ENV__Header * SOAP_FMAC4 soap_in_SOAP_ENV__Header(struct soap*, const char*, struct SOAP_ENV__Header *, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Header(struct soap*, const struct SOAP_ENV__Header *, const char*, const char*); + #ifndef soap_write_SOAP_ENV__Header -#define soap_write_SOAP_ENV__Header(soap, data) ( soap_begin_send(soap) || (soap_serialize_SOAP_ENV__Header(soap, data), 0) || soap_put_SOAP_ENV__Header(soap, data, "SOAP-ENV:Header", NULL) || soap_end_send(soap) ) +#define soap_write_SOAP_ENV__Header(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_SOAP_ENV__Header(soap, data),0) || soap_put_SOAP_ENV__Header(soap, data, "SOAP-ENV:Header", NULL) || soap_end_send(soap), (soap)->error ) #endif - -SOAP_FMAC3 int SOAP_FMAC4 soap_put_SOAP_ENV__Header(struct soap*, const struct SOAP_ENV__Header *, const char*, const char*); +SOAP_FMAC3 struct SOAP_ENV__Header * SOAP_FMAC4 soap_get_SOAP_ENV__Header(struct soap*, struct SOAP_ENV__Header *, const char*, const char*); #ifndef soap_read_SOAP_ENV__Header -#define soap_read_SOAP_ENV__Header(soap, data) ( soap_begin_recv(soap) || !soap_get_SOAP_ENV__Header(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_SOAP_ENV__Header(soap, data) ( soap_begin_recv(soap) || !soap_get_SOAP_ENV__Header(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 struct SOAP_ENV__Header * SOAP_FMAC4 soap_get_SOAP_ENV__Header(struct soap*, struct SOAP_ENV__Header *, const char*, const char*); +SOAP_FMAC1 struct SOAP_ENV__Header * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Header(struct soap*, int, const char*, const char*, size_t*); -#define soap_new_SOAP_ENV__Header(soap, n) soap_instantiate_SOAP_ENV__Header(soap, n, NULL, NULL, NULL) +inline struct SOAP_ENV__Header * soap_new_SOAP_ENV__Header(struct soap *soap, int n = -1) { return soap_instantiate_SOAP_ENV__Header(soap, n, NULL, NULL, NULL); } +inline struct SOAP_ENV__Header * soap_new_req_SOAP_ENV__Header(struct soap *soap) { struct SOAP_ENV__Header *_p = soap_instantiate_SOAP_ENV__Header(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_SOAP_ENV__Header(soap, _p); } return _p; } -#define soap_delete_SOAP_ENV__Header(soap, p) soap_delete(soap, p) +inline struct SOAP_ENV__Header * soap_new_set_SOAP_ENV__Header(struct soap *soap) { struct SOAP_ENV__Header *_p = soap_instantiate_SOAP_ENV__Header(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_SOAP_ENV__Header(soap, _p); } return _p; } + +inline void soap_delete_SOAP_ENV__Header(struct soap *soap, struct SOAP_ENV__Header *p) { soap_delete(soap, p); } -SOAP_FMAC1 struct SOAP_ENV__Header * SOAP_FMAC2 soap_instantiate_SOAP_ENV__Header(struct soap*, int, const char*, const char*, size_t*); SOAP_FMAC3 void SOAP_FMAC4 soap_copy_SOAP_ENV__Header(struct soap*, int, int, void*, size_t, const void*, size_t); #endif @@ -245,25 +270,28 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__executeCommand(struct soap*, cons SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__executeCommand(struct soap*, const char*, int, const struct ns1__executeCommand *, const char*); SOAP_FMAC3 struct ns1__executeCommand * SOAP_FMAC4 soap_in_ns1__executeCommand(struct soap*, const char*, struct ns1__executeCommand *, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__executeCommand(struct soap*, const struct ns1__executeCommand *, const char*, const char*); + #ifndef soap_write_ns1__executeCommand -#define soap_write_ns1__executeCommand(soap, data) ( soap_begin_send(soap) || (soap_serialize_ns1__executeCommand(soap, data), 0) || soap_put_ns1__executeCommand(soap, data, "ns1:executeCommand", NULL) || soap_end_send(soap) ) +#define soap_write_ns1__executeCommand(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_ns1__executeCommand(soap, data),0) || soap_put_ns1__executeCommand(soap, data, "ns1:executeCommand", NULL) || soap_end_send(soap), (soap)->error ) #endif - -SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__executeCommand(struct soap*, const struct ns1__executeCommand *, const char*, const char*); +SOAP_FMAC3 struct ns1__executeCommand * SOAP_FMAC4 soap_get_ns1__executeCommand(struct soap*, struct ns1__executeCommand *, const char*, const char*); #ifndef soap_read_ns1__executeCommand -#define soap_read_ns1__executeCommand(soap, data) ( soap_begin_recv(soap) || !soap_get_ns1__executeCommand(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_ns1__executeCommand(soap, data) ( soap_begin_recv(soap) || !soap_get_ns1__executeCommand(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 struct ns1__executeCommand * SOAP_FMAC4 soap_get_ns1__executeCommand(struct soap*, struct ns1__executeCommand *, const char*, const char*); +SOAP_FMAC1 struct ns1__executeCommand * SOAP_FMAC2 soap_instantiate_ns1__executeCommand(struct soap*, int, const char*, const char*, size_t*); -#define soap_new_ns1__executeCommand(soap, n) soap_instantiate_ns1__executeCommand(soap, n, NULL, NULL, NULL) +inline struct ns1__executeCommand * soap_new_ns1__executeCommand(struct soap *soap, int n = -1) { return soap_instantiate_ns1__executeCommand(soap, n, NULL, NULL, NULL); } +inline struct ns1__executeCommand * soap_new_req_ns1__executeCommand(struct soap *soap) { struct ns1__executeCommand *_p = soap_instantiate_ns1__executeCommand(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_ns1__executeCommand(soap, _p); } return _p; } -#define soap_delete_ns1__executeCommand(soap, p) soap_delete(soap, p) +inline struct ns1__executeCommand * soap_new_set_ns1__executeCommand(struct soap *soap, char *command) { struct ns1__executeCommand *_p = soap_instantiate_ns1__executeCommand(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_ns1__executeCommand(soap, _p); _p->command = command; } return _p; } + +inline void soap_delete_ns1__executeCommand(struct soap *soap, struct ns1__executeCommand *p) { soap_delete(soap, p); } -SOAP_FMAC1 struct ns1__executeCommand * SOAP_FMAC2 soap_instantiate_ns1__executeCommand(struct soap*, int, const char*, const char*, size_t*); SOAP_FMAC3 void SOAP_FMAC4 soap_copy_ns1__executeCommand(struct soap*, int, int, void*, size_t, const void*, size_t); #ifndef SOAP_TYPE_ns1__executeCommandResponse @@ -274,25 +302,28 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_ns1__executeCommandResponse(struct soa SOAP_FMAC3 int SOAP_FMAC4 soap_out_ns1__executeCommandResponse(struct soap*, const char*, int, const struct ns1__executeCommandResponse *, const char*); SOAP_FMAC3 struct ns1__executeCommandResponse * SOAP_FMAC4 soap_in_ns1__executeCommandResponse(struct soap*, const char*, struct ns1__executeCommandResponse *, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__executeCommandResponse(struct soap*, const struct ns1__executeCommandResponse *, const char*, const char*); + #ifndef soap_write_ns1__executeCommandResponse -#define soap_write_ns1__executeCommandResponse(soap, data) ( soap_begin_send(soap) || (soap_serialize_ns1__executeCommandResponse(soap, data), 0) || soap_put_ns1__executeCommandResponse(soap, data, "ns1:executeCommandResponse", NULL) || soap_end_send(soap) ) +#define soap_write_ns1__executeCommandResponse(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_ns1__executeCommandResponse(soap, data),0) || soap_put_ns1__executeCommandResponse(soap, data, "ns1:executeCommandResponse", NULL) || soap_end_send(soap), (soap)->error ) #endif - -SOAP_FMAC3 int SOAP_FMAC4 soap_put_ns1__executeCommandResponse(struct soap*, const struct ns1__executeCommandResponse *, const char*, const char*); +SOAP_FMAC3 struct ns1__executeCommandResponse * SOAP_FMAC4 soap_get_ns1__executeCommandResponse(struct soap*, struct ns1__executeCommandResponse *, const char*, const char*); #ifndef soap_read_ns1__executeCommandResponse -#define soap_read_ns1__executeCommandResponse(soap, data) ( soap_begin_recv(soap) || !soap_get_ns1__executeCommandResponse(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_ns1__executeCommandResponse(soap, data) ( soap_begin_recv(soap) || !soap_get_ns1__executeCommandResponse(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 struct ns1__executeCommandResponse * SOAP_FMAC4 soap_get_ns1__executeCommandResponse(struct soap*, struct ns1__executeCommandResponse *, const char*, const char*); +SOAP_FMAC1 struct ns1__executeCommandResponse * SOAP_FMAC2 soap_instantiate_ns1__executeCommandResponse(struct soap*, int, const char*, const char*, size_t*); -#define soap_new_ns1__executeCommandResponse(soap, n) soap_instantiate_ns1__executeCommandResponse(soap, n, NULL, NULL, NULL) +inline struct ns1__executeCommandResponse * soap_new_ns1__executeCommandResponse(struct soap *soap, int n = -1) { return soap_instantiate_ns1__executeCommandResponse(soap, n, NULL, NULL, NULL); } +inline struct ns1__executeCommandResponse * soap_new_req_ns1__executeCommandResponse(struct soap *soap) { struct ns1__executeCommandResponse *_p = soap_instantiate_ns1__executeCommandResponse(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_ns1__executeCommandResponse(soap, _p); } return _p; } -#define soap_delete_ns1__executeCommandResponse(soap, p) soap_delete(soap, p) +inline struct ns1__executeCommandResponse * soap_new_set_ns1__executeCommandResponse(struct soap *soap, char **result) { struct ns1__executeCommandResponse *_p = soap_instantiate_ns1__executeCommandResponse(soap, -1, NULL, NULL, NULL); if (_p) { soap_default_ns1__executeCommandResponse(soap, _p); _p->result = result; } return _p; } + +inline void soap_delete_ns1__executeCommandResponse(struct soap *soap, struct ns1__executeCommandResponse *p) { soap_delete(soap, p); } -SOAP_FMAC1 struct ns1__executeCommandResponse * SOAP_FMAC2 soap_instantiate_ns1__executeCommandResponse(struct soap*, int, const char*, const char*, size_t*); SOAP_FMAC3 void SOAP_FMAC4 soap_copy_ns1__executeCommandResponse(struct soap*, int, int, void*, size_t, const void*, size_t); #ifndef WITH_NOGLOBAL @@ -303,18 +334,18 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_copy_ns1__executeCommandResponse(struct soap*, i SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason *const*); SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Reason(struct soap*, const char *, int, struct SOAP_ENV__Reason *const*, const char *); SOAP_FMAC3 struct SOAP_ENV__Reason ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Reason(struct soap*, const char*, struct SOAP_ENV__Reason **, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason *const*, const char*, const char*); #ifndef soap_write_PointerToSOAP_ENV__Reason -#define soap_write_PointerToSOAP_ENV__Reason(soap, data) ( soap_begin_send(soap) || (soap_serialize_PointerToSOAP_ENV__Reason(soap, data), 0) || soap_put_PointerToSOAP_ENV__Reason(soap, data, "SOAP-ENV:Reason", NULL) || soap_end_send(soap) ) +#define soap_write_PointerToSOAP_ENV__Reason(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_PointerToSOAP_ENV__Reason(soap, data),0) || soap_put_PointerToSOAP_ENV__Reason(soap, data, "SOAP-ENV:Reason", NULL) || soap_end_send(soap), (soap)->error ) #endif -SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason *const*, const char*, const char*); +SOAP_FMAC3 struct SOAP_ENV__Reason ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason **, const char*, const char*); #ifndef soap_read_PointerToSOAP_ENV__Reason -#define soap_read_PointerToSOAP_ENV__Reason(soap, data) ( soap_begin_recv(soap) || !soap_get_PointerToSOAP_ENV__Reason(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_PointerToSOAP_ENV__Reason(soap, data) ( soap_begin_recv(soap) || !soap_get_PointerToSOAP_ENV__Reason(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 struct SOAP_ENV__Reason ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Reason(struct soap*, struct SOAP_ENV__Reason **, const char*, const char*); #endif @@ -326,18 +357,18 @@ SOAP_FMAC3 struct SOAP_ENV__Reason ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Rea SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail *const*); SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Detail(struct soap*, const char *, int, struct SOAP_ENV__Detail *const*, const char *); SOAP_FMAC3 struct SOAP_ENV__Detail ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Detail(struct soap*, const char*, struct SOAP_ENV__Detail **, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail *const*, const char*, const char*); #ifndef soap_write_PointerToSOAP_ENV__Detail -#define soap_write_PointerToSOAP_ENV__Detail(soap, data) ( soap_begin_send(soap) || (soap_serialize_PointerToSOAP_ENV__Detail(soap, data), 0) || soap_put_PointerToSOAP_ENV__Detail(soap, data, "SOAP-ENV:Detail", NULL) || soap_end_send(soap) ) +#define soap_write_PointerToSOAP_ENV__Detail(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_PointerToSOAP_ENV__Detail(soap, data),0) || soap_put_PointerToSOAP_ENV__Detail(soap, data, "SOAP-ENV:Detail", NULL) || soap_end_send(soap), (soap)->error ) #endif -SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail *const*, const char*, const char*); +SOAP_FMAC3 struct SOAP_ENV__Detail ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail **, const char*, const char*); #ifndef soap_read_PointerToSOAP_ENV__Detail -#define soap_read_PointerToSOAP_ENV__Detail(soap, data) ( soap_begin_recv(soap) || !soap_get_PointerToSOAP_ENV__Detail(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_PointerToSOAP_ENV__Detail(soap, data) ( soap_begin_recv(soap) || !soap_get_PointerToSOAP_ENV__Detail(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 struct SOAP_ENV__Detail ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Detail(struct soap*, struct SOAP_ENV__Detail **, const char*, const char*); #endif @@ -349,18 +380,18 @@ SOAP_FMAC3 struct SOAP_ENV__Detail ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Det SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerToSOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code *const*); SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerToSOAP_ENV__Code(struct soap*, const char *, int, struct SOAP_ENV__Code *const*, const char *); SOAP_FMAC3 struct SOAP_ENV__Code ** SOAP_FMAC4 soap_in_PointerToSOAP_ENV__Code(struct soap*, const char*, struct SOAP_ENV__Code **, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code *const*, const char*, const char*); #ifndef soap_write_PointerToSOAP_ENV__Code -#define soap_write_PointerToSOAP_ENV__Code(soap, data) ( soap_begin_send(soap) || (soap_serialize_PointerToSOAP_ENV__Code(soap, data), 0) || soap_put_PointerToSOAP_ENV__Code(soap, data, "SOAP-ENV:Code", NULL) || soap_end_send(soap) ) +#define soap_write_PointerToSOAP_ENV__Code(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_PointerToSOAP_ENV__Code(soap, data),0) || soap_put_PointerToSOAP_ENV__Code(soap, data, "SOAP-ENV:Code", NULL) || soap_end_send(soap), (soap)->error ) #endif -SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerToSOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code *const*, const char*, const char*); +SOAP_FMAC3 struct SOAP_ENV__Code ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code **, const char*, const char*); #ifndef soap_read_PointerToSOAP_ENV__Code -#define soap_read_PointerToSOAP_ENV__Code(soap, data) ( soap_begin_recv(soap) || !soap_get_PointerToSOAP_ENV__Code(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_PointerToSOAP_ENV__Code(soap, data) ( soap_begin_recv(soap) || !soap_get_PointerToSOAP_ENV__Code(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 struct SOAP_ENV__Code ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Code(struct soap*, struct SOAP_ENV__Code **, const char*, const char*); #endif @@ -370,18 +401,18 @@ SOAP_FMAC3 struct SOAP_ENV__Code ** SOAP_FMAC4 soap_get_PointerToSOAP_ENV__Code( SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_PointerTostring(struct soap*, char **const*); SOAP_FMAC3 int SOAP_FMAC4 soap_out_PointerTostring(struct soap*, const char *, int, char **const*, const char *); SOAP_FMAC3 char *** SOAP_FMAC4 soap_in_PointerTostring(struct soap*, const char*, char ***, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerTostring(struct soap*, char **const*, const char*, const char*); #ifndef soap_write_PointerTostring -#define soap_write_PointerTostring(soap, data) ( soap_begin_send(soap) || (soap_serialize_PointerTostring(soap, data), 0) || soap_put_PointerTostring(soap, data, "byte", NULL) || soap_end_send(soap) ) +#define soap_write_PointerTostring(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_PointerTostring(soap, data),0) || soap_put_PointerTostring(soap, data, "byte", NULL) || soap_end_send(soap), (soap)->error ) #endif -SOAP_FMAC3 int SOAP_FMAC4 soap_put_PointerTostring(struct soap*, char **const*, const char*, const char*); +SOAP_FMAC3 char *** SOAP_FMAC4 soap_get_PointerTostring(struct soap*, char ***, const char*, const char*); #ifndef soap_read_PointerTostring -#define soap_read_PointerTostring(soap, data) ( soap_begin_recv(soap) || !soap_get_PointerTostring(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_PointerTostring(soap, data) ( soap_begin_recv(soap) || !soap_get_PointerTostring(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 char *** SOAP_FMAC4 soap_get_PointerTostring(struct soap*, char ***, const char*, const char*); #ifndef SOAP_TYPE__QName #define SOAP_TYPE__QName (5) @@ -389,23 +420,21 @@ SOAP_FMAC3 char *** SOAP_FMAC4 soap_get_PointerTostring(struct soap*, char ***, #define soap_default__QName(soap, a) soap_default_string(soap, a) - -#define soap_serialize__QName(soap, a) soap_serialize_string(soap, a) - +SOAP_FMAC3 void SOAP_FMAC4 soap_serialize__QName(struct soap*, char *const*); SOAP_FMAC3 int SOAP_FMAC4 soap_out__QName(struct soap*, const char*, int, char*const*, const char*); SOAP_FMAC3 char * * SOAP_FMAC4 soap_in__QName(struct soap*, const char*, char **, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put__QName(struct soap*, char *const*, const char*, const char*); #ifndef soap_write__QName -#define soap_write__QName(soap, data) ( soap_begin_send(soap) || (soap_serialize__QName(soap, data), 0) || soap_put__QName(soap, data, "byte", NULL) || soap_end_send(soap) ) +#define soap_write__QName(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize__QName(soap, data),0) || soap_put__QName(soap, data, "byte", NULL) || soap_end_send(soap), (soap)->error ) #endif -SOAP_FMAC3 int SOAP_FMAC4 soap_put__QName(struct soap*, char *const*, const char*, const char*); +SOAP_FMAC3 char ** SOAP_FMAC4 soap_get__QName(struct soap*, char **, const char*, const char*); #ifndef soap_read__QName -#define soap_read__QName(soap, data) ( soap_begin_recv(soap) || !soap_get__QName(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read__QName(soap, data) ( soap_begin_recv(soap) || !soap_get__QName(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 char ** SOAP_FMAC4 soap_get__QName(struct soap*, char **, const char*, const char*); #ifndef SOAP_TYPE_string #define SOAP_TYPE_string (4) @@ -414,18 +443,18 @@ SOAP_FMAC3 void SOAP_FMAC4 soap_default_string(struct soap*, char **); SOAP_FMAC3 void SOAP_FMAC4 soap_serialize_string(struct soap*, char *const*); SOAP_FMAC3 int SOAP_FMAC4 soap_out_string(struct soap*, const char*, int, char*const*, const char*); SOAP_FMAC3 char * * SOAP_FMAC4 soap_in_string(struct soap*, const char*, char **, const char*); +SOAP_FMAC3 int SOAP_FMAC4 soap_put_string(struct soap*, char *const*, const char*, const char*); #ifndef soap_write_string -#define soap_write_string(soap, data) ( soap_begin_send(soap) || (soap_serialize_string(soap, data), 0) || soap_put_string(soap, data, "byte", NULL) || soap_end_send(soap) ) +#define soap_write_string(soap, data) ( soap_free_temp(soap), soap_begin_send(soap) || (soap_serialize_string(soap, data),0) || soap_put_string(soap, data, "byte", NULL) || soap_end_send(soap), (soap)->error ) #endif -SOAP_FMAC3 int SOAP_FMAC4 soap_put_string(struct soap*, char *const*, const char*, const char*); +SOAP_FMAC3 char ** SOAP_FMAC4 soap_get_string(struct soap*, char **, const char*, const char*); #ifndef soap_read_string -#define soap_read_string(soap, data) ( soap_begin_recv(soap) || !soap_get_string(soap, data, NULL, NULL) || soap_end_recv(soap) ) +#define soap_read_string(soap, data) ( soap_begin_recv(soap) || !soap_get_string(soap, data, NULL, NULL) || soap_end_recv(soap), (soap)->error ) #endif -SOAP_FMAC3 char ** SOAP_FMAC4 soap_get_string(struct soap*, char **, const char*, const char*); #endif diff --git a/dep/gsoap/soapServer.cpp b/dep/gsoap/soapServer.cpp index b6304a3e525..6e5c8040165 100644 --- a/dep/gsoap/soapServer.cpp +++ b/dep/gsoap/soapServer.cpp @@ -1,9 +1,9 @@ /* soapServer.cpp - Generated by gSOAP 2.8.10 from gsoap.stub + Generated by gSOAP 2.8.17r from gsoap.stub -Copyright(C) 2000-2012, Robert van Engelen, Genivia Inc. All Rights Reserved. +Copyright(C) 2000-2013, Robert van Engelen, Genivia Inc. All Rights Reserved. The generated code is released under one of the following licenses: -1) GPL or 2) Genivia's license for commercial use. +GPL or Genivia's license for commercial use. This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. */ @@ -14,10 +14,10 @@ compiling, linking, and/or using OpenSSL is allowed. #endif #include "soapH.h" -SOAP_SOURCE_STAMP("@(#) soapServer.cpp ver 2.8.10 2012-09-02 20:48:00 GMT") +SOAP_SOURCE_STAMP("@(#) soapServer.cpp ver 2.8.17r 2014-06-21 21:43:17 GMT") -SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap *soap) +extern "C" SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap *soap) { #ifndef WITH_FASTCGI unsigned int k = soap->max_keep_alive; @@ -53,7 +53,7 @@ SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap *soap) } #ifndef WITH_NOSERVEREQUEST -SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap *soap) +extern "C" SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap *soap) { soap_peek_element(soap); if (!soap_match_tag(soap, soap->tag, "ns1:executeCommand")) @@ -70,7 +70,6 @@ SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__executeCommand(struct soap *soap) soap_tmp_string = NULL; soap_tmp_ns1__executeCommandResponse.result = &soap_tmp_string; soap_default_ns1__executeCommand(soap, &soap_tmp_ns1__executeCommand); - soap->encodingStyle = NULL; if (!soap_get_ns1__executeCommand(soap, &soap_tmp_ns1__executeCommand, "ns1:executeCommand", NULL)) return soap->error; if (soap_body_end_in(soap) @@ -80,6 +79,7 @@ SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__executeCommand(struct soap *soap) soap->error = ns1__executeCommand(soap, soap_tmp_ns1__executeCommand.command, soap_tmp_ns1__executeCommandResponse.result); if (soap->error) return soap->error; + soap->encodingStyle = NULL; soap_serializeheader(soap); soap_serialize_ns1__executeCommandResponse(soap, &soap_tmp_ns1__executeCommandResponse); if (soap_begin_count(soap)) diff --git a/dep/gsoap/soapStub.h b/dep/gsoap/soapStub.h index fbdd170e0f4..38374561cde 100644 --- a/dep/gsoap/soapStub.h +++ b/dep/gsoap/soapStub.h @@ -1,9 +1,9 @@ /* soapStub.h - Generated by gSOAP 2.8.10 from gsoap.stub + Generated by gSOAP 2.8.17r from gsoap.stub -Copyright(C) 2000-2012, Robert van Engelen, Genivia Inc. All Rights Reserved. +Copyright(C) 2000-2013, Robert van Engelen, Genivia Inc. All Rights Reserved. The generated code is released under one of the following licenses: -1) GPL or 2) Genivia's license for commercial use. +GPL or Genivia's license for commercial use. This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. */ @@ -11,7 +11,7 @@ compiling, linking, and/or using OpenSSL is allowed. #ifndef soapStub_H #define soapStub_H #include "stdsoap2.h" -#if GSOAP_VERSION != 20810 +#if GSOAP_VERSION != 20817 # error "GSOAP VERSION MISMATCH IN GENERATED CODE: PLEASE REINSTALL PACKAGE" #endif @@ -48,6 +48,8 @@ struct ns1__executeCommandResponse { public: char **result; /* SOAP 1.2 RPC return element (when namespace qualified) */ /* optional element of type xsd:string */ +public: + int soap_type() const { return 9; } /* = unique type id SOAP_TYPE_ns1__executeCommandResponse */ }; #endif @@ -58,6 +60,8 @@ struct ns1__executeCommand { public: char *command; /* optional element of type xsd:string */ +public: + int soap_type() const { return 10; } /* = unique type id SOAP_TYPE_ns1__executeCommand */ }; #endif @@ -68,6 +72,8 @@ public: /* SOAP Header: */ struct SOAP_ENV__Header { +public: + int soap_type() const { return 11; } /* = unique type id SOAP_TYPE_SOAP_ENV__Header */ #ifdef WITH_NOEMPTYSTRUCT private: char dummy; /* dummy member to enable compilation */ @@ -87,6 +93,8 @@ struct SOAP_ENV__Code public: char *SOAP_ENV__Value; /* optional element of type xsd:QName */ struct SOAP_ENV__Code *SOAP_ENV__Subcode; /* optional element of type SOAP-ENV:Code */ +public: + int soap_type() const { return 12; } /* = unique type id SOAP_TYPE_SOAP_ENV__Code */ }; #endif @@ -103,6 +111,8 @@ public: char *__any; int __type; /* any type of element <fault> (defined below) */ void *fault; /* transient */ +public: + int soap_type() const { return 14; } /* = unique type id SOAP_TYPE_SOAP_ENV__Detail */ }; #endif @@ -117,6 +127,8 @@ struct SOAP_ENV__Reason { public: char *SOAP_ENV__Text; /* optional element of type xsd:string */ +public: + int soap_type() const { return 17; } /* = unique type id SOAP_TYPE_SOAP_ENV__Reason */ }; #endif @@ -139,6 +151,8 @@ public: char *SOAP_ENV__Node; /* optional element of type xsd:string */ char *SOAP_ENV__Role; /* optional element of type xsd:string */ struct SOAP_ENV__Detail *SOAP_ENV__Detail; /* optional element of type SOAP-ENV:Detail */ +public: + int soap_type() const { return 18; } /* = unique type id SOAP_TYPE_SOAP_ENV__Fault */ }; #endif @@ -183,9 +197,9 @@ SOAP_FMAC5 int SOAP_FMAC6 ns1__executeCommand(struct soap*, char *command, char * * \******************************************************************************/ -SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap*); +extern "C" SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap*); -SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap*); +extern "C" SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap*); SOAP_FMAC5 int SOAP_FMAC6 soap_serve_ns1__executeCommand(struct soap*); diff --git a/dep/gsoap/stdsoap2.cpp b/dep/gsoap/stdsoap2.cpp index 36a8901dcab..6186f8904e3 100644 --- a/dep/gsoap/stdsoap2.cpp +++ b/dep/gsoap/stdsoap2.cpp @@ -1,10 +1,10 @@ /* - stdsoap2.c[pp] 2.8.10 + stdsoap2.c[pp] 2.8.17r gSOAP runtime engine gSOAP XML Web services tools -Copyright (C) 2000-2012, Robert van Engelen, Genivia Inc., All Rights Reserved. +Copyright (C) 2000-2013, Robert van Engelen, Genivia Inc., All Rights Reserved. This part of the software is released under ONE of the following licenses: GPL, or the gSOAP public license, or Genivia's license for commercial use. -------------------------------------------------------------------------------- @@ -24,7 +24,7 @@ WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Initial Developer of the Original Code is Robert A. van Engelen. -Copyright (C) 2000-2012, Robert van Engelen, Genivia Inc., All Rights Reserved. +Copyright (C) 2000-2013, Robert van Engelen, Genivia Inc., All Rights Reserved. -------------------------------------------------------------------------------- GPL license. @@ -51,13 +51,16 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com -------------------------------------------------------------------------------- */ -#define GSOAP_LIB_VERSION 20810 +#define GSOAP_LIB_VERSION 20817 #ifdef AS400 # pragma convert(819) /* EBCDIC to ASCII */ #endif #include "stdsoap2.h" +#if defined(VXWORKS) && defined(WM_SECURE_KEY_STORAGE) +#include <ipcom_key_db.h> +#endif #if GSOAP_VERSION != GSOAP_LIB_VERSION # error "GSOAP VERSION MISMATCH IN LIBRARY: PLEASE REINSTALL PACKAGE" #endif @@ -76,10 +79,10 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com #endif #ifdef __cplusplus -SOAP_SOURCE_STAMP("@(#) stdsoap2.cpp ver 2.8.10 2012-08-16 00:00:00 GMT") +SOAP_SOURCE_STAMP("@(#) stdsoap2.cpp ver 2.8.17r 2013-12-18 00:00:00 GMT") extern "C" { #else -SOAP_SOURCE_STAMP("@(#) stdsoap2.c ver 2.8.10 2012-08-16 00:00:00 GMT") +SOAP_SOURCE_STAMP("@(#) stdsoap2.c ver 2.8.17r 2013-12-18 00:00:00 GMT") #endif /* 8bit character representing unknown/nonrepresentable character data (e.g. not supported by current locale with multibyte support enabled) */ @@ -570,7 +573,7 @@ fsend(struct soap *soap, const char *s, size_t n) err = soap->errnum; if (!err) return soap->error; - if (err != SOAP_EINTR && err != SOAP_EAGAIN && err != SOAP_EWOULDBLOCK) + if (err != SOAP_EAGAIN && err != SOAP_EWOULDBLOCK) return SOAP_EOF; } } @@ -614,6 +617,14 @@ fsend(struct soap *soap, const char *s, size_t n) } while (nwritten < 0 && --udp_repeat > 0); } + if (nwritten < 0) + { err = soap_socket_errno(sk); + if (err && err != SOAP_EINTR) + { soap->errnum = err; + return SOAP_EOF; + } + nwritten = 0; /* and call write() again */ + } } else #endif @@ -654,7 +665,7 @@ fsend(struct soap *soap, const char *s, size_t n) r = tcp_select(soap, sk, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, soap->send_timeout ? soap->send_timeout : -10000); if (!r && soap->send_timeout) return SOAP_EOF; - if (r < 0 && soap->errnum != SOAP_EINTR) + if (r < 0) return SOAP_EOF; } else if (err && err != SOAP_EINTR) @@ -809,13 +820,18 @@ soap_flush_raw(struct soap *soap, const char *s, size_t n) #ifndef WITH_LEANER if ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK) { char t[16]; - sprintf(t, "\r\n%lX\r\n" + (soap->chunksize ? 0 : 2), (unsigned long)n); +#ifdef HAVE_SNPRINTF + soap_snprintf(t, sizeof(t), &"\r\n%lX\r\n"[soap->chunksize ? 0 : 2], (unsigned long)n); +#else + sprintf(t, &"\r\n%lX\r\n"[soap->chunksize ? 0 : 2], (unsigned long)n); +#endif DBGMSG(SENT, t, strlen(t)); if ((soap->error = soap->fsend(soap, t, strlen(t)))) return soap->error; soap->chunksize += n; } DBGMSG(SENT, s, n); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Send %u bytes to socket=%d/fd=%d\n", (unsigned int)n, soap->socket, soap->sendfd)); #endif return soap->error = soap->fsend(soap, s, n); } @@ -899,7 +915,7 @@ frecv(struct soap *soap, char *s, size_t n) if (!r) return 0; r = soap->errnum; - if (r != SOAP_EINTR && r != SOAP_EAGAIN && r != SOAP_EWOULDBLOCK) + if (r != SOAP_EAGAIN && r != SOAP_EWOULDBLOCK) return 0; } } @@ -955,11 +971,11 @@ frecv(struct soap *soap, char *s, size_t n) } #if defined(WITH_OPENSSL) if (soap->ssl && err == SSL_ERROR_WANT_WRITE) - r = tcp_select(soap, sk, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, soap->recv_timeout ? soap->recv_timeout : 5); + r = tcp_select(soap, sk, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, soap->recv_timeout ? soap->recv_timeout : 5); else #elif defined(WITH_GNUTLS) if (soap->session && gnutls_record_get_direction(soap->session)) - r = tcp_select(soap, sk, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, soap->recv_timeout ? soap->recv_timeout : 5); + r = tcp_select(soap, sk, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, soap->recv_timeout ? soap->recv_timeout : 5); else #endif r = tcp_select(soap, sk, SOAP_TCP_SELECT_RCV | SOAP_TCP_SELECT_ERR, soap->recv_timeout ? soap->recv_timeout : 5); @@ -967,7 +983,7 @@ frecv(struct soap *soap, char *s, size_t n) return 0; if (r < 0) { r = soap->errnum; - if (r != SOAP_EINTR && r != SOAP_EAGAIN && r != SOAP_EWOULDBLOCK) + if (r != SOAP_EAGAIN && r != SOAP_EWOULDBLOCK) return 0; } if (retries-- <= 0) @@ -1108,12 +1124,12 @@ zlib_again: t = tmp; if (!soap->chunkbuflen) { soap->chunkbuflen = ret = soap->frecv(soap, soap->buf, SOAP_BUFLEN); - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Read %u bytes (chunked) from socket %d\n", (unsigned int)ret, soap->socket)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Read %u bytes (chunked) from socket=%d\n", (unsigned int)ret, soap->socket)); DBGMSG(RECV, soap->buf, ret); soap->bufidx = 0; if (!ret) { soap->ahead = EOF; - return EOF; + return EOF; } } else @@ -1123,7 +1139,7 @@ zlib_again: while (!soap_isxdigit((int)(c = soap_getchunkchar(soap)))) { if ((int)c == EOF) { soap->ahead = EOF; - return EOF; + return EOF; } } do @@ -1217,6 +1233,7 @@ zlib_again: return soap->error = r; #endif soap->count += ret; + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Read count=%lu (+%lu)\n", (unsigned long)soap->count, (unsigned long)ret)); return !ret; } #endif @@ -1637,9 +1654,9 @@ soap_get_pi(struct soap *soap) SOAP_FMAC1 int SOAP_FMAC2 -soap_move(struct soap *soap, long n) -{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Moving %ld bytes forward\n", (long)n)); - for (; n > 0; n--) +soap_move(struct soap *soap, size_t n) +{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Moving %lu bytes forward\n", (unsigned long)n)); + for (; n; n--) if ((int)soap_getchar(soap) == EOF) return SOAP_EOF; return SOAP_OK; @@ -1697,8 +1714,12 @@ soap_pututf8(struct soap *soap, register unsigned long c) *t++ = (char)(0x80 | (c & 0x3F)); *t = '\0'; } + else +#endif +#ifdef HAVE_SNPRINTF + soap_snprintf(tmp, sizeof(tmp), "&#%lu;", c); #else - sprintf(tmp, "&#%lu;", c); + sprintf(tmp, "&#%lu;", c); #endif return soap_send(soap, tmp); } @@ -1712,7 +1733,7 @@ SOAP_FMAC2 soap_getutf8(struct soap *soap) { register soap_wchar c, c1, c2, c3, c4; c = soap->ahead; - if (c) + if (c >= 0x80) soap->ahead = 0; else c = soap_get(soap); @@ -1930,8 +1951,8 @@ soap_getbase64(struct soap *soap, int *n, int malloc_flag) register int j = 0; do { register soap_wchar c = soap_get(soap); - if (c < SOAP_AP) - c &= 0x7FFFFFFF; + if (c < SOAP_AP) + c &= 0x7FFFFFFF; if (c == '=' || c < 0) { unsigned char *p; switch (j) @@ -2527,27 +2548,70 @@ SOAP_FMAC1 char * SOAP_FMAC2 soap_putsizesoffsets(struct soap *soap, const char *type, const int *size, const int *offset, int dim) -{ int i; - if (!type) +{ register int i; + register size_t l; + if (!type || strlen(type) + 13 > sizeof(soap->type)) /* prevent overruns */ return NULL; if (soap->version == 2) - { sprintf(soap->type, "%s[%d", type, size[0]); + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->type, sizeof(soap->type) - 1, "%s[%d", type, size[0]); +#else + sprintf(soap->type, "%s[%d", type, size[0]); +#endif for (i = 1; i < dim; i++) - sprintf(soap->type + strlen(soap->type), " %d", size[i]); + { +#ifdef HAVE_SNPRINTF + l = strlen(soap->type); + soap_snprintf(soap->type + l, sizeof(soap->type) - l - 1, " %d", size[i]); +#else + if ((l = strlen(soap->type)) + 13 > sizeof(soap->type)) + return NULL; + sprintf(soap->type + l, " %d", size[i]); +#endif + } } else { if (offset) - { sprintf(soap->type, "%s[%d", type, size[0] + offset[0]); + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->type, sizeof(soap->type) - 1, "%s[%d", type, size[0] + offset[0]); +#else + sprintf(soap->type, "%s[%d", type, size[0] + offset[0]); +#endif for (i = 1; i < dim; i++) - sprintf(soap->type + strlen(soap->type), ",%d", size[i] + offset[i]); + { +#ifdef HAVE_SNPRINTF + l = strlen(soap->type); + soap_snprintf(soap->type + l, sizeof(soap->type) - l - 1, ",%d", size[i] + offset[i]); +#else + if ((l = strlen(soap->type)) + 13 > sizeof(soap->type)) + return NULL; + sprintf(soap->type + l, ",%d", size[i] + offset[i]); +#endif + } } else - { sprintf(soap->type, "%s[%d", type, size[0]); + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->type, sizeof(soap->type) - 1, "%s[%d", type, size[0]); +#else + sprintf(soap->type, "%s[%d", type, size[0]); +#endif for (i = 1; i < dim; i++) - sprintf(soap->type + strlen(soap->type), ",%d", size[i]); + { +#ifdef HAVE_SNPRINTF + l = strlen(soap->type); + soap_snprintf(soap->type + l, sizeof(soap->type) - l - 1, ",%d", size[i]); +#else + if ((l = strlen(soap->type)) + 13 > sizeof(soap->type)) + return NULL; + sprintf(soap->type + l, ",%d", size[i]); +#endif + } } - strcat(soap->type, "]"); } + strcat(soap->type, "]"); return soap->type; } #endif @@ -2569,9 +2633,25 @@ char * SOAP_FMAC2 soap_putoffsets(struct soap *soap, const int *offset, int dim) { register int i; + register size_t l; +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->arrayOffset, sizeof(soap->arrayOffset) - 1, "[%d", offset[0]); +#else + if (sizeof(soap->arrayOffset) < 13) /* prevent overruns */ + return NULL; sprintf(soap->arrayOffset, "[%d", offset[0]); +#endif for (i = 1; i < dim; i++) - sprintf(soap->arrayOffset + strlen(soap->arrayOffset), ",%d", offset[i]); + { +#ifdef HAVE_SNPRINTF + l = strlen(soap->arrayOffset); + soap_snprintf(soap->arrayOffset + l, sizeof(soap->arrayOffset) - l - 1, ",%d", offset[i]); +#else + if ((l = strlen(soap->arrayOffset)) + 13 > sizeof(soap->arrayOffset)) + return NULL; + sprintf(soap->arrayOffset + l, ",%d", offset[i]); +#endif + } strcat(soap->arrayOffset, "]"); return soap->arrayOffset; } @@ -2901,6 +2981,11 @@ soap_match_tag(struct soap *soap, const char *tag1, const char *tag2) return err; } } + else if (!t[1]) + { err = soap_match_namespace(soap, tag1, tag2, 0, t - tag2); + if (err == SOAP_NAMESPACE) + return SOAP_TAG_MISMATCH; + } else if (SOAP_STRCMP(tag1, t + 1)) { return SOAP_TAG_MISMATCH; } @@ -2970,9 +3055,16 @@ soap_rand() SOAP_FMAC1 int SOAP_FMAC2 +#if defined(VXWORKS) && defined(WM_SECURE_KEY_STORAGE) +soap_ssl_server_context(struct soap *soap, unsigned short flags, const char *keyfile, const char *keyid, const char *password, const char *cafile, const char *capath, const char *dhfile, const char *randfile, const char *sid) +#else soap_ssl_server_context(struct soap *soap, unsigned short flags, const char *keyfile, const char *password, const char *cafile, const char *capath, const char *dhfile, const char *randfile, const char *sid) +#endif { int err; soap->keyfile = keyfile; +#if defined(VXWORKS) && defined(WM_SECURE_KEY_STORAGE) + soap->keyid = keyid; +#endif soap->password = password; soap->cafile = cafile; soap->capath = capath; @@ -3038,8 +3130,15 @@ soap_ssl_server_context(struct soap *soap, unsigned short flags, const char *key SOAP_FMAC1 int SOAP_FMAC2 +#if defined(VXWORKS) && defined(WM_SECURE_KEY_STORAGE) +soap_ssl_client_context(struct soap *soap, unsigned short flags, const char *keyfile, const char *keyid, const char *password, const char *cafile, const char *capath, const char *randfile) +#else soap_ssl_client_context(struct soap *soap, unsigned short flags, const char *keyfile, const char *password, const char *cafile, const char *capath, const char *randfile) +#endif { soap->keyfile = keyfile; +#if defined(VXWORKS) && defined(WM_SECURE_KEY_STORAGE) + soap->keyid = keyid; +#endif soap->password = password; soap->cafile = cafile; soap->capath = capath; @@ -3130,13 +3229,16 @@ soap_ssl_error(struct soap *soap, int ret) else { switch (ret) { case 0: - strcpy(soap->msgbuf, "EOF was observed that violates the protocol. The client probably provided invalid authentication information."); + strcpy(soap->msgbuf, "EOF was observed that violates the SSL/TLS protocol. The client probably provided invalid authentication information."); break; case -1: #ifdef HAVE_SNPRINTF - soap_snprintf(soap->msgbuf, sizeof(soap->msgbuf), "Error observed by underlying BIO: %s", strerror(errno)); + soap_snprintf(soap->msgbuf, sizeof(soap->msgbuf), "Error observed by underlying SSL/TLS BIO: %s", strerror(errno)); #else - sprintf(soap->msgbuf, "Error observed by underlying BIO: %s", strerror(errno)); + { const char *s = strerror(errno); + size_t l = strlen(s); + sprintf(soap->msgbuf, "Error observed by underlying SSL/TLS BIO: %s", l + 44 < sizeof(soap->msgbuf) ? s : SOAP_STR_EOS); + } #endif break; } @@ -3159,6 +3261,9 @@ ssl_auth_init(struct soap *soap) #ifdef WITH_OPENSSL long flags; int mode; +#if defined(VXWORKS) && defined(WM_SECURE_KEY_STORAGE) + EVP_PKEY* pkey; +#endif if (!soap_ssl_init_done) soap_ssl_init(); ERR_clear_error(); @@ -3194,7 +3299,17 @@ ssl_auth_init(struct soap *soap) } if (!SSL_CTX_use_PrivateKey_file(soap->ctx, soap->keyfile, SSL_FILETYPE_PEM)) return soap_set_receiver_error(soap, "SSL/TLS error", "Can't read key file", SOAP_SSL_ERROR); +#ifndef WM_SECURE_KEY_STORAGE + if (!SSL_CTX_use_PrivateKey_file(soap->ctx, soap->keyfile, SSL_FILETYPE_PEM)) + return soap_set_receiver_error(soap, "SSL/TLS error", "Can't read key file", SOAP_SSL_ERROR); +#endif } +#if defined(VXWORKS) && defined(WM_SECURE_KEY_STORAGE) + if (NULL == (pkey = ipcom_key_db_pkey_get(soap->keyid))) + return soap_set_receiver_error(soap, "SSL error", "Can't find key", SOAP_SSL_ERROR); + if (0 == SSL_CTX_use_PrivateKey(soap->ctx, pkey)) + return soap_set_receiver_error(soap, "SSL error", "Can't read key", SOAP_SSL_ERROR); +#endif /* Suggested alternative approach to check the key file for certs (cafile=NULL):*/ #if 0 if (soap->password) @@ -3225,7 +3340,11 @@ ssl_auth_init(struct soap *soap) int n = (int)soap_strtoul(soap->dhfile, &s, 10); /* if dhfile is numeric, treat it as a key length to generate DH params which can take a while */ if (n >= 512 && s && *s == '\0') +#if defined(VXWORKS) + DH_generate_parameters_ex(dh, n, 2/*or 5*/, NULL); +#else dh = DH_generate_parameters(n, 2/*or 5*/, NULL, NULL); +#endif else { BIO *bio; bio = BIO_new_file(soap->dhfile, "r"); @@ -3478,7 +3597,7 @@ soap_ssl_accept(struct soap *soap) s = tcp_select(soap, sk, SOAP_TCP_SELECT_RCV | SOAP_TCP_SELECT_ERR, -100000); else s = tcp_select(soap, sk, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, -100000); - if (s < 0 && soap->errnum != SOAP_EINTR) + if (s < 0) break; } else @@ -3533,7 +3652,7 @@ soap_ssl_accept(struct soap *soap) s = tcp_select(soap, sk, SOAP_TCP_SELECT_RCV | SOAP_TCP_SELECT_ERR, -100000); else s = tcp_select(soap, sk, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, -100000); - if (s < 0 && soap->errnum != SOAP_EINTR) + if (s < 0) break; } else @@ -3611,7 +3730,12 @@ tcp_error(struct soap *soap) msg = soap_code_str(h_error_codes, soap->errnum); if (!msg) #endif - { sprintf(soap->msgbuf, "TCP/UDP IP error %d", soap->errnum); + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->msgbuf, sizeof(soap->msgbuf), "TCP/UDP IP error %d", soap->errnum); +#else + sprintf(soap->msgbuf, "TCP/UDP IP error %d", soap->errnum); +#endif msg = soap->msgbuf; } } @@ -3647,7 +3771,7 @@ tcp_gethost(struct soap *soap, const char *addr, struct in_addr *inaddr) { memcpy(inaddr, &iadd, sizeof(iadd)); return SOAP_OK; } -#if defined(__GLIBC__) || (defined(HAVE_GETHOSTBYNAME_R) && (defined(FREEBSD) || defined(__FreeBSD__))) +#if defined(__GLIBC__) || (defined(HAVE_GETHOSTBYNAME_R) && (defined(FREEBSD) || defined(__FreeBSD__))) || defined(__ANDROID__) if (gethostbyname_r(addr, &hostent, soap->buf, SOAP_BUFLEN, &host, &soap->errnum) < 0) host = NULL; #elif defined(_AIX43) || ((defined(TRU64) || defined(HP_UX)) && defined(HAVE_GETHOSTBYNAME_R)) @@ -3915,7 +4039,7 @@ again: } #endif #endif - DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Opening socket %d to host='%s' port=%d\n", sk, host, port)); + DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Opening socket=%d to host='%s' port=%d\n", sk, host, port)); #ifndef WITH_IPV6 soap->peerlen = sizeof(soap->peer); memset((void*)&soap->peer, 0, sizeof(soap->peer)); @@ -3988,7 +4112,7 @@ again: #endif return SOAP_INVALID_SOCKET; } - r = soap->errnum; + r = soap->errnum = soap_socket_errno(sk); if (r != SOAP_EINTR) { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Could not connect to host\n")); soap_set_sender_error(soap, tcp_error(soap), "connect failed in tcp_connect()", SOAP_TCP_ERROR); @@ -4057,7 +4181,7 @@ again: short keep_alive = soap->keep_alive; /* save the KA status */ soap->omode &= ~SOAP_ENC; /* mask IO and ENC */ soap->omode |= SOAP_IO_BUFFER; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Connecting to %s proxy server\n", soap->proxy_http_version)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Connecting to %s proxy server %s for destination endpoint %s\n", soap->proxy_http_version, soap->proxy_host, endpoint)); #ifdef WITH_NTLM if (soap->ntlm_challenge) { if (soap_ntlm_handshake(soap, SOAP_CONNECT, endpoint, host, port)) @@ -4071,7 +4195,7 @@ again: soap->status = SOAP_CONNECT; soap->keep_alive = 1; if ((soap->error = soap->fpost(soap, endpoint, host, port, NULL, NULL, 0)) - || soap_end_send(soap)) + || soap_end_send_flush(soap)) { soap->fclosesocket(soap, sk); return SOAP_INVALID_SOCKET; } @@ -4095,7 +4219,9 @@ again: return SOAP_INVALID_SOCKET; } if (endpoint) - strncpy(soap->endpoint, endpoint, sizeof(soap->endpoint)-1); /* restore */ + { strncpy(soap->endpoint, endpoint, sizeof(soap->endpoint)); /* restore */ + soap->endpoint[sizeof(soap->endpoint) - 1] = '\0'; + } soap->mode = m; } #ifdef WITH_OPENSSL @@ -4146,7 +4272,7 @@ again: s = tcp_select(soap, sk, SOAP_TCP_SELECT_RCV | SOAP_TCP_SELECT_ERR, -100000); else s = tcp_select(soap, sk, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, -100000); - if (s < 0 && soap->errnum != SOAP_EINTR) + if (s < 0) { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "SSL_connect/select error in tcp_connect\n")); soap_set_sender_error(soap, soap_ssl_error(soap, r), "SSL_connect failed in tcp_connect()", SOAP_TCP_ERROR); soap->fclosesocket(soap, sk); @@ -4178,15 +4304,20 @@ again: } if (!(soap->ssl_flags & SOAP_SSL_SKIP_HOST_CHECK)) { X509_NAME *subj; + STACK_OF(CONF_VALUE) *val = NULL; +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) + GENERAL_NAMES *names = NULL; +#else int ext_count; +#endif int ok = 0; - X509 *peer; - peer = SSL_get_peer_certificate(soap->ssl); + X509 *peer = SSL_get_peer_certificate(soap->ssl); if (!peer) { soap_set_sender_error(soap, "SSL/TLS error", "No SSL/TLS certificate was presented by the peer in tcp_connect()", SOAP_SSL_ERROR); soap->fclosesocket(soap, sk); return SOAP_INVALID_SOCKET; } +#if (OPENSSL_VERSION_NUMBER < 0x0090800fL) ext_count = X509_get_ext_count(peer); if (ext_count > 0) { int i; @@ -4195,36 +4326,42 @@ again: const char *ext_str = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext))); if (ext_str && !strcmp(ext_str, "subjectAltName")) { X509V3_EXT_METHOD *meth = (X509V3_EXT_METHOD*)X509V3_EXT_get(ext); - void *ext_data; -#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) - const unsigned char *data; -#else unsigned char *data; -#endif - STACK_OF(CONF_VALUE) *val; - int j; if (!meth) break; data = ext->value->data; + if (data) + { #if (OPENSSL_VERSION_NUMBER > 0x00907000L) - if (meth->it) - ext_data = ASN1_item_d2i(NULL, &data, ext->value->length, ASN1_ITEM_ptr(meth->it)); - else - { /* OpenSSL not perfectly portable at this point (?): - Some compilers appear to prefer - meth->d2i(NULL, (const unsigned char**)&data, ... - and others prefer - meth->d2i(NULL, &data, ext->value->length); - */ - ext_data = meth->d2i(NULL, &data, ext->value->length); - } + void *ext_data; + if (meth->it) + ext_data = ASN1_item_d2i(NULL, &data, ext->value->length, ASN1_ITEM_ptr(meth->it)); + else + { /* OpenSSL is not portable at this point (?): + Some compilers appear to prefer + meth->d2i(NULL, (const unsigned char**)&data, ... + and others prefer + meth->d2i(NULL, &data, ext->value->length); + */ + ext_data = meth->d2i(NULL, &data, ext->value->length); + } + if (ext_data) + val = meth->i2v(meth, ext_data, NULL); + else + val = NULL; + if (meth->it) + ASN1_item_free((ASN1_VALUE*)ext_data, ASN1_ITEM_ptr(meth->it)); + else + meth->ext_free(ext_data); #else - ext_data = meth->d2i(NULL, &data, ext->value->length); + void *ext_data = meth->d2i(NULL, &data, ext->value->length); + if (ext_data) + val = meth->i2v(meth, ext_data, NULL); + meth->ext_free(ext_data); #endif - if (ext_data) - { val = meth->i2v(meth, ext_data, NULL); if (val) - { for (j = 0; j < sk_CONF_VALUE_num(val); j++) + { int j; + for (j = 0; j < sk_CONF_VALUE_num(val); j++) { CONF_VALUE *nval = sk_CONF_VALUE_value(val, j); if (nval && !strcmp(nval->name, "DNS") && !strcmp(nval->value, host)) { ok = 1; @@ -4233,20 +4370,30 @@ again: } sk_CONF_VALUE_pop_free(val, X509V3_conf_free); } -#if (OPENSSL_VERSION_NUMBER > 0x00907000L) - if (meth->it) - ASN1_item_free((ASN1_VALUE*)ext_data, ASN1_ITEM_ptr(meth->it)); - else - meth->ext_free(ext_data); -#else - meth->ext_free(ext_data); -#endif } } if (ok) break; } } +#else + names = (GENERAL_NAMES*)X509_get_ext_d2i(peer, NID_subject_alt_name, NULL, NULL); + if (names) + { val = i2v_GENERAL_NAMES(NULL, names, val); + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + } + if (val) + { int j; + for (j = 0; j < sk_CONF_VALUE_num(val); j++) + { CONF_VALUE *nval = sk_CONF_VALUE_value(val, j); + if (nval && !strcmp(nval->name, "DNS") && !strcmp(nval->value, host)) + { ok = 1; + break; + } + } + sk_CONF_VALUE_pop_free(val, X509V3_conf_free); + } +#endif if (!ok && (subj = X509_get_subject_name(peer))) { int i = -1; do @@ -4310,7 +4457,7 @@ again: s = tcp_select(soap, sk, SOAP_TCP_SELECT_RCV | SOAP_TCP_SELECT_ERR, -100000); else s = tcp_select(soap, sk, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, -100000); - if (s < 0 && soap->errnum != SOAP_EINTR) + if (s < 0) break; } else @@ -4352,9 +4499,11 @@ again: #ifndef PALM_1 static int tcp_select(struct soap *soap, SOAP_SOCKET sk, int flags, int timeout) -{ register int r; +{ int r; struct timeval tv; fd_set fd[3], *rfd, *sfd, *efd; + int retries = 0; + int eintr = SOAP_MAXEINTR; soap->errnum = 0; #ifndef WIN32 #if !defined(FD_SETSIZE) || defined(__QNX__) || defined(QNX) @@ -4366,7 +4515,6 @@ tcp_select(struct soap *soap, SOAP_SOCKET sk, int flags, int timeout) #endif #ifdef HAVE_POLL { struct pollfd pollfd; - int retries = 0; pollfd.fd = (int)sk; pollfd.events = 0; if (flags & SOAP_TCP_SELECT_RCV) @@ -4375,16 +4523,17 @@ tcp_select(struct soap *soap, SOAP_SOCKET sk, int flags, int timeout) pollfd.events |= POLLOUT; if (flags & SOAP_TCP_SELECT_ERR) pollfd.events |= POLLERR; - if (timeout < 0) + if (timeout <= 0) timeout /= -1000; /* -usec -> ms */ - else if (timeout <= 1000000) /* avoid overflow */ - timeout *= 1000; /* sec -> ms */ else - { retries = timeout / 1000000; - timeout = 1000000000; + { retries = timeout - 1; + timeout = 1000; } - do r = poll(&pollfd, 1, timeout); - while (r == 0 && retries--); + do + { r = poll(&pollfd, 1, timeout); + if (r < 0 && (soap->errnum = soap_socket_errno(sk)) == SOAP_EINTR && eintr--) + continue; + } while (r == 0 && retries--); if (r > 0) { r = 0; if ((flags & SOAP_TCP_SELECT_RCV) && (pollfd.revents & POLLIN)) @@ -4394,8 +4543,6 @@ tcp_select(struct soap *soap, SOAP_SOCKET sk, int flags, int timeout) if ((flags & SOAP_TCP_SELECT_ERR) && (pollfd.revents & POLLERR)) r |= SOAP_TCP_SELECT_ERR; } - else if (r < 0) - soap->errnum = soap_socket_errno(s); return r; } #else @@ -4404,31 +4551,37 @@ tcp_select(struct soap *soap, SOAP_SOCKET sk, int flags, int timeout) } #endif #endif - rfd = sfd = efd = NULL; - if (flags & SOAP_TCP_SELECT_RCV) - { rfd = &fd[0]; - FD_ZERO(rfd); - FD_SET(sk, rfd); - } - if (flags & SOAP_TCP_SELECT_SND) - { sfd = &fd[1]; - FD_ZERO(sfd); - FD_SET(sk, sfd); - } - if (flags & SOAP_TCP_SELECT_ERR) - { efd = &fd[2]; - FD_ZERO(efd); - FD_SET(sk, efd); - } - if (timeout >= 0) - { tv.tv_sec = timeout; - tv.tv_usec = 0; - } - else - { tv.tv_sec = -timeout / 1000000; - tv.tv_usec = -timeout % 1000000; - } - r = select((int)sk + 1, rfd, sfd, efd, &tv); + if (timeout > 0) + retries = timeout - 1; + do + { rfd = sfd = efd = NULL; + if (flags & SOAP_TCP_SELECT_RCV) + { rfd = &fd[0]; + FD_ZERO(rfd); + FD_SET(sk, rfd); + } + if (flags & SOAP_TCP_SELECT_SND) + { sfd = &fd[1]; + FD_ZERO(sfd); + FD_SET(sk, sfd); + } + if (flags & SOAP_TCP_SELECT_ERR) + { efd = &fd[2]; + FD_ZERO(efd); + FD_SET(sk, efd); + } + if (timeout <= 0) + { tv.tv_sec = -timeout / 1000000; + tv.tv_usec = -timeout % 1000000; + } + else + { tv.tv_sec = 1; + tv.tv_usec = 0; + } + r = select((int)sk + 1, rfd, sfd, efd, &tv); + if (r < 0 && (soap->errnum = soap_socket_errno(sk)) == SOAP_EINTR && eintr--) + continue; + } while (r == 0 && retries--); if (r > 0) { r = 0; if ((flags & SOAP_TCP_SELECT_RCV) && FD_ISSET(sk, rfd)) @@ -4438,8 +4591,6 @@ tcp_select(struct soap *soap, SOAP_SOCKET sk, int flags, int timeout) if ((flags & SOAP_TCP_SELECT_ERR) && FD_ISSET(sk, efd)) r |= SOAP_TCP_SELECT_ERR; } - else if (r < 0) - soap->errnum = soap_socket_errno(s); return r; } #endif @@ -4509,7 +4660,7 @@ tcp_disconnect(struct soap *soap) timeout) */ r = tcp_select(soap, soap->socket, SOAP_TCP_SELECT_RCV | SOAP_TCP_SELECT_ERR, 5); - if (r <= 0 && soap->errnum != SOAP_EINTR) + if (r <= 0) { soap->errnum = 0; DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Connection lost...\n")); soap->fclosesocket(soap, soap->socket); @@ -4565,7 +4716,7 @@ tcp_disconnect(struct soap *soap) static int tcp_closesocket(struct soap *soap, SOAP_SOCKET sk) { (void)soap; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Close socket %d\n", (int)sk)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Close socket=%d\n", (int)sk)); return soap_closesocket(sk); } #endif @@ -4577,7 +4728,7 @@ tcp_closesocket(struct soap *soap, SOAP_SOCKET sk) static int tcp_shutdownsocket(struct soap *soap, SOAP_SOCKET sk, int how) { (void)soap; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Shutdown socket %d how=%d\n", (int)sk, how)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Shutdown socket=%d how=%d\n", (int)sk, how)); return shutdown(sk, how); } #endif @@ -4788,11 +4939,13 @@ soap_poll(struct soap *soap) } else #endif + { int t; if (soap_valid_socket(soap->socket) && (r & SOAP_TCP_SELECT_SND) && (!(r & SOAP_TCP_SELECT_RCV) - || recv(soap->socket, soap->tmpbuf, 1, MSG_PEEK) > 0)) + || recv(soap->socket, (char*)&t, 1, MSG_PEEK) > 0)) return SOAP_OK; + } } else if (r < 0) { if ((soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) && soap_socket_errno(soap->master) != SOAP_EINTR) @@ -4878,10 +5031,14 @@ soap_accept(struct soap *soap) soap->port = soap_strtol(port, NULL, 10); #else soap->ip = ntohl(soap->peer.sin_addr.s_addr); +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->host, sizeof(soap->host), "%u.%u.%u.%u", (int)(soap->ip>>24)&0xFF, (int)(soap->ip>>16)&0xFF, (int)(soap->ip>>8)&0xFF, (int)soap->ip&0xFF); +#else sprintf(soap->host, "%u.%u.%u.%u", (int)(soap->ip>>24)&0xFF, (int)(soap->ip>>16)&0xFF, (int)(soap->ip>>8)&0xFF, (int)soap->ip&0xFF); +#endif soap->port = (int)ntohs(soap->peer.sin_port); /* does not return port number on some systems */ #endif - DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Accept socket %d at port %d from IP %s\n", soap->socket, soap->port, soap->host)); + DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Accept socket=%d at port=%d from IP='%s'\n", soap->socket, soap->port, soap->host)); #ifndef WITH_LEAN if (soap->accept_flags == SO_LINGER) { struct linger linger; @@ -4971,17 +5128,20 @@ soap_closesock(struct soap *soap) soap->keep_alive = 0; } #ifdef WITH_ZLIB - if (soap->zlib_state == SOAP_ZLIB_DEFLATE) - deflateEnd(soap->d_stream); - else if (soap->zlib_state == SOAP_ZLIB_INFLATE) - inflateEnd(soap->d_stream); - soap->zlib_state = SOAP_ZLIB_NONE; + if (!(soap->mode & SOAP_MIME_POSTCHECK)) + { if (soap->zlib_state == SOAP_ZLIB_DEFLATE) + deflateEnd(soap->d_stream); + else if (soap->zlib_state == SOAP_ZLIB_INFLATE) + inflateEnd(soap->d_stream); + soap->zlib_state = SOAP_ZLIB_NONE; + } #endif return soap->error = status; } #endif /******************************************************************************/ +#ifndef WITH_NOIO #ifndef PALM_1 SOAP_FMAC1 int @@ -4993,6 +5153,7 @@ soap_force_closesock(struct soap *soap) return SOAP_OK; } #endif +#endif /******************************************************************************/ #ifndef WITH_NOIO @@ -5024,7 +5185,7 @@ soap_done(struct soap *soap) #endif if (soap_check_state(soap)) return; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Done with context\n")); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Done with context%s\n", soap->state == SOAP_COPY ? " copy" : "")); soap_free_temp(soap); while (soap->clist) { struct soap_clist *p = soap->clist->next; @@ -5034,6 +5195,8 @@ soap_done(struct soap *soap) if (soap->state == SOAP_INIT) soap->omode &= ~SOAP_IO_UDP; /* to force close the socket */ soap->keep_alive = 0; /* to force close the socket */ + if (soap->master == soap->socket) /* do not close twice */ + soap->master = SOAP_INVALID_SOCKET; soap_closesock(soap); #ifdef WITH_COOKIES soap_free_cookies(soap); @@ -5194,14 +5357,13 @@ soap_done(struct soap *soap) /******************************************************************************/ #ifndef WITH_NOHTTP #ifndef PALM_1 -int +static int http_parse(struct soap *soap) { char header[SOAP_HDRLEN], *s; unsigned short httpcmd = 0; int status = 0; DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Waiting for HTTP request/response...\n")); *soap->endpoint = '\0'; - soap->length = 0; #ifdef WITH_NTLM if (!soap->ntlm_challenge) #endif @@ -5213,11 +5375,13 @@ http_parse(struct soap *soap) soap->ntlm_challenge = NULL; #endif soap->proxy_from = NULL; - soap->http_content = NULL; - soap->action = NULL; - soap->status = 0; do - { if (soap_getline(soap, soap->msgbuf, sizeof(soap->msgbuf))) + { soap->length = 0; + soap->http_content = NULL; + soap->action = NULL; + soap->status = 0; + soap->body = 1; + if (soap_getline(soap, soap->msgbuf, sizeof(soap->msgbuf))) { if (soap->error == SOAP_EOF) return SOAP_EOF; return soap->error = 414; @@ -5271,9 +5435,7 @@ http_parse(struct soap *soap) { if (soap->keep_alive == 1) soap->keep_alive = 0; if (soap->status == 0 && (soap->omode & SOAP_IO) == SOAP_IO_CHUNK) /* soap->status == 0 for HTTP request */ - { soap->imode |= SOAP_IO_CHUNK; - soap->omode = (soap->omode & ~SOAP_IO) | SOAP_IO_STORE; - } + soap->omode = (soap->omode & ~SOAP_IO) | SOAP_IO_STORE; /* HTTP 1.0 does not support chunked transfers */ } if (soap->keep_alive < 0) soap->keep_alive = 1; @@ -5297,12 +5459,16 @@ http_parse(struct soap *soap) if (s && httpcmd) { size_t m = strlen(soap->endpoint); size_t n = m + (s - soap->msgbuf) - l - 1; - if (m > n) - m = n; + size_t k; if (n >= sizeof(soap->endpoint)) n = sizeof(soap->endpoint) - 1; - strncpy(soap->path, soap->msgbuf + l, n - m); - soap->path[n - m] = '\0'; + if (m > n) + m = n; + k = n - m + 1; + if (k > sizeof(soap->path)) + k = sizeof(soap->path); + strncpy(soap->path, soap->msgbuf + l, k); + soap->path[k - 1] = '\0'; if (*soap->path && *soap->path != '/') *soap->endpoint = '\0'; strcat(soap->endpoint, soap->path); @@ -5329,28 +5495,27 @@ http_parse(struct soap *soap) return soap->error = status; else if (s) return soap->error = 405; - } - /* Status OK (HTTP 200) */ - if (soap->status == 0 || soap->status == 200) return SOAP_OK; - /* Status 201 (Created), 202 (Accepted), ... and HTTP 400 and 500 errors - may not have a body. When content length, content type, or chunking is - used assume there is a message to parse, either XML or HTTP. - */ + } +#if 0 if (soap->length > 0 || (soap->http_content && (!soap->keep_alive || soap->recv_timeout)) || (soap->imode & SOAP_IO) == SOAP_IO_CHUNK) - { if ((soap->status > 200 && soap->status <= 299) - || soap->status == 400 - || soap->status == 500) +#endif + if (soap->body) + { if ((soap->status >= 200 && soap->status <= 299) /* OK, Accepted, etc */ + || soap->status == 400 /* Bad Request */ + || soap->status == 500) /* Internal Server Error */ return SOAP_OK; /* force close afterwards in soap_closesock() */ soap->keep_alive = 0; #ifndef WITH_LEAN /* read HTTP body for error details */ - s = soap_get_http_body(soap); + s = soap_get_http_body(soap, NULL); if (s) return soap_set_receiver_error(soap, soap->msgbuf, s, soap->status); #endif } + else if (soap->status >= 200 && soap->status <= 299) + return soap->error = soap->status; DBGLOG(TEST,SOAP_MESSAGE(fdebug, "HTTP error %d\n", soap->status)); return soap_set_receiver_error(soap, "HTTP Error", soap->msgbuf, soap->status); } @@ -5371,7 +5536,6 @@ http_parse_header(struct soap *soap, const char *key, const char *val) #endif strcpy(soap->endpoint, "http://"); strncat(soap->endpoint, val, sizeof(soap->endpoint) - 8); - soap->endpoint[sizeof(soap->endpoint) - 1] = '\0'; } #ifndef WITH_LEANER else if (!soap_tag_cmp(key, "Content-Type")) @@ -5389,7 +5553,8 @@ http_parse_header(struct soap *soap, const char *key, const char *val) if (action) { if (*action == '"') { soap->action = soap_strdup(soap, action + 1); - soap->action[strlen(soap->action) - 1] = '\0'; + if (*soap->action) + soap->action[strlen(soap->action) - 1] = '\0'; } else soap->action = soap_strdup(soap, action); @@ -5398,6 +5563,8 @@ http_parse_header(struct soap *soap, const char *key, const char *val) #endif else if (!soap_tag_cmp(key, "Content-Length")) { soap->length = soap_strtoul(val, NULL, 10); + if (!soap->length) + soap->body = 0; } else if (!soap_tag_cmp(key, "Content-Encoding")) { if (!soap_tag_cmp(val, "deflate")) @@ -5439,8 +5606,14 @@ http_parse_header(struct soap *soap, const char *key, const char *val) soap->keep_alive = 0; } #ifndef WITH_LEAN - else if (!soap_tag_cmp(key, "Authorization")) - { if (!soap_tag_cmp(val, "Basic *")) + else if (!soap_tag_cmp(key, "Authorization") || !soap_tag_cmp(key, "Proxy-Authorization")) + { +#ifdef WITH_NTLM + if (!soap_tag_cmp(val, "NTLM*")) + soap->ntlm_challenge = soap_strdup(soap, val + 4); + else +#endif + if (!soap_tag_cmp(val, "Basic *")) { int n; char *s; soap_base642s(soap, val + 6, soap->tmpbuf, sizeof(soap->tmpbuf) - 1, &n); @@ -5472,7 +5645,8 @@ http_parse_header(struct soap *soap, const char *key, const char *val) else if (!soap_tag_cmp(key, "SOAPAction")) { if (*val == '"') { soap->action = soap_strdup(soap, val + 1); - soap->action[strlen(soap->action) - 1] = '\0'; + if (*soap->action) + soap->action[strlen(soap->action) - 1] = '\0'; } else soap->action = soap_strdup(soap, val); @@ -5555,24 +5729,28 @@ static const char* soap_decode(char *buf, size_t len, const char *val, const char *sep) { const char *s; char *t = buf; + size_t i = len; for (s = val; *s; s++) if (*s != ' ' && *s != '\t' && !strchr(sep, *s)) break; - if (*s == '"') - { s++; - while (*s && *s != '"' && --len) - *t++ = *s++; - } - else - { while (*s && !soap_blank((soap_wchar)*s) && !strchr(sep, *s) && --len) - { if (*s == '%') - { *t++ = ((s[1] >= 'A' ? (s[1] & 0x7) + 9 : s[1] - '0') << 4) - + (s[2] >= 'A' ? (s[2] & 0x7) + 9 : s[2] - '0'); - s += 3; - } - else + if (len > 0) + { if (*s == '"') + { s++; + while (*s && *s != '"' && --i) *t++ = *s++; } + else + { while (*s && !soap_blank((soap_wchar)*s) && !strchr(sep, *s) && --i) + { if (*s == '%' && s[1] && s[2]) + { *t++ = ((s[1] >= 'A' ? (s[1] & 0x7) + 9 : s[1] - '0') << 4) + + (s[2] >= 'A' ? (s[2] & 0x7) + 9 : s[2] - '0'); + s += 3; + } + else + *t++ = *s++; + } + } + buf[len - 1] = '\0'; /* appease */ } *t = '\0'; while (*s && !strchr(sep, *s)) @@ -5617,8 +5795,8 @@ http_get(struct soap *soap) #ifndef PALM_1 static int http_405(struct soap *soap) -{ return 405; - (void)soap; +{ (void)soap; + return 405; } #endif #endif @@ -5663,14 +5841,33 @@ http_post(struct soap *soap, const char *endpoint, const char *host, int port, c if (!endpoint || (soap_tag_cmp(endpoint, "http:*") && soap_tag_cmp(endpoint, "https:*") && strncmp(endpoint, "httpg:", 6))) #endif return SOAP_OK; - if (strlen(endpoint) + strlen(soap->http_version) > sizeof(soap->tmpbuf) - 80) - return soap->error = SOAP_EOM; /* prevent overrun */ + if (strlen(endpoint) + strlen(soap->http_version) > sizeof(soap->tmpbuf) - 80 + || strlen(host) + strlen(soap->http_version) > sizeof(soap->tmpbuf) - 80) + return soap->error = SOAP_EOM; /* prevent overrun (note that 'host' and 'soap->host' are substrings of 'endpoint') */ if (soap->status == SOAP_CONNECT) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "%s %s:%d HTTP/%s", s, soap->host, soap->port, soap->http_version); +#else sprintf(soap->tmpbuf, "%s %s:%d HTTP/%s", s, soap->host, soap->port, soap->http_version); +#endif + } else if (soap->proxy_host && endpoint) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "%s %s HTTP/%s", s, endpoint, soap->http_version); +#else sprintf(soap->tmpbuf, "%s %s HTTP/%s", s, endpoint, soap->http_version); +#endif + } else + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "%s /%s HTTP/%s", s, (*path == '/' ? path + 1 : path), soap->http_version); +#else sprintf(soap->tmpbuf, "%s /%s HTTP/%s", s, (*path == '/' ? path + 1 : path), soap->http_version); +#endif + } if ((err = soap->fposthdr(soap, soap->tmpbuf, NULL))) return err; #ifdef WITH_OPENSSL @@ -5681,13 +5878,38 @@ http_post(struct soap *soap, const char *endpoint, const char *host, int port, c { #ifdef WITH_IPV6 if (*host != '[' && strchr(host, ':')) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "[%s]:%d", host, port); /* RFC 2732 */ +#else sprintf(soap->tmpbuf, "[%s]:%d", host, port); /* RFC 2732 */ +#endif + } else #endif + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "%s:%d", host, port); +#else sprintf(soap->tmpbuf, "%s:%d", host, port); +#endif + } } else - strcpy(soap->tmpbuf, host); + { +#ifdef WITH_IPV6 + if (*host != '[' && strchr(host, ':')) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "[%s]", host); /* RFC 2732 */ +#else + sprintf(soap->tmpbuf, "[%s]", host); /* RFC 2732 */ +#endif + } + else +#endif + strcpy(soap->tmpbuf, host); + } if ((err = soap->fposthdr(soap, "Host", soap->tmpbuf))) return err; if ((err = soap->fposthdr(soap, "User-Agent", "gSOAP/2.8"))) @@ -5703,34 +5925,51 @@ http_post(struct soap *soap, const char *endpoint, const char *host, int port, c return err; #endif #ifndef WITH_LEAN - if (soap->userid && soap->passwd && strlen(soap->userid) + strlen(soap->passwd) < 761) - { #ifdef WITH_NTLM - if (soap->ntlm_challenge && strlen(soap->ntlm_challenge) + 6 < sizeof(soap->tmpbuf)) + if (soap->ntlm_challenge && strlen(soap->ntlm_challenge) + 6 < sizeof(soap->tmpbuf)) + { if (*soap->ntlm_challenge) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "NTLM %s", soap->ntlm_challenge); +#else sprintf(soap->tmpbuf, "NTLM %s", soap->ntlm_challenge); - else #endif - { strcpy(soap->tmpbuf, "Basic "); - sprintf(soap->tmpbuf + 262, "%s:%s", soap->userid, soap->passwd); - soap_s2base64(soap, (const unsigned char*)(soap->tmpbuf + 262), soap->tmpbuf + 6, (int)strlen(soap->tmpbuf + 262)); + if (soap->proxy_host) + { if ((err = soap->fposthdr(soap, "Proxy-Authorization", soap->tmpbuf))) + return err; + } + else if ((err = soap->fposthdr(soap, "Authorization", soap->tmpbuf))) + return err; } + } + else + { +#endif + if (soap->userid && soap->passwd && strlen(soap->userid) + strlen(soap->passwd) < 761) + { strcpy(soap->tmpbuf, "Basic "); +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf + 262, sizeof(soap->tmpbuf) - 262, "%s:%s", soap->userid, soap->passwd); +#else + sprintf(soap->tmpbuf + 262, "%s:%s", soap->userid, soap->passwd); +#endif + soap_s2base64(soap, (const unsigned char*)(soap->tmpbuf + 262), soap->tmpbuf + 6, (int)strlen(soap->tmpbuf + 262)); if ((err = soap->fposthdr(soap, "Authorization", soap->tmpbuf))) return err; } if (soap->proxy_userid && soap->proxy_passwd && strlen(soap->proxy_userid) + strlen(soap->proxy_passwd) < 761) - { -#ifdef WITH_NTLM - if (soap->ntlm_challenge && strlen(soap->ntlm_challenge) + 6 < sizeof(soap->tmpbuf)) - sprintf(soap->tmpbuf, "NTLM %s", soap->ntlm_challenge); - else + { strcpy(soap->tmpbuf, "Basic "); +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf + 262, sizeof(soap->tmpbuf) - 262, "%s:%s", soap->proxy_userid, soap->proxy_passwd); +#else + sprintf(soap->tmpbuf + 262, "%s:%s", soap->proxy_userid, soap->proxy_passwd); #endif - { strcpy(soap->tmpbuf, "Basic "); - sprintf(soap->tmpbuf + 262, "%s:%s", soap->proxy_userid, soap->proxy_passwd); - soap_s2base64(soap, (const unsigned char*)(soap->tmpbuf + 262), soap->tmpbuf + 6, (int)strlen(soap->tmpbuf + 262)); - } + soap_s2base64(soap, (const unsigned char*)(soap->tmpbuf + 262), soap->tmpbuf + 6, (int)strlen(soap->tmpbuf + 262)); if ((err = soap->fposthdr(soap, "Proxy-Authorization", soap->tmpbuf))) return err; } +#ifdef WITH_NTLM + } +#endif #endif #ifdef WITH_COOKIES #ifdef WITH_OPENSSL @@ -5742,7 +5981,12 @@ http_post(struct soap *soap, const char *endpoint, const char *host, int port, c #endif #endif if (action && soap->status != SOAP_GET && soap->status != SOAP_DEL) - { sprintf(soap->tmpbuf, "\"%s\"", action && strlen(action) < sizeof(soap->tmpbuf) - 3 ? action : SOAP_STR_EOS); + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "\"%s\"", action); +#else + sprintf(soap->tmpbuf, "\"%s\"", strlen(action) < sizeof(soap->tmpbuf) - 3 ? action : SOAP_STR_EOS); +#endif if ((err = soap->fposthdr(soap, "SOAPAction", soap->tmpbuf))) return err; } @@ -5792,68 +6036,69 @@ http_post_header(struct soap *soap, const char *key, const char *val) static int http_response(struct soap *soap, int status, size_t count) { register int err; + char http[10]; + int code = status; + const char *line; #ifdef WMW_RPM_IO if (soap->rpmreqid) httpOutputEnable(soap->rpmreqid); #endif - if (strlen(soap->http_version) > 4) + if (!soap->http_version || strlen(soap->http_version) > 4) return soap->error = SOAP_EOM; - if (!status || status == SOAP_HTML || status == SOAP_FILE) - { const char *s; - if (count || ((soap->omode & SOAP_IO) == SOAP_IO_CHUNK)) - s = "200 OK"; - else - s = "202 Accepted"; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Status = %s\n", s)); #ifdef WMW_RPM_IO - if (soap->rpmreqid || soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) /* RPM behaves as if standalone */ + if (soap->rpmreqid || soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) /* RPM behaves as if standalone */ #else - if (soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) /* standalone application (socket) or CGI (stdin/out)? */ + if (soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) /* standalone application (socket) or CGI (stdin/out)? */ #endif - { sprintf(soap->tmpbuf, "HTTP/%s %s", soap->http_version, s); - if ((err = soap->fposthdr(soap, soap->tmpbuf, NULL))) - return err; - } - else if ((err = soap->fposthdr(soap, "Status", s))) /* CGI header */ - return err; - } - else if (status >= 200 && status < 600) - { sprintf(soap->tmpbuf, "HTTP/%s %d %s", soap->http_version, status, http_error(soap, status)); - if ((err = soap->fposthdr(soap, soap->tmpbuf, NULL))) - return err; -#ifndef WITH_LEAN - if (status == 401) - { sprintf(soap->tmpbuf, "Basic realm=\"%s\"", (soap->authrealm && strlen(soap->authrealm) < sizeof(soap->tmpbuf) - 14) ? soap->authrealm : "gSOAP Web Service"); - if ((err = soap->fposthdr(soap, "WWW-Authenticate", soap->tmpbuf))) - return err; - } - else if ((status >= 301 && status <= 303) || status == 307) - { if ((err = soap->fposthdr(soap, "Location", soap->endpoint))) - return err; - } + { +#ifdef HAVE_SNPRINTF + soap_snprintf(http, sizeof(http), "HTTP/%s", soap->http_version); +#else + sprintf(http, "HTTP/%s", soap->http_version); #endif } else + strcpy(http, "Status:"); + if (!status || status == SOAP_HTML || status == SOAP_FILE) + { if (count || ((soap->omode & SOAP_IO) == SOAP_IO_CHUNK)) + code = 200; + else + code = 202; + } + else if (status < 200 || status >= 600) { const char *s = *soap_faultcode(soap); if (status >= SOAP_GET_METHOD && status <= SOAP_HTTP_METHOD) - s = "405 Method Not Allowed"; + code = 405; else if (soap->version == 2 && (!s || !strcmp(s, "SOAP-ENV:Sender"))) - s = "400 Bad Request"; + code = 400; else - s = "500 Internal Server Error"; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error %s (status=%d)\n", s, status)); -#ifdef WMW_RPM_IO - if (soap->rpmreqid || soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) /* RPM behaves as if standalone */ + code = 500; + } + line = http_error(soap, code); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "HTTP Status = %d %s\n", code, line)); +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "%s %d %s", http, code, line); #else - if (soap_valid_socket(soap->master) || soap_valid_socket(soap->socket)) /* standalone application */ + sprintf(soap->tmpbuf, "%s %d %s", http, code, line); #endif - { sprintf(soap->tmpbuf, "HTTP/%s %s", soap->http_version, s); - if ((err = soap->fposthdr(soap, soap->tmpbuf, NULL))) - return err; - } - else if ((err = soap->fposthdr(soap, "Status", s))) /* CGI */ + if ((err = soap->fposthdr(soap, soap->tmpbuf, NULL))) + return err; +#ifndef WITH_LEAN + if (status == 401) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "Basic realm=\"%s\"", (soap->authrealm && strlen(soap->authrealm) < sizeof(soap->tmpbuf) - 14) ? soap->authrealm : "gSOAP Web Service"); +#else + sprintf(soap->tmpbuf, "Basic realm=\"%s\"", (soap->authrealm && strlen(soap->authrealm) < sizeof(soap->tmpbuf) - 14) ? soap->authrealm : "gSOAP Web Service"); +#endif + if ((err = soap->fposthdr(soap, "WWW-Authenticate", soap->tmpbuf))) + return err; + } + else if ((status >= 301 && status <= 303) || status == 307) + { if ((err = soap->fposthdr(soap, "Location", soap->endpoint))) return err; } +#endif if ((err = soap->fposthdr(soap, "Server", "gSOAP/2.8")) || (err = soap_puthttphdr(soap, status, count))) return err; @@ -5901,18 +6146,26 @@ soap_response(struct soap *soap, int status) } #endif -/******************************************************************************\ - * - * HTTP Cookies - * -\******************************************************************************/ +/******************************************************************************/ +#ifndef PALM_1 +SOAP_FMAC1 +const char* +SOAP_FMAC2 +soap_url(struct soap *soap, const char *s, const char *t) +{ if (!t || (*t != '/' && *t != '?') || strlen(s) + strlen(t) >= sizeof(soap->msgbuf)) + return s; + strcpy(soap->msgbuf, s); + strcat(soap->msgbuf, t); + return soap->msgbuf; +} +#endif -#ifdef WITH_COOKIES /******************************************************************************/ +#ifndef PALM_1 SOAP_FMAC1 size_t SOAP_FMAC2 -soap_encode_cookie(const char *s, char *t, size_t len) +soap_encode_url(const char *s, char *t, size_t len) { register int c; register size_t n = len; while ((c = *s++) && --n > 0) @@ -5931,8 +6184,34 @@ soap_encode_cookie(const char *s, char *t, size_t len) *t = '\0'; return len - n; } +#endif /******************************************************************************/ +#ifndef PALM_1 +SOAP_FMAC1 +const char* +SOAP_FMAC2 +soap_encode_url_string(struct soap *soap, const char *s) +{ if (s) + { size_t n = 3*strlen(s)+1; + char *t = (char*)soap_malloc(soap, n); + if (t) + { soap_encode_url(s, t, n); + return t; + } + } + return SOAP_STR_EOS; +} +#endif + +/******************************************************************************\ + * + * HTTP Cookies + * +\******************************************************************************/ + +#ifdef WITH_COOKIES +/******************************************************************************/ SOAP_FMAC1 struct soap_cookie* SOAP_FMAC2 @@ -5946,9 +6225,9 @@ soap_cookie(struct soap *soap, const char *name, const char *domain, const char path = SOAP_STR_EOS; else if (*path == '/') path++; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Search cookie %s domain=%s path=%s\n", name, domain ? domain : "(null)", path ? path : "(null)")); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Search cookie='%s' domain='%s' path='%s'\n", name, domain ? domain : "(null)", path ? path : "(null)")); for (p = soap->cookies; p; p = p->next) - { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Cookie in database: %s=%s domain=%s path=%s env=%hd\n", p->name, p->value ? p->value : "(null)", p->domain ? p->domain : "(null)", p->path ? p->path : "(null)", p->env)); + { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Cookie in database: %s='%s' domain='%s' path='%s' env=%hd\n", p->name, p->value ? p->value : "(null)", p->domain ? p->domain : "(null)", p->path ? p->path : "(null)", p->env)); if (!strcmp(p->name, name) && p->domain && p->path @@ -5975,7 +6254,7 @@ soap_set_cookie(struct soap *soap, const char *name, const char *value, const ch else if (*path == '/') path++; q = soap_cookie(soap, name, domain, path); - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Set %scookie: %s=%s domain=%s path=%s\n", q ? SOAP_STR_EOS : "new ", name, value ? value : "(null)", domain ? domain : "(null)", path ? path : "(null)")); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Set %scookie: %s='%s' domain='%s' path='%s'\n", q ? SOAP_STR_EOS : "new ", name, value ? value : "(null)", domain ? domain : "(null)", path ? path : "(null)")); if (!q) { if ((q = (struct soap_cookie*)SOAP_MALLOC(soap, sizeof(struct soap_cookie)))) { if ((q->name = (char*)SOAP_MALLOC(soap, strlen(name)+1))) @@ -6044,13 +6323,13 @@ soap_clr_cookie(struct soap *soap, const char *name, const char *domain, const c if (!domain) domain = soap->cookie_domain; if (!domain) - { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error in clear cookie %s: cookie domain not set\n", name ? name : "(null)")); + { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error in clear cookie='%s': cookie domain not set\n", name ? name : "(null)")); return; } if (!path) path = soap->cookie_path; if (!path) - { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error in clear cookie %s: cookie path not set\n", name ? name : "(null)")); + { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error in clear cookie='%s': cookie path not set\n", name ? name : "(null)")); return; } if (*path == '/') @@ -6110,7 +6389,7 @@ int SOAP_FMAC2 soap_set_cookie_expire(struct soap *soap, const char *name, long expire, const char *domain, const char *path) { struct soap_cookie *p; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Set cookie expiration max-age %ld: %s domain=%s path=%s\n", expire, name, domain ? domain : "(null)", path ? path : "(null)")); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Set cookie expiration max-age=%ld: cookie='%s' domain='%s' path='%s'\n", expire, name, domain ? domain : "(null)", path ? path : "(null)")); if ((p = soap_cookie(soap, name, domain, path))) { p->maxage = expire; p->modified = 1; @@ -6164,10 +6443,10 @@ soap_putsetcookies(struct soap *soap) ) { s = tmp; if (p->name) - s += soap_encode_cookie(p->name, s, tmp-s+4064); + s += soap_encode_url(p->name, s, tmp-s+4064); if (p->value && *p->value) { *s++ = '='; - s += soap_encode_cookie(p->value, s, tmp-s+4064); + s += soap_encode_url(p->value, s, tmp-s+4064); } if (p->domain && (int)strlen(p->domain) < tmp-s+4064) { strcpy(s, ";Domain="); @@ -6192,15 +6471,25 @@ soap_putsetcookies(struct soap *soap) s += strlen(s); } else - s += soap_encode_cookie(t, s, tmp-s+4064); + s += soap_encode_url(t, s, tmp-s+4064); } } if (p->version > 0 && s-tmp < 4060) - { sprintf(s, ";Version=%u", p->version); + { +#ifdef HAVE_SNPRINTF + soap_snprintf(s, 4096 - (s-tmp), ";Version=%u", p->version); +#else + sprintf(s, ";Version=%u", p->version); +#endif s += strlen(s); } if (p->maxage >= 0 && s-tmp < 4060) - { sprintf(s, ";Max-Age=%ld", p->maxage); + { +#ifdef HAVE_SNPRINTF + soap_snprintf(s, 4096 - (s-tmp), ";Max-Age=%ld", p->maxage); +#else + sprintf(s, ";Max-Age=%ld", p->maxage); +#endif s += strlen(s); } if (s-tmp < 4073 @@ -6231,7 +6520,7 @@ soap_putcookies(struct soap *soap, const char *domain, const char *path, int sec return SOAP_OK; s = tmp; p = &soap->cookies; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Sending cookies for domain=%s path=%s\n", domain, path)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Sending cookies for domain='%s' path='%s'\n", domain, path)); if (*path == '/') path++; while ((q = *p)) @@ -6277,45 +6566,60 @@ soap_putcookies(struct soap *soap, const char *domain, const char *path, int sec && (!q->path || !strncmp(q->path, path, strlen(q->path))) && (!q->secure || secure)) { size_t n = 12; - if (q->name) - n += 3*strlen(q->name); - if (q->value && *q->value) - n += 3*strlen(q->value) + 1; + if (q->name) + n += 3*strlen(q->name); + if (q->value && *q->value) + n += 3*strlen(q->value) + 1; if (q->path && *q->path) - n += strlen(q->path) + 9; - if (q->domain) - n += strlen(q->domain) + 11; - if (tmp - s + n > sizeof(tmp)) + n += strlen(q->path) + 9; + if (q->domain) + n += strlen(q->domain) + 11; + if (tmp - s + n > sizeof(tmp)) { if (s == tmp) - return SOAP_OK; /* HTTP header size overflow */ - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Cookie: %s\n", tmp)); + return SOAP_OK; /* HTTP header size overflow */ + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Cookie: %s\n", tmp)); if ((soap->error = soap->fposthdr(soap, "Cookie", tmp))) return soap->error; - s = tmp; - } - else if (s != tmp) - { strcat(s, " "); - s++; - } - if (q->version != version) - { sprintf(s, "$Version=%u;", q->version); + s = tmp; + } + else if (s != tmp) + { strcat(s, " "); + s++; + } + if (q->version != version && s-tmp < 4060) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(s, 4096 - (s-tmp), "$Version=%u;", q->version); +#else + sprintf(s, "$Version=%u;", q->version); +#endif version = q->version; s += strlen(s); } if (q->name) - s += soap_encode_cookie(q->name, s, tmp+sizeof(tmp)-s-16); + s += soap_encode_url(q->name, s, tmp+sizeof(tmp)-s-16); if (q->value && *q->value) { *s++ = '='; - s += soap_encode_cookie(q->value, s, tmp+sizeof(tmp)-s-16); + s += soap_encode_url(q->value, s, tmp+sizeof(tmp)-s-16); } - if (q->path) - { sprintf(s, ";$Path=\"/%s\"", (*q->path == '/' ? q->path + 1 : q->path)); + if (q->path && (s-tmp) + strlen(q->path) < 4060) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(s, 4096 - (s-tmp), ";$Path=\"/%s\"", (*q->path == '/' ? q->path + 1 : q->path)); +#else + sprintf(s, ";$Path=\"/%s\"", (*q->path == '/' ? q->path + 1 : q->path)); +#endif s += strlen(s); } - if (q->domain) - { sprintf(s, ";$Domain=\"%s\"", q->domain); + if (q->domain && (s-tmp) + strlen(q->domain) < 4060) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(s, 4096 - (s-tmp), ";$Domain=\"%s\"", q->domain); +#else + sprintf(s, ";$Domain=\"%s\"", q->domain); +#endif s += strlen(s); - } + } } p = &q->next; } @@ -6452,7 +6756,7 @@ soap_getcookies(struct soap *soap, const char *val) p->secure = 1; else { if (p) - { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Got environment cookie %s=%s domain=%s path=%s expire=%ld secure=%d\n", p->name, p->value ? p->value : "(null)", p->domain ? p->domain : "(null)", p->path ? p->path : "(null)", p->expire, p->secure)); + { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Got environment cookie='%s' value='%s' domain='%s' path='%s' expire=%ld secure=%d\n", p->name, p->value ? p->value : "(null)", p->domain ? p->domain : "(null)", p->path ? p->path : "(null)", p->expire, p->secure)); if ((q = soap_set_cookie(soap, p->name, p->value, p->domain, p->path))) { q->version = p->version; q->expire = p->expire; @@ -6504,7 +6808,7 @@ soap_getcookies(struct soap *soap, const char *val) } } if (p) - { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Got environment cookie %s=%s domain=%s path=%s expire=%ld secure=%d\n", p->name, p->value ? p->value : "(null)", p->domain ? p->domain : "(null)", p->path ? p->path : "(null)", p->expire, p->secure)); + { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Got environment cookie='%s' value='%s' domain='%s' path='%s' expire=%ld secure=%d\n", p->name, p->value ? p->value : "(null)", p->domain ? p->domain : "(null)", p->path ? p->path : "(null)", p->expire, p->secure)); if ((q = soap_set_cookie(soap, p->name, p->value, p->domain, p->path))) { q->version = p->version; q->expire = p->expire; @@ -6694,6 +6998,7 @@ SOAP_FMAC2 soap_embed(struct soap *soap, const void *p, const struct soap_array *a, int n, const char *tag, int type) { register int i; struct soap_plist *pp; + (void)soap; if (soap->version == 2) soap->encoding = 1; if (a) @@ -6707,7 +7012,6 @@ soap_embed(struct soap *soap, const void *p, const struct soap_array *a, int n, soap_set_embedded(soap, pp); } return i; - (void)soap; } #endif #endif @@ -6745,6 +7049,7 @@ SOAP_FMAC2 soap_pointer_enter(struct soap *soap, const void *p, const struct soap_array *a, int n, int type, struct soap_plist **ppp) { register size_t h; register struct soap_plist *pp; + (void)n; if (!soap->pblk || soap->pidx >= SOAP_PTRBLK) { register struct soap_pblk *pb = (struct soap_pblk*)SOAP_MALLOC(soap, sizeof(struct soap_pblk)); if (!pb) @@ -6770,7 +7075,6 @@ soap_pointer_enter(struct soap *soap, const void *p, const struct soap_array *a, soap->pht[h] = pp; pp->id = ++soap->idnum; return pp->id; - (void)n; } #endif #endif @@ -6840,8 +7144,6 @@ soap_begin_count(struct soap *soap) soap->mode |= SOAP_IO_STORE; } #endif - if (!soap->encodingStyle && !(soap->mode & SOAP_XML_GRAPH)) - soap->mode |= SOAP_XML_TREE; #ifndef WITH_LEANER if ((soap->mode & SOAP_ENC_MTOM) && (soap->mode & SOAP_ENC_DIME)) soap->mode |= SOAP_ENC_MIME; @@ -6863,13 +7165,13 @@ soap_begin_count(struct soap *soap) soap->idnum = 0; soap_clr_attr(soap); soap_set_local_namespaces(soap); - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Begin count phase (socket=%d mode=0x%x count=%lu)\n", soap->socket, (unsigned int)soap->mode, (unsigned long)soap->count)); #ifndef WITH_LEANER soap->dime.count = 0; /* count # of attachments */ soap->dime.size = 0; /* accumulate total size of attachments */ if (soap->fprepareinitsend && (soap->mode & SOAP_IO) != SOAP_IO_STORE && (soap->error = soap->fprepareinitsend(soap))) return soap->error; #endif + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Begin count phase (socket=%d mode=0x%x count=%lu)\n", soap->socket, (unsigned int)soap->mode, (unsigned long)soap->count)); return SOAP_OK; } #endif @@ -6897,7 +7199,7 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_begin_send(struct soap *soap) -{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Initializing for output\n")); +{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Initializing for output to socket=%d/fd=%d\n", soap->socket, soap->sendfd)); soap_free_ns(soap); soap->error = SOAP_OK; soap->mode = soap->omode | (soap->mode & (SOAP_IO_LENGTH | SOAP_ENC_DIME)); @@ -6928,8 +7230,6 @@ soap_begin_send(struct soap *soap) return soap->error; if (!(soap->mode & SOAP_IO_KEEPALIVE)) soap->keep_alive = 0; - if (!soap->encodingStyle && !(soap->mode & SOAP_XML_GRAPH)) - soap->mode |= SOAP_XML_TREE; #ifndef WITH_LEANER if ((soap->mode & SOAP_ENC_MTOM) && (soap->mode & SOAP_ENC_DIME)) { soap->mode |= SOAP_ENC_MIME; @@ -7036,7 +7336,7 @@ int SOAP_FMAC2 soap_reference(struct soap *soap, const void *p, int t) { struct soap_plist *pp; - if (!p || (soap->mode & SOAP_XML_TREE)) + if (!p || (!soap->encodingStyle && !(soap->omode & (SOAP_ENC_DIME|SOAP_ENC_MIME|SOAP_ENC_MTOM|SOAP_XML_GRAPH))) || (soap->omode & SOAP_XML_TREE)) return 1; if (soap_pointer_lookup(soap, p, t, &pp)) { if (pp->mark1 == 0) @@ -7044,11 +7344,7 @@ soap_reference(struct soap *soap, const void *p, int t) pp->mark2 = 2; } } - else if (soap_pointer_enter(soap, p, NULL, 0, t, &pp)) - { pp->mark1 = 0; - pp->mark2 = 0; - } - else + else if (!soap_pointer_enter(soap, p, NULL, 0, t, &pp)) return 1; DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Reference %p type=%d (%d %d)\n", p, t, (int)pp->mark1, (int)pp->mark2)); return pp->mark1; @@ -7063,12 +7359,10 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_array_reference(struct soap *soap, const void *p, const struct soap_array *a, int n, int t) -{ register int i; - struct soap_plist *pp; - if (!p || !a->__ptr) +{ struct soap_plist *pp; + if (!p || !a->__ptr || (!soap->encodingStyle && !(soap->omode & (SOAP_ENC_DIME|SOAP_ENC_MIME|SOAP_ENC_MTOM|SOAP_XML_GRAPH))) || (soap->omode & SOAP_XML_TREE)) return 1; - i = soap_array_pointer_lookup(soap, p, a, n, t, &pp); - if (i) + if (soap_array_pointer_lookup(soap, p, a, n, t, &pp)) { if (pp->mark1 == 0) { pp->mark1 = 2; pp->mark2 = 2; @@ -7076,10 +7370,6 @@ soap_array_reference(struct soap *soap, const void *p, const struct soap_array * } else if (!soap_pointer_enter(soap, p, a, n, t, &pp)) return 1; - else - { pp->mark1 = 0; - pp->mark2 = 0; - } DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Array reference %p ptr=%p dim=%d type=%d (%d %d)\n", p, a->__ptr, n, t, (int)pp->mark1, (int)pp->mark2)); return pp->mark1; } @@ -7094,10 +7384,10 @@ int SOAP_FMAC2 soap_embedded_id(struct soap *soap, int id, const void *p, int t) { struct soap_plist *pp = NULL; - if (soap->mode & SOAP_XML_TREE) + if (!id || (!soap->encodingStyle && !(soap->omode & SOAP_XML_GRAPH)) || (soap->omode & SOAP_XML_TREE)) return id; DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Embedded_id %p type=%d id=%d\n", p, t, id)); - if (soap->version == 1 && soap->encodingStyle && !(soap->mode & SOAP_XML_GRAPH) && soap->part != SOAP_IN_HEADER) + if (soap->version == 1 && soap->part != SOAP_IN_HEADER) { if (id < 0) { id = soap_pointer_lookup(soap, p, t, &pp); if (id) @@ -7209,7 +7499,12 @@ soap_attachment(struct soap *soap, const char *tag, int id, const void *p, const if (id <= 0) id = i; if (!aid) - { sprintf(soap->tmpbuf, soap->dime_id_format, id); + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), soap->dime_id_format, id); +#else + sprintf(soap->tmpbuf, soap->dime_id_format, id); +#endif aid = soap_strdup(soap, soap->tmpbuf); } /* Add MTOM xop:Include element when necessary */ @@ -7325,8 +7620,8 @@ soap_enter(struct soap *soap, const char *id) register struct soap_ilist *ip; ip = (struct soap_ilist*)SOAP_MALLOC(soap, sizeof(struct soap_ilist) + strlen(id)); if (ip) - { h = soap_hash(id); - strcpy((char*)ip->id, id); + { strcpy((char*)ip->id, id); + h = soap_hash(id); /* h = (HASH(id) % SOAP_IDHASH) so soap->iht[h] is safe */ ip->next = soap->iht[h]; soap->iht[h] = ip; } @@ -7619,6 +7914,22 @@ soap_delegate_deletion(struct soap *soap, struct soap *soap_to) *q = (char*)soap_to->alist; soap_to->alist = soap->alist; soap->alist = NULL; +#ifdef SOAP_MEM_DEBUG + cp = soap->clist; + while (cp) + { h = soap_hash_ptr(cp); + for (mp = &soap->mht[h]; *mp; mp = &(*mp)->next) + { if ((*mp)->ptr == cp) + { mq = *mp; + *mp = mq->next; + mq->next = soap_to->mht[h]; + soap_to->mht[h] = mq; + break; + } + } + cp = cp->next; + } +#endif cp = soap_to->clist; if (cp) { while (cp->next) @@ -7950,7 +8261,17 @@ soap_end_send(struct soap *soap) if (err) return err; #endif - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "End send mode=0x%x\n", soap->mode)); + return soap_end_send_flush(soap); +} +#endif + +/******************************************************************************/ +#ifndef PALM_1 +SOAP_FMAC1 +int +SOAP_FMAC2 +soap_end_send_flush(struct soap *soap) +{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "End send mode=0x%x\n", soap->mode)); if (soap->mode & SOAP_IO) /* need to flush the remaining data in buffer */ { if (soap_flush(soap)) #ifdef WITH_ZLIB @@ -8022,16 +8343,20 @@ soap_end_send(struct soap *soap) #endif for (p = soap_first_block(soap, NULL); p; p = soap_next_block(soap, NULL)) { DBGMSG(SENT, p, soap_block_size(soap, NULL)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Send %u bytes to socket=%d/fd=%d\n", (unsigned int)soap_block_size(soap, NULL), soap->socket, soap->sendfd)); if ((soap->error = soap->fsend(soap, p, soap_block_size(soap, NULL)))) { soap_end_block(soap, NULL); return soap->error; } } soap_end_block(soap, NULL); + if (soap->fpreparefinalsend && (soap->error = soap->fpreparefinalsend(soap))) + return soap->error; } #ifndef WITH_LEANER else if ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK) { DBGMSG(SENT, "\r\n0\r\n\r\n", 7); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Send 7 bytes to socket=%d/fd=%d\n", soap->socket, soap->sendfd)); if ((soap->error = soap->fsend(soap, "\r\n0\r\n\r\n", 7))) return soap->error; } @@ -8193,13 +8518,9 @@ soap_free_temp(struct soap *soap) if (ns) { for (; ns->id; ns++) { if (ns->out) - { if (soap->encodingStyle == ns->out) - soap->encodingStyle = SOAP_STR_EOS; - SOAP_FREE(soap, ns->out); + { SOAP_FREE(soap, ns->out); ns->out = NULL; } - if (soap->encodingStyle == ns->ns) - soap->encodingStyle = SOAP_STR_EOS; } SOAP_FREE(soap, soap->local_namespaces); soap->local_namespaces = NULL; @@ -8299,34 +8620,37 @@ soap_set_logfile(struct soap *soap, int i, const char *logfile) #endif /******************************************************************************/ -#ifdef SOAP_DEBUG SOAP_FMAC1 void SOAP_FMAC2 soap_set_recv_logfile(struct soap *soap, const char *logfile) -{ soap_set_logfile(soap, SOAP_INDEX_RECV, logfile); -} +{ +#ifdef SOAP_DEBUG + soap_set_logfile(soap, SOAP_INDEX_RECV, logfile); #endif +} /******************************************************************************/ -#ifdef SOAP_DEBUG SOAP_FMAC1 void SOAP_FMAC2 soap_set_sent_logfile(struct soap *soap, const char *logfile) -{ soap_set_logfile(soap, SOAP_INDEX_SENT, logfile); -} +{ +#ifdef SOAP_DEBUG + soap_set_logfile(soap, SOAP_INDEX_SENT, logfile); #endif +} /******************************************************************************/ -#ifdef SOAP_DEBUG SOAP_FMAC1 void SOAP_FMAC2 soap_set_test_logfile(struct soap *soap, const char *logfile) -{ soap_set_logfile(soap, SOAP_INDEX_TEST, logfile); -} +{ +#ifdef SOAP_DEBUG + soap_set_logfile(soap, SOAP_INDEX_TEST, logfile); #endif +} /******************************************************************************/ #ifndef PALM_1 @@ -8382,12 +8706,10 @@ soap_copy_context(struct soap *copy, const struct soap *soap) soap_set_sent_logfile(copy, soap->logfile[SOAP_INDEX_SENT]); soap_set_recv_logfile(copy, soap->logfile[SOAP_INDEX_RECV]); #endif - copy->namespaces = NULL; + copy->namespaces = soap->local_namespaces; copy->local_namespaces = NULL; - if (soap->local_namespaces) - soap_set_namespaces(copy, soap->local_namespaces); - else - soap_set_namespaces(copy, soap->namespaces); + soap_set_local_namespaces(copy); /* copy content of soap->local_namespaces */ + copy->namespaces = soap->namespaces; /* point to shared read-only namespaces table */ #ifdef WITH_C_LOCALE # ifdef WIN32 copy->c_locale = _create_locale(LC_ALL, "C"); @@ -8455,6 +8777,7 @@ soap_copy_stream(struct soap *copy, struct soap *soap) { struct soap_attribute *tp = NULL, *tq; if (copy == soap) return; + copy->header = soap->header; copy->mode = soap->mode; copy->imode = soap->imode; copy->omode = soap->omode; @@ -8524,6 +8847,7 @@ soap_copy_stream(struct soap *copy, struct soap *soap) /* copy XML parser state */ soap_free_ns(copy); soap_set_local_namespaces(copy); + copy->version = soap->version; if (soap->nlist && soap->local_namespaces) { register struct soap_nlist *np = NULL, *nq; /* copy reversed nlist */ @@ -8616,6 +8940,16 @@ soap_free_stream(struct soap *soap) SOAP_FMAC1 void SOAP_FMAC2 +soap_initialize(struct soap *soap) +{ soap_versioning(soap_init)(soap, SOAP_IO_DEFAULT, SOAP_IO_DEFAULT); +} +#endif + +/******************************************************************************/ +#ifndef PALM_1 +SOAP_FMAC1 +void +SOAP_FMAC2 soap_versioning(soap_init)(struct soap *soap, soap_mode imode, soap_mode omode) { size_t i; soap->state = SOAP_INIT; @@ -8667,8 +9001,8 @@ soap_versioning(soap_init)(struct soap *soap, soap_mode imode, soap_mode omode) soap->fconnect = NULL; soap->fdisconnect = NULL; #ifndef WITH_NOIO - soap->ipv6_multicast_if = 0; - soap->ipv4_multicast_if = NULL; + soap->ipv6_multicast_if = 0; /* in_addr_t value */ + soap->ipv4_multicast_if = NULL; /* points to struct in_addr or in_addr_t */ soap->ipv4_multicast_ttl = 0; /* 0: use default */ #ifndef WITH_IPV6 soap->fresolve = tcp_gethost; @@ -8746,7 +9080,7 @@ soap_versioning(soap_init)(struct soap *soap, soap_mode imode, soap_mode omode) soap->labbuf = NULL; soap->lablen = 0; soap->labidx = 0; - soap->encodingStyle = SOAP_STR_EOS; + soap->encodingStyle = NULL; #ifndef WITH_NONAMESPACES soap->namespaces = namespaces; #else @@ -8835,6 +9169,7 @@ soap_versioning(soap_init)(struct soap *soap, soap_mode imode, soap_mode omode) soap->session = NULL; soap->ssl_flags = SOAP_SSL_DEFAULT; soap->keyfile = NULL; + soap->keyid = NULL; soap->password = NULL; soap->cafile = NULL; soap->capath = NULL; @@ -8853,6 +9188,7 @@ soap_versioning(soap_init)(struct soap *soap, soap_mode imode, soap_mode omode) soap->session = NULL; soap->ssl_flags = SOAP_SSL_DEFAULT; soap->keyfile = NULL; + soap->keyid = NULL; soap->password = NULL; soap->cafile = NULL; soap->capath = NULL; @@ -8926,6 +9262,7 @@ soap_begin(struct soap *soap) soap->idnum = 0; soap->level = 0; soap->endpoint[0] = '\0'; + soap->encodingStyle = SOAP_STR_EOS; #ifndef WITH_LEANER soap->dime.chunksize = 0; soap->dime.buflen = 0; @@ -8962,6 +9299,27 @@ soap_end(struct soap *soap) /******************************************************************************/ #ifndef PALM_1 SOAP_FMAC1 +void +SOAP_FMAC2 +soap_set_version(struct soap *soap, short version) +{ soap_set_local_namespaces(soap); + if (soap->version != version) + { if (version == 1) + { soap->local_namespaces[0].ns = soap_env1; + soap->local_namespaces[1].ns = soap_enc1; + } + else if (version == 2) + { soap->local_namespaces[0].ns = soap_env2; + soap->local_namespaces[1].ns = soap_enc2; + } + soap->version = version; + } +} +#endif + +/******************************************************************************/ +#ifndef PALM_1 +SOAP_FMAC1 int SOAP_FMAC2 soap_set_namespaces(struct soap *soap, const struct Namespace *p) @@ -9034,7 +9392,7 @@ soap_set_local_namespaces(struct soap *soap) if (ns2[0].ns) { if (!strcmp(ns2[0].ns, soap_env1)) soap->version = 1; - else + else if (!strcmp(ns2[0].ns, soap_env2)) soap->version = 2; } soap->local_namespaces = ns2; @@ -9145,11 +9503,14 @@ soap_utilize_ns(struct soap *soap, const char *tag) size_t n = 0; const char *t = strchr(tag, ':'); if (t) - n = t - tag; + { n = t - tag; + if (n >= sizeof(soap->tmpbuf)) + n = sizeof(soap->tmpbuf) - 1; + } np = soap_lookup_ns(soap, tag, n); DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Utilizing namespace of '%s'\n", tag)); if (np) - { if (np->index == 0) + { if (np->index <= 0) soap_push_ns(soap, np->id, np->ns, 1); } else if (strncmp(tag, "xml", 3)) @@ -9191,8 +9552,8 @@ soap_element(struct soap *soap, const char *tag, int id, const char *type) for (np = soap->nlist; np; np = np->next) { if (np->index == 2) { struct soap_nlist *np1 = soap_push_ns(soap, np->id, np->ns, 1); - if (np1) - np1->index = 0; + if (np1) + np1->index = 0; } } soap->evlev = soap->level; @@ -9268,17 +9629,34 @@ soap_element(struct soap *soap, const char *tag, int id, const char *type) } #endif if (!soap->ns) - { struct Namespace *ns; + { struct Namespace *ns = soap->local_namespaces; int k = -1; + if (ns) + { #ifndef WITH_LEAN - if ((soap->mode & SOAP_XML_DEFAULTNS)) - k = 4; /* only produce the first four required entries */ + if ((soap->mode & SOAP_XML_DEFAULTNS)) + { if (soap->version) + k = 4; /* first four required entries */ + else if (!(soap->mode & SOAP_XML_NOTYPE) || (soap->mode & SOAP_XML_NIL)) + { ns += 2; + k = 2; /* next two entries */ + } + else + k = 0; /* no entries */ + } #endif - for (ns = soap->local_namespaces; ns && ns->id && k; ns++, k--) - { if (*ns->id && (ns->out || ns->ns)) - { sprintf(soap->tmpbuf, "xmlns:%s", ns->id); - if (soap_attribute(soap, soap->tmpbuf, ns->out ? ns->out : ns->ns)) - return soap->error; + while (k-- && ns->id) + { if (*ns->id && (ns->out || ns->ns)) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "xmlns:%s", ns->id); +#else + sprintf(soap->tmpbuf, "xmlns:%s", ns->id); +#endif + if (soap_attribute(soap, soap->tmpbuf, ns->out ? ns->out : ns->ns)) + return soap->error; + } + ns++; } } } @@ -9288,8 +9666,17 @@ soap_element(struct soap *soap, const char *tag, int id, const char *type) soap_utilize_ns(soap, tag); #endif if (id > 0) - { sprintf(soap->tmpbuf, "_%d", id); - if (soap_attribute(soap, "id", soap->tmpbuf)) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "_%d", id); +#else + sprintf(soap->tmpbuf, "_%d", id); +#endif + if (soap->version == 2) + { if (soap_attribute(soap, "SOAP-ENC:id", soap->tmpbuf)) + return soap->error; + } + else if (soap_attribute(soap, "id", soap->tmpbuf)) return soap->error; } if (type && *type && !(soap->mode & SOAP_XML_NOTYPE) && soap->part != SOAP_IN_HEADER) @@ -9312,9 +9699,20 @@ soap_element(struct soap *soap, const char *tag, int id, const char *type) } if (soap->null && soap->position > 0) { register int i; +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf) - 1, "[%d", soap->positions[0]); +#else sprintf(soap->tmpbuf, "[%d", soap->positions[0]); +#endif for (i = 1; i < soap->position; i++) - sprintf(soap->tmpbuf + strlen(soap->tmpbuf), ",%d", soap->positions[i]); + { register size_t l = strlen(soap->tmpbuf); +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf + l, sizeof(soap->tmpbuf)-l-1, ",%d", soap->positions[i]); +#else + if (l + 13 < sizeof(soap->tmpbuf)) + sprintf(soap->tmpbuf + l, ",%d", soap->positions[i]); +#endif + } strcat(soap->tmpbuf, "]"); if (soap_attribute(soap, "SOAP-ENC:position", soap->tmpbuf)) return soap->error; @@ -9329,7 +9727,7 @@ soap_element(struct soap *soap, const char *tag, int id, const char *type) soap->mustUnderstand = 0; } if (soap->encoding) - { if (soap->encodingStyle && soap->local_namespaces) + { if (soap->encodingStyle && soap->local_namespaces && soap->local_namespaces[0].id && soap->local_namespaces[1].id) { if (!*soap->encodingStyle) { if (soap->local_namespaces[1].out) soap->encodingStyle = soap->local_namespaces[1].out; @@ -9339,6 +9737,8 @@ soap_element(struct soap *soap, const char *tag, int id, const char *type) if (soap->encodingStyle && soap_attribute(soap, "SOAP-ENV:encodingStyle", soap->encodingStyle)) return soap->error; } + else + soap->encodingStyle = NULL; soap->encoding = 0; } soap->null = 0; @@ -9495,13 +9895,19 @@ soap_array_begin_out(struct soap *soap, const char *tag, int id, const char *typ if (soap->version == 2) { const char *s; s = soap_strrchr(type, '['); - if ((size_t)(s - type) < sizeof(soap->tmpbuf)) + if (s && (size_t)(s - type) < sizeof(soap->tmpbuf)) { strncpy(soap->tmpbuf, type, s - type); soap->tmpbuf[s - type] = '\0'; if (soap_attribute(soap, "SOAP-ENC:itemType", soap->tmpbuf)) return soap->error; - if (s && (soap_attribute(soap, "SOAP-ENC:arraySize", s + 1))) - return soap->error; + s++; + if (*s) + { strncpy(soap->tmpbuf, s, sizeof(soap->tmpbuf)); + soap->tmpbuf[sizeof(soap->tmpbuf) - 1] = '\0'; + soap->tmpbuf[strlen(soap->tmpbuf) - 1] = '\0'; + if (soap_attribute(soap, "SOAP-ENC:arraySize", soap->tmpbuf)) + return soap->error; + } } } else @@ -9534,7 +9940,16 @@ soap_element_start_end_out(struct soap *soap, const char *tag) } for (np = soap->nlist; np; np = np->next) { if (np->index == 1 && np->ns) - { sprintf(soap->tmpbuf, *(np->id) ? "xmlns:%s" : "xmlns", np->id); + { if (*(np->id)) + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "xmlns:%s", np->id); +#else + sprintf(soap->tmpbuf, "xmlns:%s", np->id); +#endif + } + else + strcpy(soap->tmpbuf, "xmlns"); DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Enabling utilized binding (level=%u) %s='%s'\n", np->level, soap->tmpbuf, np->ns)); soap_set_attr(soap, soap->tmpbuf, np->ns, 1); np->index = 2; @@ -9658,13 +10073,19 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_element_ref(struct soap *soap, const char *tag, int id, int href) -{ register int n = 0; - const char *s = "href"; - if (soap->version == 2) - { s = "SOAP-ENC:ref"; - n = 1; +{ register const char *s = "ref"; + register int n = 1; + if (soap->version == 1) + { s = "href"; + n = 0; } + else if (soap->version == 2) + s = "SOAP-ENC:ref"; +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->href, sizeof(soap->href), "#_%d", href); +#else sprintf(soap->href, "#_%d", href); +#endif return soap_element_href(soap, tag, id, s, soap->href + n); } #endif @@ -9714,7 +10135,7 @@ int SOAP_FMAC2 soap_element_nil(struct soap *soap, const char *tag) { if (soap_element(soap, tag, -1, NULL) - || soap_attribute(soap, "xsi:nil", "true")) + || ((soap->mode & SOAP_XML_NIL) && soap_attribute(soap, "xsi:nil", "true"))) return soap->error; return soap_element_start_end_out(soap, tag); } @@ -9727,11 +10148,11 @@ int SOAP_FMAC2 soap_element_id(struct soap *soap, const char *tag, int id, const void *p, const struct soap_array *a, int n, const char *type, int t) { if (!p) - { soap_element_null(soap, tag, id, type); + { soap->error = soap_element_null(soap, tag, id, type); return -1; } #ifndef WITH_NOIDREF - if (soap->mode & SOAP_XML_TREE) + if ((!soap->encodingStyle && !(soap->omode & SOAP_XML_GRAPH)) || (soap->omode & SOAP_XML_TREE)) return 0; if (id < 0) { struct soap_plist *pp; @@ -9988,7 +10409,7 @@ soap_set_attr(struct soap *soap, const char *name, const char *value, int flag) if ((soap->mode & SOAP_XML_CANONICAL)) { struct soap_attribute **tpp = &soap->attributes; const char *s = strchr(name, ':'); - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Inserting attribute %s for c14n\n", name)) + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Inserting attribute %s for c14n\n", name)); if (!strncmp(name, "xmlns", 5)) { for (; *tpp; tpp = &(*tpp)->next) if (strncmp((*tpp)->name, "xmlns", 5) || strcmp((*tpp)->name + 5, name + 5) > 0) @@ -10054,7 +10475,7 @@ soap_set_attr(struct soap *soap, const char *name, const char *value, int flag) if (!strcmp(name, "wsu:Id")) { soap->event = SOAP_SEC_BEGIN; strncpy(soap->id, value, sizeof(soap->id)); - soap->id[sizeof(soap->id)-1] = '\0'; + soap->id[sizeof(soap->id) - 1] = '\0'; } #endif } @@ -10139,7 +10560,9 @@ soap_getattrval(struct soap *soap, char *s, size_t n, soap_wchar d) } default: if ((int)c == EOF) + { *s = '\0'; return soap->error = SOAP_CHK_EOF; + } *s++ = (char)c; } } @@ -10169,12 +10592,12 @@ SOAP_FMAC2 soap_append_lab(struct soap *soap, const char *s, size_t n) { if (soap->labidx + n >= soap->lablen) { register char *t = soap->labbuf; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Enlarging look-aside buffer to append data, old size=%lu", (unsigned long)soap->lablen)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Enlarging look-aside buffer to append data, size=%lu\n", (unsigned long)soap->lablen)); if (soap->lablen == 0) soap->lablen = SOAP_LABLEN; while (soap->labidx + n >= soap->lablen) soap->lablen <<= 1; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, ", new size=%lu\n", (unsigned long)soap->lablen)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "New look-aside buffer size=%lu\n", (unsigned long)soap->lablen)); soap->labbuf = (char*)SOAP_MALLOC(soap, soap->lablen); if (!soap->labbuf) { if (t) @@ -10271,7 +10694,7 @@ soap_peek_element(struct soap *soap) { *soap->tag = '\0'; if ((int)c == EOF) return soap->error = SOAP_CHK_EOF; - soap_unget(soap, c > 0 ? c | 0x80000000 : c); + soap_unget(soap, c); #ifdef WITH_DOM /* whitespace leading to end tag is significant for DOM */ if ((soap->mode & SOAP_XML_DOM) && soap->dom) @@ -10283,18 +10706,18 @@ soap_peek_element(struct soap *soap) #endif return soap->error = SOAP_NO_TAG; } - s = soap->tag; do c = soap_get1(soap); while (soap_blank(c)); + s = soap->tag; i = sizeof(soap->tag); while (c != '>' && c != '/' && soap_notblank(c) && (int)c != EOF) { if (--i > 0) *s++ = (char)c; c = soap_get1(soap); } + *s = '\0'; while (soap_blank(c)) c = soap_get1(soap); - *s = '\0'; #ifdef WITH_DOM if (soap->mode & SOAP_XML_DOM) { register struct soap_dom_element *elt; @@ -10503,27 +10926,35 @@ soap_peek_element(struct soap *soap) { #ifndef WITH_NOIDREF if (!strcmp(tp->name, "id")) - { if ((soap->version > 0 && !(soap->mode & SOAP_XML_TREE)) + { if ((soap->version > 0 && !(soap->imode & SOAP_XML_TREE)) || (soap->mode & SOAP_XML_GRAPH)) { *soap->id = '#'; strncpy(soap->id + 1, tp->value, sizeof(soap->id) - 2); - soap->id[sizeof(soap->id)-1] = '\0'; + soap->id[sizeof(soap->id) - 1] = '\0'; } } else if (!strcmp(tp->name, "href")) - { if (soap->version == 1 + { if ((soap->version == 1 && !(soap->imode & SOAP_XML_TREE)) || (soap->mode & SOAP_XML_GRAPH) || (soap->mode & SOAP_ENC_MTOM) || (soap->mode & SOAP_ENC_DIME)) { strncpy(soap->href, tp->value, sizeof(soap->href) - 1); - soap->href[sizeof(soap->href)-1] = '\0'; + soap->href[sizeof(soap->href) - 1] = '\0'; + } + } + else if (!strcmp(tp->name, "ref")) + { if ((soap->version == 2 && !(soap->imode & SOAP_XML_TREE)) + || (soap->mode & SOAP_XML_GRAPH)) + { *soap->href = '#'; + strncpy(soap->href + (*tp->value != '#'), tp->value, sizeof(soap->href) - 2); + soap->href[sizeof(soap->href) - 1] = '\0'; } } else #endif if (!soap_match_tag(soap, tp->name, "xsi:type")) { strncpy(soap->type, tp->value, sizeof(soap->type) - 1); - soap->type[sizeof(soap->type)-1] = '\0'; + soap->type[sizeof(soap->type) - 1] = '\0'; } else if ((!soap_match_tag(soap, tp->name, "xsi:null") || !soap_match_tag(soap, tp->name, "xsi:nil")) @@ -10537,12 +10968,12 @@ soap_peek_element(struct soap *soap) if (s && (size_t)(s - tp->value) < sizeof(soap->arrayType)) { strncpy(soap->arrayType, tp->value, s - tp->value); soap->arrayType[s - tp->value] = '\0'; - strncpy(soap->arraySize, s, sizeof(soap->arraySize) - 1); + strncpy(soap->arraySize, s, sizeof(soap->arraySize)); } else - strncpy(soap->arrayType, tp->value, sizeof(soap->arrayType) - 1); - soap->arraySize[sizeof(soap->arrayType)-1] = '\0'; - soap->arrayType[sizeof(soap->arrayType)-1] = '\0'; + strncpy(soap->arrayType, tp->value, sizeof(soap->arrayType)); + soap->arraySize[sizeof(soap->arraySize) - 1] = '\0'; + soap->arrayType[sizeof(soap->arrayType) - 1] = '\0'; } else if (!soap_match_tag(soap, tp->name, "SOAP-ENC:offset")) strncpy(soap->arrayOffset, tp->value, sizeof(soap->arrayOffset)); @@ -10562,11 +10993,15 @@ soap_peek_element(struct soap *soap) else if (soap->version == 2) { #ifndef WITH_NOIDREF - if (!strcmp(tp->name, "ref") - || !soap_match_tag(soap, tp->name, "SOAP-ENC:ref")) + if (!soap_match_tag(soap, tp->name, "SOAP-ENC:id")) + { *soap->id = '#'; + strncpy(soap->id + 1, tp->value, sizeof(soap->id) - 2); + soap->id[sizeof(soap->id) - 1] = '\0'; + } + else if (!soap_match_tag(soap, tp->name, "SOAP-ENC:ref")) { *soap->href = '#'; - strncpy(soap->href + 1, tp->value, sizeof(soap->href) - 2); - soap->href[sizeof(soap->href)-1] = '\0'; + strncpy(soap->href + (*tp->value != '#'), tp->value, sizeof(soap->href) - 2); + soap->href[sizeof(soap->href) - 1] = '\0'; } else #endif @@ -10694,7 +11129,7 @@ soap_string_out(struct soap *soap, const char *s, int flag) { wchar_t wc; register int m = mbtowc(&wc, t - 1, MB_CUR_MAX); if (m > 0 && !((soap_wchar)wc == c && m == 1 && c < 0x80)) - { if (soap_send_raw(soap, s, t - s - 1) || soap_pututf8(soap, wc)) + { if (soap_send_raw(soap, s, t - s - 1) || soap_pututf8(soap, (unsigned long)wc)) return soap->error; s = t += m - 1; continue; @@ -10740,8 +11175,8 @@ soap_string_in(struct soap *soap, int flag, long minlen, long maxlen) DBGLOG(TEST,SOAP_MESSAGE(fdebug, "String content includes tag '%s' and attributes\n", soap->tag)); t = soap->tmpbuf; *t = '<'; - t[sizeof(soap->tmpbuf)-1] = '\0'; strncpy(t + 1, soap->tag, sizeof(soap->tmpbuf) - 2); + t[sizeof(soap->tmpbuf) - 1] = '\0'; t += strlen(t); for (tp = soap->attributes; tp; tp = tp->next) { if (tp->visible) @@ -10810,7 +11245,7 @@ soap_string_in(struct soap *soap, int flag, long minlen, long maxlen) { soap_unget(soap, c); c = soap_getutf8(soap); } - if ((c & 0x7FFFFFFF) >= 0x80 && (soap->mode & SOAP_C_UTFSTRING)) + if ((c & 0x7FFFFFFF) >= 0x80 && (!flag || (soap->mode & SOAP_C_UTFSTRING))) { c &= 0x7FFFFFFF; t = buf; if (c < 0x0800) @@ -10868,7 +11303,7 @@ soap_string_in(struct soap *soap, int flag, long minlen, long maxlen) case 5: if (c == '>') state = 0; - else + else if (c != ']') state = 1; *s++ = (char)c; continue; @@ -10883,7 +11318,7 @@ soap_string_in(struct soap *soap, int flag, long minlen, long maxlen) case 7: if (c == '>') state = 0; - else + else if (c != '-') state = 2; *s++ = (char)c; continue; @@ -10891,7 +11326,7 @@ soap_string_in(struct soap *soap, int flag, long minlen, long maxlen) case 8: if (c == '>') state = 0; - else + else if (c != '?') state = 3; *s++ = (char)c; continue; @@ -11039,7 +11474,12 @@ soap_string_in(struct soap *soap, int flag, long minlen, long maxlen) m--; continue; } - if (soap->mode & SOAP_C_UTFSTRING) +#ifndef WITH_CDATA + if (!flag) + c = soap_getchar(soap); + else +#endif + if ((soap->mode & SOAP_C_UTFSTRING)) { if (((c = soap_get(soap)) & 0x80000000) && c >= -0x7FFFFF80 && c < SOAP_AP) { c &= 0x7FFFFFFF; t = buf; @@ -11100,9 +11540,16 @@ soap_string_in(struct soap *soap, int flag, long minlen, long maxlen) break; case '/': if (n > 0) - { c = soap_get(soap); - if (c == SOAP_GT) - n--; + { if (!flag) + { c = soap_getchar(soap); + if (c == '>') + n--; + } + else + { c = soap_get(soap); + if (c == SOAP_GT) + n--; + } soap_unget(soap, c); } *s++ = '/'; @@ -11155,6 +11602,25 @@ soap_string_in(struct soap *soap, int flag, long minlen, long maxlen) default: if ((int)c == EOF) goto end; +#ifndef WITH_CDATA + if (c == '<' && !flag) + { if (f && n == 0) + goto end; + c = soap_getchar(soap); + soap_unget(soap, c); + if (c == '/') + { c = SOAP_TT; + if (n == 0) + goto end; + n--; + } + else + n++; + *s++ = '<'; + break; + } + else +#endif #ifndef WITH_LEANER #ifdef HAVE_WCTOMB if (soap->mode & SOAP_C_MBSTRING) @@ -11188,7 +11654,7 @@ end: #ifdef WITH_FAST t = soap_strdup(soap, soap->labbuf); #else - soap_size_block(soap, NULL, i+1); + soap_size_block(soap, NULL, i + 1); t = soap_save_block(soap, NULL, 0); #endif if (l < minlen) @@ -11275,11 +11741,12 @@ soap_wstring_out(struct soap *soap, const wchar_t *s, int flag) if (soap_send_raw(soap, &tmp, 1)) return soap->error; } - else /* check UTF16 encoding when wchar_t is too small to hold UCS */ - { if (sizeof(wchar_t) < 4 && (c & 0xD800) == 0xD800) - { /* http://unicode.org/faq/utf_bom.html#utf16-2 */ - if ((*s & 0xD800) == 0xD800) - c = (c << 10) + *s++ + 0x10000 - (0xD800 << 10) - 0xDC00; + else + { /* check for UTF16 encoding when wchar_t is too small to hold UCS */ + if (sizeof(wchar_t) < 4 && (c & 0xFC00) == 0xD800) + { register soap_wchar d = *s++; + if ((d & 0xFC00) == 0xDC00) + c = ((c - 0xD800) << 10) + (d - 0xDC00) + 0x10000; else c = 0xFFFD; /* Malformed */ } @@ -11316,8 +11783,8 @@ soap_wstring_in(struct soap *soap, int flag, long minlen, long maxlen) struct soap_attribute *tp; t = soap->tmpbuf; *t = '<'; - t[sizeof(soap->tmpbuf)-1] = '\0'; strncpy(t + 1, soap->tag, sizeof(soap->tmpbuf) - 2); + t[sizeof(soap->tmpbuf) - 1] = '\0'; t += strlen(t); for (tp = soap->attributes; tp; tp = tp->next) { if (tp->visible) @@ -11422,9 +11889,9 @@ soap_wstring_in(struct soap *soap, int flag, long minlen, long maxlen) default: if ((int)c == EOF) goto end; + /* use UTF16 encoding when wchar_t is too small to hold UCS */ if (sizeof(wchar_t) < 4 && c > 0xFFFF) - { soap_wchar c1, c2; - /* http://unicode.org/faq/utf_bom.html#utf16-2 */ + { register soap_wchar c1, c2; c1 = 0xD800 - (0x10000 >> 10) + (c >> 10); c2 = 0xDC00 + (c & 0x3FF); c = c1; @@ -11552,7 +12019,12 @@ SOAP_FMAC1 const char* SOAP_FMAC2 soap_long2s(struct soap *soap, long n) -{ sprintf(soap->tmpbuf, "%ld", n); +{ +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "%ld", n); +#else + sprintf(soap->tmpbuf, "%ld", n); +#endif return soap->tmpbuf; } #endif @@ -11635,7 +12107,12 @@ SOAP_FMAC1 const char* SOAP_FMAC2 soap_LONG642s(struct soap *soap, LONG64 n) -{ sprintf(soap->tmpbuf, SOAP_LONG_FORMAT, n); +{ +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), SOAP_LONG_FORMAT, n); +#else + sprintf(soap->tmpbuf, SOAP_LONG_FORMAT, n); +#endif return soap->tmpbuf; } #endif @@ -11881,16 +12358,19 @@ soap_float2s(struct soap *soap, float n) return "INF"; if (soap_isninff(n)) return "-INF"; - s = soap->tmpbuf; #if defined(HAVE_SPRINTF_L) # ifdef WIN32 - _sprintf_s_l(s, _countof(soap->tmpbuf), soap->float_format, soap->c_locale, n); + _sprintf_s_l(soap->tmpbuf, _countof(soap->tmpbuf), soap->float_format, soap->c_locale, n); # else - sprintf_l(s, soap->c_locale, soap->float_format, n); + sprintf_l(soap->tmpbuf, soap->c_locale, soap->float_format, n); # endif #else - sprintf(s, soap->float_format, n); - s = strchr(s, ','); /* convert decimal comma to DP */ +# ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), soap->float_format, n); +# else + sprintf(soap->tmpbuf, soap->float_format, n); +# endif + s = strchr(soap->tmpbuf, ','); /* convert decimal comma to DP */ if (s) *s = '.'; #endif @@ -11954,10 +12434,10 @@ soap_s2float(struct soap *soap, const char *s, float *p) #endif { #if defined(HAVE_SSCANF_L) && !defined(HAVE_STRTOF_L) && !defined(HAVE_STRTOD_L) - if (sscanf_l(s, soap->c_locale, "%g", p) != 1) + if (sscanf_l(s, soap->c_locale, "%f", p) != 1) soap->error = SOAP_TYPE; #elif defined(HAVE_SSCANF) - if (sscanf(s, "%g", p) != 1) + if (sscanf(s, "%f", p) != 1) soap->error = SOAP_TYPE; #else soap->error = SOAP_TYPE; @@ -12035,16 +12515,19 @@ soap_double2s(struct soap *soap, double n) return "INF"; if (soap_isninfd(n)) return "-INF"; - s = soap->tmpbuf; #if defined(HAVE_SPRINTF_L) # ifdef WIN32 - _sprintf_s_l(s, _countof(soap->tmpbuf), soap->double_format, soap->c_locale, n); + _sprintf_s_l(soap->tmpbuf, _countof(soap->tmpbuf), soap->double_format, soap->c_locale, n); # else - sprintf_l(s, soap->c_locale, soap->double_format, n); + sprintf_l(soap->tmpbuf, soap->c_locale, soap->double_format, n); # endif #else - sprintf(s, soap->double_format, n); - s = strchr(s, ','); /* convert decimal comma to DP */ +# ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), soap->double_format, n); +#else + sprintf(soap->tmpbuf, soap->double_format, n); +#endif + s = strchr(soap->tmpbuf, ','); /* convert decimal comma to DP */ if (s) *s = '.'; #endif @@ -12099,10 +12582,10 @@ soap_s2double(struct soap *soap, const char *s, double *p) #endif { #if defined(HAVE_SSCANF_L) && !defined(HAVE_STRTOF_L) && !defined(HAVE_STRTOD_L) - if (sscanf_l(s, soap->c_locale, "%lg", p) != 1) + if (sscanf_l(s, soap->c_locale, "%lf", p) != 1) soap->error = SOAP_TYPE; #elif defined(HAVE_SSCANF) - if (sscanf(s, "%lg", p) != 1) + if (sscanf(s, "%lf", p) != 1) soap->error = SOAP_TYPE; #else soap->error = SOAP_TYPE; @@ -12370,7 +12853,12 @@ SOAP_FMAC1 const char* SOAP_FMAC2 soap_unsignedLong2s(struct soap *soap, unsigned long n) -{ sprintf(soap->tmpbuf, "%lu", n); +{ +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "%lu", n); +#else + sprintf(soap->tmpbuf, "%lu", n); +#endif return soap->tmpbuf; } #endif @@ -12453,7 +12941,12 @@ SOAP_FMAC1 const char* SOAP_FMAC2 soap_ULONG642s(struct soap *soap, ULONG64 n) -{ sprintf(soap->tmpbuf, SOAP_ULONG_FORMAT, n); +{ +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), SOAP_ULONG_FORMAT, n); +#else + sprintf(soap->tmpbuf, SOAP_ULONG_FORMAT, n); +#endif return soap->tmpbuf; } #endif @@ -12590,8 +13083,8 @@ soap_s2QName(struct soap *soap, const char *s, char **t, long minlen, long maxle while (s[n] && !soap_blank((soap_wchar)s[n])) n++; np = soap->nlist; - /* if there is no namespace stack, or prefix is "xml" then copy string */ - if (!np || !strncmp(s, "xml:", 4)) + /* if there is no namespace stack, or prefix is "#" or "xml" then copy string */ + if (!np || *s == '#' || !strncmp(s, "xml:", 4)) { soap_append_lab(soap, s, n); } else /* we normalize the QName by replacing its prefix */ @@ -12628,7 +13121,7 @@ soap_s2QName(struct soap *soap, const char *s, char **t, long minlen, long maxle soap_append_lab(soap, "\"", 1); } else - { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\nNamespace prefix of '%s' not defined (index=%d, URI=%s)\n", s, np->index, np->ns ? np->ns : SOAP_STR_EOS)); + { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\nNamespace prefix of '%s' not defined (index=%d, URI='%s')\n", s, np->index, np->ns ? np->ns : SOAP_STR_EOS)); return soap->error = SOAP_NAMESPACE; } } @@ -12676,11 +13169,19 @@ soap_QName2s(struct soap *soap, const char *s) n++; /* normal prefix: pass string as is */ if (*s != '"') - { soap_append_lab(soap, s, n); + { #ifndef WITH_LEAN if ((soap->mode & SOAP_XML_CANONICAL)) soap_utilize_ns(soap, s); + if ((soap->mode & SOAP_XML_DEFAULTNS)) + { const char *r = strchr(s, ':'); + if (r && soap->nlist && !strncmp(soap->nlist->id, s, r-s) && !soap->nlist->id[r-s]) + { n -= r-s + 1; + s = r + 1; + } + } #endif + soap_append_lab(soap, s, n); } else /* URL-based string prefix */ { const char *q; @@ -12700,12 +13201,22 @@ soap_QName2s(struct soap *soap, const char *s) } /* URL is in the namespace table? */ if (p && p->id) - { soap_append_lab(soap, p->id, strlen(p->id)); + { const char *r = p->id; +#ifndef WITH_LEAN + if ((soap->mode & SOAP_XML_DEFAULTNS) && soap->nlist && !strcmp(soap->nlist->id, r)) + q++; + else +#endif + soap_append_lab(soap, r, strlen(r)); } else /* not in namespace table: create xmlns binding */ { char *r = soap_strdup(soap, s); r[q-s] = '\0'; +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "xmlns:_%d", soap->idnum++); +#else sprintf(soap->tmpbuf, "xmlns:_%d", soap->idnum++); +#endif soap_set_attr(soap, soap->tmpbuf, r, 1); soap_append_lab(soap, soap->tmpbuf + 6, strlen(soap->tmpbuf + 6)); } @@ -13004,6 +13515,20 @@ soap_dateTime2s(struct soap *soap, time_t n) #elif defined(HAVE_GMTIME) if ((pT = gmtime(&n))) strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%SZ", pT); +#elif defined(HAVE_TM_GMTOFF) || defined(HAVE_STRUCT_TM_TM_GMTOFF) || defined(HAVE_STRUCT_TM___TM_GMTOFF) +#if defined(HAVE_LOCALTIME_R) + if (localtime_r(&n, pT)) + { strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S%z", pT); + memmove(soap->tmpbuf + 23, soap->tmpbuf + 22, 3); /* 2000-03-01T02:00:00+0300 */ + soap->tmpbuf[22] = ':'; /* 2000-03-01T02:00:00+03:00 */ + } +#else + if ((pT = localtime(&n))) + { strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S%z", pT); + memmove(soap->tmpbuf + 23, soap->tmpbuf + 22, 3); /* 2000-03-01T02:00:00+0300 */ + soap->tmpbuf[22] = ':'; /* 2000-03-01T02:00:00+03:00 */ + } +#endif #elif defined(HAVE_GETTIMEOFDAY) struct timezone tz; memset((void*)&tz, 0, sizeof(tz)); @@ -13012,14 +13537,22 @@ soap_dateTime2s(struct soap *soap, time_t n) { struct timeval tv; gettimeofday(&tv, &tz); strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S", pT); +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf + strlen(soap->tmpbuf), sizeof(soap->tmpbuf) - strlen(soap->tmpbuf), "%+03d:%02d", -tz.tz_minuteswest/60+(pT->tm_isdst!=0), abs(tz.tz_minuteswest)%60); +#else sprintf(soap->tmpbuf + strlen(soap->tmpbuf), "%+03d:%02d", -tz.tz_minuteswest/60+(pT->tm_isdst!=0), abs(tz.tz_minuteswest)%60); +#endif } #else if ((pT = localtime(&n))) { struct timeval tv; gettimeofday(&tv, &tz); strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S", pT); +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf + strlen(soap->tmpbuf), sizeof(soap->tmpbuf) - strlen(soap->tmpbuf), "%+03d:%02d", -tz.tz_minuteswest/60+(pT->tm_isdst!=0), abs(tz.tz_minuteswest)%60); +#else sprintf(soap->tmpbuf + strlen(soap->tmpbuf), "%+03d:%02d", -tz.tz_minuteswest/60+(pT->tm_isdst!=0), abs(tz.tz_minuteswest)%60); +#endif } #endif #elif defined(HAVE_FTIME) @@ -13034,7 +13567,11 @@ soap_dateTime2s(struct soap *soap, time_t n) ftime(&t); #endif strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S", pT); +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf + strlen(soap->tmpbuf), sizeof(soap->tmpbuf) - strlen(soap->tmpbuf), "%+03d:%02d", -t.timezone/60+(pT->tm_isdst!=0), abs(t.timezone)%60); +#else sprintf(soap->tmpbuf + strlen(soap->tmpbuf), "%+03d:%02d", -t.timezone/60+(pT->tm_isdst!=0), abs(t.timezone)%60); +#endif } #else if ((pT = localtime(&n))) @@ -13045,7 +13582,11 @@ soap_dateTime2s(struct soap *soap, time_t n) ftime(&t); #endif strftime(soap->tmpbuf, sizeof(soap->tmpbuf), "%Y-%m-%dT%H:%M:%S", pT); +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf + strlen(soap->tmpbuf), sizeof(soap->tmpbuf) - strlen(soap->tmpbuf), "%+03d:%02d", -t.timezone/60+(pT->tm_isdst!=0), abs(t.timezone)%60); +#else sprintf(soap->tmpbuf + strlen(soap->tmpbuf), "%+03d:%02d", -t.timezone/60+(pT->tm_isdst!=0), abs(t.timezone)%60); +#endif } #endif #elif defined(HAVE_LOCALTIME_R) @@ -13189,8 +13730,11 @@ soap_outliteral(struct soap *soap, const char *tag, char *const*p, const char *t const char *t = NULL; if (tag && *tag != '-') { if (soap->local_namespaces && (t = strchr(tag, ':'))) - { strncpy(soap->tmpbuf, tag, t-tag); - soap->tmpbuf[t-tag] = '\0'; + { size_t n = t - tag; + if (n >= sizeof(soap->tmpbuf)) + n = sizeof(soap->tmpbuf) - 1; + strncpy(soap->tmpbuf, tag, n); + soap->tmpbuf[n] = '\0'; for (i = 0; soap->local_namespaces[i].id; i++) if (!strcmp(soap->tmpbuf, soap->local_namespaces[i].id)) break; @@ -13207,7 +13751,7 @@ soap_outliteral(struct soap *soap, const char *tag, char *const*p, const char *t } } if (p && *p) - { if (soap_send(soap, *p)) + { if (soap_send(soap, *p)) /* send as-is */ return soap->error; } if (t) @@ -13261,8 +13805,11 @@ soap_outwliteral(struct soap *soap, const char *tag, wchar_t *const*p, const cha const char *t = NULL; if (tag && *tag != '-') { if (soap->local_namespaces && (t = strchr(tag, ':'))) - { strncpy(soap->tmpbuf, tag, t-tag); - soap->tmpbuf[t-tag] = '\0'; + { size_t n = t - tag; + if (n >= sizeof(soap->tmpbuf)) + n = sizeof(soap->tmpbuf) - 1; + strncpy(soap->tmpbuf, tag, n); + soap->tmpbuf[n] = '\0'; for (i = 0; soap->local_namespaces[i].id; i++) if (!strcmp(soap->tmpbuf, soap->local_namespaces[i].id)) break; @@ -13277,14 +13824,12 @@ soap_outwliteral(struct soap *soap, const char *tag, wchar_t *const*p, const cha if (soap_element_begin_out(soap, t, 0, type)) return soap->error; } - if (soap_send(soap, soap->tmpbuf)) - return soap->error; } if (p) { wchar_t c; const wchar_t *s = *p; while ((c = *s++)) - { if (soap_pututf8(soap, (unsigned long)c)) + { if (soap_pututf8(soap, (unsigned long)c)) /* send as-is in UTF8 */ return soap->error; } } @@ -13359,6 +13904,7 @@ soap_value(struct soap *soap) break; } s[1] = '\0'; + soap->tmpbuf[sizeof(soap->tmpbuf) - 1] = '\0'; /* appease */ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Element content value='%s'\n", soap->tmpbuf)); if (c == SOAP_TT || c == SOAP_LT || (int)c == EOF) soap_unget(soap, c); @@ -13392,11 +13938,11 @@ soap_getline(struct soap *soap, char *s, int len) return soap->error = SOAP_CHK_EOF; *s++ = (char)c; } + *s = '\0'; if (c != '\n') c = soap_getchar(soap); /* got \r or something else, now get \n */ if (c == '\n') - { *s = '\0'; - if (i+1 == len) /* empty line: end of HTTP/MIME header */ + { if (i + 1 == len) /* empty line: end of HTTP/MIME header */ break; c = soap_get0(soap); if (c != ' ' && c != '\t') /* HTTP line continuation? */ @@ -13404,7 +13950,7 @@ soap_getline(struct soap *soap, char *s, int len) } else if ((int)c == EOF) return soap->error = SOAP_CHK_EOF; - if (i < 0) + if (i <= 0) return soap->error = SOAP_HDR; } return SOAP_OK; @@ -13464,7 +14010,7 @@ soap_count_attachments(struct soap *soap) /* count \r\n--boundary-- */ count += 6 + n; } - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "New count is %lu bytes\n", (unsigned long)count)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "New count=%lu\n", (unsigned long)count)); return count; #else return soap->count; @@ -13662,7 +14208,7 @@ soap_getdimefield(struct soap *soap, size_t n) *s++ = (char)c; } *s = '\0'; - if ((soap->error = soap_move(soap, -(long)n&3))) + if ((soap->error = soap_move(soap, (size_t)(-(long)n&3)))) return NULL; } else @@ -13689,7 +14235,7 @@ soap_getdimehdr(struct soap *soap) return soap->error = SOAP_DIME_END; DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Get DIME header\n")); if (soap->dime.buflen || soap->dime.chunksize) - { if (soap_move(soap, (long)(soap->dime.size - soap_tell(soap)))) + { if (soap_move(soap, soap->dime.size - soap_tell(soap))) return soap->error = SOAP_CHK_EOF; soap_unget(soap, soap_getchar(soap)); /* skip padding and get hdr */ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "... From chunked\n")); @@ -13707,7 +14253,7 @@ soap_getdimehdr(struct soap *soap) optlen = (tmp[2] << 8) | tmp[3]; idlen = (tmp[4] << 8) | tmp[5]; typelen = (tmp[6] << 8) | tmp[7]; - soap->dime.size = (tmp[8] << 24) | (tmp[9] << 16) | (tmp[10] << 8) | tmp[11]; + soap->dime.size = ((size_t)tmp[8] << 24) | ((size_t)tmp[9] << 16) | ((size_t)tmp[10] << 8) | ((size_t)tmp[11]); DBGLOG(TEST, SOAP_MESSAGE(fdebug, "DIME size=%lu flags=0x%X\n", (unsigned long)soap->dime.size, soap->dime.flags)); if (!(soap->dime.options = soap_getdimefield(soap, optlen)) && soap->error) return soap->error; @@ -13715,7 +14261,7 @@ soap_getdimehdr(struct soap *soap) return soap->error; if (!(soap->dime.type = soap_getdimefield(soap, typelen)) && soap->error) return soap->error; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "DIME id=%s, type=%s, options=%s\n", soap->dime.id ? soap->dime.id : SOAP_STR_EOS, soap->dime.type ? soap->dime.type : "", soap->dime.options ? soap->dime.options+4 : SOAP_STR_EOS)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "DIME id='%s', type='%s', options='%s'\n", soap->dime.id ? soap->dime.id : SOAP_STR_EOS, soap->dime.type ? soap->dime.type : "", soap->dime.options ? soap->dime.options+4 : SOAP_STR_EOS)); if (soap->dime.flags & SOAP_DIME_ME) soap->mode &= ~SOAP_ENC_DIME; return SOAP_OK; @@ -13733,10 +14279,10 @@ soap_getdime(struct soap *soap) { while (soap->dime.flags & SOAP_DIME_CF) { if (soap_getdimehdr(soap)) return soap->error; - if (soap_move(soap, (long)soap->dime.size)) + if (soap_move(soap, soap->dime.size)) return soap->error = SOAP_EOF; } - if (soap_move(soap, (long)(((soap->dime.size+3)&(~3))-soap_tell(soap)))) + if (soap_move(soap, (size_t)(((soap->dime.size+3)&(~3)) - soap_tell(soap)))) return soap->error = SOAP_EOF; for (;;) { register struct soap_multipart *content; @@ -13768,7 +14314,7 @@ soap_getdime(struct soap *soap) goto end; } } - if (soap_move(soap, -(long)soap->dime.size&3)) + if (soap_move(soap, (size_t)(-(long)soap->dime.size&3))) { soap->error = SOAP_EOF; break; } @@ -13796,6 +14342,10 @@ end: { register soap_wchar c; register size_t i; register char *s; + if (soap->dime.size > SOAP_MAXDIMESIZE) + { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "DIME size=%lu exceeds SOAP_MAXDIMESIZE=%lu\n", (unsigned long)soap->dime.size, (unsigned long)SOAP_MAXDIMESIZE)); + return soap->error = SOAP_DIME_ERROR; + } s = (char*)soap_push_block(soap, NULL, soap->dime.size); if (!s) return soap->error = SOAP_EOM; @@ -13804,17 +14354,17 @@ end: return soap->error = SOAP_EOF; *s++ = (char)c; } - if (soap_move(soap, -(long)soap->dime.size&3)) + if (soap_move(soap, (size_t)(-(long)soap->dime.size&3))) return soap->error = SOAP_EOF; if (!(soap->dime.flags & SOAP_DIME_CF)) break; if (soap_getdimehdr(soap)) return soap->error; } - soap->dime.size = soap->blist->size++; /* allocate one more for '\0' */ + soap->dime.size = soap->blist->size++; /* allocate one more byte in blist for the terminating '\0' */ if (!(soap->dime.ptr = soap_save_block(soap, NULL, NULL, 0))) return soap->error; - soap->dime.ptr[soap->dime.size] = '\0'; /* force 0-terminated */ + soap->dime.ptr[soap->dime.size] = '\0'; /* make 0-terminated */ soap->dime.id = id; soap->dime.type = type; soap->dime.options = options; @@ -13967,7 +14517,7 @@ soap_get_mime_attachment(struct soap *soap, void *handle) return NULL; } } - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Parsing MIME content id=%s type=%s\n", content->id ? content->id : SOAP_STR_EOS, content->type ? content->type : SOAP_STR_EOS)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Parsing MIME content id='%s' type='%s'\n", content->id ? content->id : SOAP_STR_EOS, content->type ? content->type : SOAP_STR_EOS)); if (!content->ptr && soap_new_block(soap) == NULL) { soap->error = SOAP_EOM; return NULL; @@ -13988,7 +14538,9 @@ soap_get_mime_attachment(struct soap *soap, void *handle) { if (!flag) { c = soap_get1(soap); if ((int)c == EOF) - { soap->error = SOAP_CHK_EOF; + { if (content->ptr && soap->fmimewriteclose) + soap->fmimewriteclose(soap, (void*)content->ptr); + soap->error = SOAP_CHK_EOF; return NULL; } } @@ -14001,7 +14553,9 @@ soap_get_mime_attachment(struct soap *soap, void *handle) do c = soap_getchar(soap); while (c == *t++); if ((int)c == EOF) - { soap->error = SOAP_CHK_EOF; + { if (content->ptr && soap->fmimewriteclose) + soap->fmimewriteclose(soap, (void*)content->ptr); + soap->error = SOAP_CHK_EOF; return NULL; } if (!*--t) @@ -14021,7 +14575,7 @@ soap_get_mime_attachment(struct soap *soap, void *handle) } } end: - *s = '\0'; /* force 0-terminated */ + *s = '\0'; /* make 0-terminated */ if (content->ptr) { if (!soap->error && soap->fmimewrite) soap->error = soap->fmimewrite(soap, (void*)content->ptr, soap->tmpbuf, i); @@ -14031,7 +14585,7 @@ end: return NULL; } else - { content->size = soap_size_block(soap, NULL, i+1)-1; + { content->size = soap_size_block(soap, NULL, i+1) - 1; /* last block with '\0' */ content->ptr = soap_save_block(soap, NULL, NULL, 0); } soap_resolve_attachment(soap, content); @@ -14095,11 +14649,11 @@ static void soap_resolve_attachment(struct soap *soap, struct soap_multipart *content) { if (content->id) { register struct soap_xlist **xp = &soap->xlist; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resolving attachment data for id=%s\n", content->id)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Resolving attachment data for id='%s'\n", content->id)); while (*xp) { register struct soap_xlist *xq = *xp; if (!soap_match_cid(soap, xq->id, content->id)) - { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Found matching attachment %s for content id=%s\n", xq->id, content->id)); + { DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Found matching attachment id='%s' for content id='%s'\n", xq->id, content->id)); *xp = xq->next; *xq->ptr = (unsigned char*)content->ptr; *xq->size = (int)content->size; @@ -14126,7 +14680,7 @@ int SOAP_FMAC2 soap_putmimehdr(struct soap *soap, struct soap_multipart *content) { const char *s; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "MIME attachment type=%s\n", content->type ? content->type : SOAP_STR_EOS)); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "MIME attachment type='%s'\n", content->type ? content->type : SOAP_STR_EOS)); if (soap_send3(soap, "\r\n--", soap->mime.boundary, "\r\n")) return soap->error; if (content->type && soap_send3(soap, "Content-Type: ", content->type, "\r\n")) @@ -14489,7 +15043,7 @@ int SOAP_FMAC2 soap_begin_recv(struct soap *soap) { register soap_wchar c; - DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Initializing for input\n")); + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Initializing for input from socket=%d/fd=%d\n", soap->socket, soap->recvfd)); soap->error = SOAP_OK; soap->filterstop = SOAP_OK; soap_free_temp(soap); @@ -14513,6 +15067,7 @@ soap_begin_recv(struct soap *soap) soap->level = 0; soap->part = SOAP_BEGIN; soap->alloced = 0; + soap->body = 1; soap->count = 0; soap->length = 0; soap->cdata = 0; @@ -14625,8 +15180,8 @@ soap_begin_recv(struct soap *soap) return soap->error = SOAP_CHK_EOF; soap_unget(soap, c); #ifndef WITH_NOHTTP - /* if not XML or MIME/DIME/ZLIB, assume HTTP header */ - if (c != '<' && !(soap->mode & (SOAP_ENC_MIME | SOAP_ENC_DIME | SOAP_ENC_ZLIB))) + /* if not XML/MIME/DIME/ZLIB, assume HTTP method or status line */ + if (((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) && !(soap->mode & (SOAP_ENC_MIME | SOAP_ENC_DIME | SOAP_ENC_ZLIB | SOAP_ENC_XML))) { soap_mode m = soap->imode; soap->mode &= ~SOAP_IO; soap->error = soap->fparse(soap); @@ -14653,17 +15208,19 @@ soap_begin_recv(struct soap *soap) soap->chunksize = 0; } /* Note: fparse should not use soap_unget to push back last char */ - if (soap_get0(soap) == (int)EOF) - { if (soap->status == 200) - return soap->error = SOAP_NO_DATA; /* HTTP OK: always expect data */ +#if 0 + if (soap->status > 200 && soap->length == 0 && !(soap->http_content && (!soap->keep_alive || soap->recv_timeout)) && (soap->imode & SOAP_IO) != SOAP_IO_CHUNK) +#endif + if (soap->status && !soap->body) return soap->error = soap->status; - } #ifdef WITH_ZLIB if (soap->zlib_in != SOAP_ZLIB_NONE) { #ifdef WITH_GZIP if (soap->zlib_in != SOAP_ZLIB_DEFLATE) { c = soap_get1(soap); + if (c == (int)EOF) + return soap->error = SOAP_EOF; if (c == 0x1F) { if (soap_getgziphdr(soap)) return soap->error; @@ -14702,10 +15259,16 @@ soap_begin_recv(struct soap *soap) #ifndef WITH_LEANER if (soap->fpreparerecv && (soap->mode & SOAP_IO) != SOAP_IO_CHUNK && soap->buflen > soap->bufidx) { int r; + DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Invoking fpreparerecv\n")); if ((r = soap->fpreparerecv(soap, soap->buf + soap->bufidx, soap->buflen - soap->bufidx))) return soap->error = r; } #endif + if (soap_get0(soap) == (int)EOF) + { if (soap->status == 0 || soap->status == 200) + return soap->error = SOAP_NO_DATA; /* HTTP OK: always expect data */ + return soap->error = soap->status; + } if (soap->error) { if (soap->error == SOAP_FORM && soap->fform) { soap->error = soap->fform(soap); @@ -14779,7 +15342,11 @@ soap_envelope_begin_out(struct soap *soap) s = "application/xop+xml; charset=utf-8; type=\"text/xml\""; else s = "text/xml; charset=utf-8"; +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "--%s\r\nContent-Type: %s\r\nContent-Transfer-Encoding: binary\r\nContent-ID: %s\r\n\r\n", soap->mime.boundary, s, soap->mime.start); +#else sprintf(soap->tmpbuf, "--%s\r\nContent-Type: %s\r\nContent-Transfer-Encoding: binary\r\nContent-ID: %s\r\n\r\n", soap->mime.boundary, s, soap->mime.start); +#endif n = strlen(soap->tmpbuf); if (soap_send_raw(soap, soap->tmpbuf, n)) return soap->error; @@ -14791,6 +15358,8 @@ soap_envelope_begin_out(struct soap *soap) return soap->error; } #endif + if (soap->version == 0) + return SOAP_OK; soap->part = SOAP_IN_ENVELOPE; return soap_element_begin_out(soap, "SOAP-ENV:Envelope", 0, NULL); } @@ -14802,13 +15371,19 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_envelope_end_out(struct soap *soap) -{ if (soap_element_end_out(soap, "SOAP-ENV:Envelope") +{ if (soap->version == 0) + return SOAP_OK; + if (soap_element_end_out(soap, "SOAP-ENV:Envelope") || soap_send_raw(soap, "\r\n", 2)) /* 2.8: always emit \r\n */ return soap->error; #ifndef WITH_LEANER if ((soap->mode & SOAP_IO_LENGTH) && (soap->mode & SOAP_ENC_DIME) && !(soap->mode & SOAP_ENC_MTOM)) { soap->dime.size = soap->count - soap->dime.size; /* DIME in MIME correction */ +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->id, sizeof(soap->id), soap->dime_id_format, 0); +#else sprintf(soap->id, soap->dime_id_format, 0); +#endif soap->dime.id = soap->id; if (soap->local_namespaces) { if (soap->local_namespaces[0].out) @@ -14836,17 +15411,19 @@ soap_envelope_end_out(struct soap *soap) SOAP_FMAC1 char* SOAP_FMAC2 -soap_get_http_body(struct soap *soap) -{ +soap_get_http_body(struct soap *soap, size_t *len) +{ if (len) + *len = 0; #ifndef WITH_LEAN register size_t l = 0, n = 0; register char *s; - /* get HTML body of HTTP error content */ + /* get HTTP body length */ if (!(soap->mode & SOAP_ENC_ZLIB) && (soap->mode & SOAP_IO) != SOAP_IO_CHUNK) { n = soap->length; if (!n) return NULL; } + DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Parsing HTTP body (mode=0x%x,len=%lu)\n", soap->mode, (unsigned long)n)); #ifdef WITH_FAST soap->labidx = 0; /* use look-aside buffer */ #else @@ -14872,7 +15449,7 @@ soap_get_http_body(struct soap *soap) l++; if (n > 0 && l > n) goto end; - c = soap_getchar(soap); + c = soap_get1(soap); if ((int)c == EOF) goto end; *s++ = (char)(c & 0xFF); @@ -14880,8 +15457,11 @@ soap_get_http_body(struct soap *soap) } end: *s = '\0'; + if (len) + *len = l - 1; /* len excludes terminating \0 */ #ifdef WITH_FAST - s = soap_strdup(soap, soap->labbuf); + if ((s = (char*)soap_malloc(soap, l))) + memcpy(s, soap->labbuf, l); #else soap_size_block(soap, NULL, i+1); s = soap_save_block(soap, NULL, 0); @@ -14903,9 +15483,13 @@ soap_envelope_begin_in(struct soap *soap) { register struct Namespace *p; soap->part = SOAP_IN_ENVELOPE; if (soap_element_begin_in(soap, "SOAP-ENV:Envelope", 0, NULL)) - { if (soap->error == SOAP_TAG_MISMATCH - && !soap_element_begin_in(soap, "Envelope", 0, NULL)) - soap->error = SOAP_VERSIONMISMATCH; + { if (soap->error == SOAP_TAG_MISMATCH) + { if (!soap_element_begin_in(soap, "Envelope", 0, NULL)) + soap->error = SOAP_VERSIONMISMATCH; + else if (soap->status == 0 || (soap->status >= 200 && soap->status <= 299)) + return SOAP_OK; /* allow non-SOAP XML content to be captured */ + soap->error = soap->status; + } else if (soap->status) soap->error = soap->status; return soap->error; @@ -14940,7 +15524,9 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_envelope_end_in(struct soap *soap) -{ soap->part = SOAP_END_ENVELOPE; +{ if (soap->version == 0) + return SOAP_OK; + soap->part = SOAP_END_ENVELOPE; return soap_element_end_in(soap, "SOAP-ENV:Envelope"); } #endif @@ -14951,13 +15537,15 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_body_begin_out(struct soap *soap) -{ soap->part = SOAP_IN_BODY; - if (soap->version == 1) +{ if (soap->version == 1) soap->encoding = 1; #ifndef WITH_LEAN if ((soap->mode & SOAP_SEC_WSUID) && soap_set_attr(soap, "wsu:Id", "Body", 1)) return soap->error; #endif + if (soap->version == 0) + return SOAP_OK; + soap->part = SOAP_IN_BODY; return soap_element_begin_out(soap, "SOAP-ENV:Body", 0, NULL); } #endif @@ -14968,7 +15556,9 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_body_end_out(struct soap *soap) -{ if (soap_element_end_out(soap, "SOAP-ENV:Body")) +{ if (soap->version == 0) + return SOAP_OK; + if (soap_element_end_out(soap, "SOAP-ENV:Body")) return soap->error; soap->part = SOAP_END_BODY; return SOAP_OK; @@ -14981,7 +15571,9 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_body_begin_in(struct soap *soap) -{ soap->part = SOAP_IN_BODY; +{ if (soap->version == 0) + return SOAP_OK; + soap->part = SOAP_IN_BODY; if (soap_element_begin_in(soap, "SOAP-ENV:Body", 0, NULL)) return soap->error; if (!soap->body) @@ -14996,7 +15588,9 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_body_end_in(struct soap *soap) -{ if (soap->part == SOAP_NO_BODY) +{ if (soap->version == 0) + return SOAP_OK; + if (soap->part == SOAP_NO_BODY) return soap->error = SOAP_OK; soap->part = SOAP_END_BODY; return soap_element_end_in(soap, "SOAP-ENV:Body"); @@ -15036,7 +15630,7 @@ soap_set_endpoint(struct soap *soap, const char *endpoint) if (!soap_tag_cmp(endpoint, "https:*")) soap->port = 443; #endif - strncpy(soap->endpoint, endpoint, sizeof(soap->endpoint) - 1); + strncpy(soap->endpoint, endpoint, sizeof(soap->endpoint)); soap->endpoint[sizeof(soap->endpoint) - 1] = '\0'; s = strchr(endpoint, ':'); if (s && s[1] == '/' && s[2] == '/') @@ -15198,7 +15792,7 @@ soap_try_connect_command(struct soap *soap, int http_command, const char *endpoi soap->mode = k; } if (http_command == SOAP_GET || http_command == SOAP_DEL) - return soap_end_send(soap); + return soap_end_send_flush(soap); #endif return SOAP_OK; } @@ -15222,6 +15816,8 @@ soap_ntlm_handshake(struct soap *soap, int command, const char *endpoint, const size_t c = soap->count; soap_mode m = soap->mode, o = soap->omode; int s = soap->status; + char *a = soap->action; + short v = soap->version; DBGLOG(TEST,SOAP_MESSAGE(fdebug, "NTLM '%s'\n", soap->ntlm_challenge)); if (!*soap->ntlm_challenge) { DBGLOG(TEST,SOAP_MESSAGE(fdebug, "NTLM S->C Type 1: received NTLM authentication challenge from server\n")); @@ -15240,7 +15836,7 @@ soap_ntlm_handshake(struct soap *soap, int command, const char *endpoint, const soap->keep_alive = 1; soap->status = command; if (soap->fpost(soap, endpoint, host, port, soap->path, soap->action, 0) - || soap_end_send(soap)) + || soap_end_send_flush(soap)) return soap->error; soap->mode = m; soap->keep_alive = k; @@ -15248,7 +15844,7 @@ soap_ntlm_handshake(struct soap *soap, int command, const char *endpoint, const oldheader = soap->header; if (soap_begin_recv(soap)) if (soap->error == SOAP_EOF) - return soap->error; + return soap->error; soap_end_recv(soap); soap->header = oldheader; soap->length = l; @@ -15266,12 +15862,18 @@ soap_ntlm_handshake(struct soap *soap, int command, const char *endpoint, const /* C -> S GET ... Authorization: NTLM TlRMTVNTUAADAAAAGAAYAHIAAAAYABgAigAAABQAFABAAAAADAAMAFQAAAASABIAYAAAAAAAAACiAAAAAYIAAFUAUgBTAEEALQBNAEkATgBPAFIAWgBhAHAAaABvAGQATABJAEcASABUAEMASQBUAFkArYfKbe/jRoW5xDxHeoxC1gBmfWiS5+iX4OAN4xBKG/IFPwfH3agtPEia6YnhsADT */ + soap->userid = NULL; + soap->passwd = NULL; + soap->proxy_userid = NULL; + soap->proxy_passwd = NULL; soap->keep_alive = k; soap->length = l; soap->count = c; soap->mode = m; soap->omode = o; soap->status = s; + soap->action = a; + soap->version = v; } return SOAP_OK; } @@ -15304,13 +15906,13 @@ soap_s2base64(struct soap *soap, const unsigned char *s, char *t, int n) t += 4; } t[0] = '\0'; - if (n > 0) + if (n > 0) /* 0 < n <= 2 implies that t[0..4] is allocated (base64 scaling formula) */ { m = 0; for (i = 0; i < n; i++) m = (m << 8) | *s++; for (; i < 3; i++) m <<= 8; - for (i++; i > 0; m >>= 6) + for (i = 4; i > 0; m >>= 6) t[--i] = soap_base64o[m & 0x3F]; for (i = 3; i > n; i--) t[i] = '='; @@ -15346,58 +15948,57 @@ soap_base642s(struct soap *soap, const char *s, char *t, size_t l, int *n) p = t; if (n) *n = 0; - for (;;) - { for (i = 0; i < SOAP_BLKLEN; i++) - { m = 0; - j = 0; - while (j < 4) - { c = *s++; - if (c == '=' || !c) - { i *= 3; - switch (j) + for (i = 0; ; i += 3, l -= 3) + { m = 0; + j = 0; + while (j < 4) + { c = *s++; + if (c == '=' || !c) + { if (l >= j - 1) + { switch (j) { case 2: *t++ = (char)((m >> 4) & 0xFF); i++; + l--; break; case 3: *t++ = (char)((m >> 10) & 0xFF); *t++ = (char)((m >> 2) & 0xFF); i += 2; + l -= 2; } - if (n) - *n += (int)i; - if (l >= j) - *t = '\0'; - return p; - } - c -= '+'; - if (c >= 0 && c <= 79) - { int b = soap_base64i[c]; - if (b >= 64) - { soap->error = SOAP_TYPE; - return NULL; - } - m = (m << 6) + b; - j++; } - else if (!soap_blank(c + '+')) + if (n) + *n = (int)i; + if (l) + *t = '\0'; + return p; + } + c -= '+'; + if (c >= 0 && c <= 79) + { int b = soap_base64i[c]; + if (b >= 64) { soap->error = SOAP_TYPE; return NULL; } + m = (m << 6) + b; + j++; } - if (l < 3) - { if (n) - *n += (int)i; - *t = '\0'; - return p; + else if (!soap_blank(c + '+')) + { soap->error = SOAP_TYPE; + return NULL; } - *t++ = (char)((m >> 16) & 0xFF); - *t++ = (char)((m >> 8) & 0xFF); - *t++ = (char)(m & 0xFF); - l -= 3; } - if (n) - *n += 3 * SOAP_BLKLEN; + if (l < 3) + { if (n) + *n = (int)i; + if (l) + *t = '\0'; + return p; + } + *t++ = (char)((m >> 16) & 0xFF); + *t++ = (char)((m >> 8) & 0xFF); + *t++ = (char)(m & 0xFF); } } #endif @@ -15481,7 +16082,7 @@ soap_puthttphdr(struct soap *soap, int status, size_t count) #ifndef WITH_LEANER register const char *r = NULL; #endif - if ((status == SOAP_FILE || soap->status == SOAP_PUT || soap->status == SOAP_POST_FILE) && soap->http_content) + if ((status == SOAP_FILE || soap->status == SOAP_PUT || soap->status == SOAP_POST_FILE) && soap->http_content && !strchr(s, 10) && !strchr(s, 13)) s = soap->http_content; else if (status == SOAP_HTML) s = "text/html; charset=utf-8"; @@ -15502,31 +16103,42 @@ soap_puthttphdr(struct soap *soap, int status, size_t count) s = "application/dime"; } if ((soap->mode & SOAP_ENC_MIME) && soap->mime.boundary && strlen(soap->mime.boundary) + strlen(soap->mime.start ? soap->mime.start : SOAP_STR_EOS) < sizeof(soap->tmpbuf) - 80) - { register const char *t = strchr(s, ';'); + { register const char *t; +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "multipart/related; charset=utf-8; boundary=\"%s\"; type=\"", soap->mime.boundary); +#else sprintf(soap->tmpbuf, "multipart/related; charset=utf-8; boundary=\"%s\"; type=\"", soap->mime.boundary); +#endif + t = strchr(s, ';'); if (t) - { strncat(soap->tmpbuf, s, t - s); - soap->tmpbuf[sizeof(soap->tmpbuf)-1] = '\0'; - } + strncat(soap->tmpbuf, s, t - s); else strcat(soap->tmpbuf, s); - if (soap->mime.start) + if (soap->mime.start && strlen(soap->tmpbuf) + strlen(soap->mime.start) + 11 < sizeof(soap->tmpbuf)) { strcat(soap->tmpbuf, "\"; start=\""); strcat(soap->tmpbuf, soap->mime.start); } strcat(soap->tmpbuf, "\""); - if (r) + if (r && strlen(soap->tmpbuf) + strlen(r) + 15 < sizeof(soap->tmpbuf)) { strcat(soap->tmpbuf, "; start-info=\""); strcat(soap->tmpbuf, r); strcat(soap->tmpbuf, "\""); } - s = soap->tmpbuf; } else - s = strcpy(soap->tmpbuf, s); + strncpy(soap->tmpbuf, s, sizeof(soap->tmpbuf)); + soap->tmpbuf[sizeof(soap->tmpbuf) - 1] = '\0'; + s = soap->tmpbuf; if (status == SOAP_OK && soap->version == 2 && soap->action && strlen(soap->action) + strlen(s) < sizeof(soap->tmpbuf) - 80) + { +#ifdef HAVE_SNPRINTF + size_t l = strlen(s); + soap_snprintf(soap->tmpbuf + l, sizeof(soap->tmpbuf) - l, "; action=\"%s\"", soap->action); +#else sprintf(soap->tmpbuf + strlen(s), "; action=\"%s\"", soap->action); #endif + } +#endif if ((err = soap->fposthdr(soap, "Content-Type", s))) return err; #ifdef WITH_ZLIB @@ -15547,7 +16159,12 @@ soap_puthttphdr(struct soap *soap, int status, size_t count) else #endif if (s) - { sprintf(soap->tmpbuf, "%lu", (unsigned long)count); + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->tmpbuf, sizeof(soap->tmpbuf), "%lu", (unsigned long)count); +#else + sprintf(soap->tmpbuf, "%lu", (unsigned long)count); +#endif err = soap->fposthdr(soap, "Content-Length", soap->tmpbuf); } if (err) @@ -15562,18 +16179,30 @@ soap_puthttphdr(struct soap *soap, int status, size_t count) #ifndef WITH_LEAN static const char* soap_set_validation_fault(struct soap *soap, const char *s, const char *t) -{ if (*soap->tag) +{ if (!t) + t = SOAP_STR_EOS; + if (*soap->tag) + { #ifdef HAVE_SNPRINTF soap_snprintf(soap->msgbuf, sizeof(soap->msgbuf), "Validation constraint violation: %s%s in element '%s'", s, t ? t : SOAP_STR_EOS, soap->tag); #else - sprintf(soap->msgbuf, "Validation constraint violation: %s%s in element '%s'", s, t ? t : SOAP_STR_EOS, soap->tag); + if (strlen(soap->tag) + strlen(t) < sizeof(soap->msgbuf) - 100) + sprintf(soap->msgbuf, "Validation constraint violation: %s%s in element '%s'", s, t, soap->tag); + else + sprintf(soap->msgbuf, "Validation constraint violation: %s", s); #endif + } else + { #ifdef HAVE_SNPRINTF soap_snprintf(soap->msgbuf, sizeof(soap->msgbuf), "Validation constraint violation: %s%s", s, t ? t : SOAP_STR_EOS); #else - sprintf(soap->msgbuf, "Validation constraint violation: %s%s", s, t ? t : SOAP_STR_EOS); + if (strlen(soap->tag) + strlen(t) < sizeof(soap->msgbuf) - 100) + sprintf(soap->msgbuf, "Validation constraint violation: %s%s", s, t); + else + sprintf(soap->msgbuf, "Validation constraint violation: %s", s); #endif + } return soap->msgbuf; } #endif @@ -15622,7 +16251,8 @@ soap_set_fault(struct soap *soap) #ifdef HAVE_SNPRINTF soap_snprintf(soap->msgbuf, sizeof(soap->msgbuf), "The data in element '%s' must be understood but cannot be handled", soap->tag); #else - sprintf(soap->msgbuf, "The data in element '%s' must be understood but cannot be handled", soap->tag); + strncpy(soap->msgbuf, soap->tag, sizeof(soap->msgbuf)); + soap->msgbuf[sizeof(soap->msgbuf) - 1] = '\0'; #endif *s = soap->msgbuf; break; @@ -15679,7 +16309,7 @@ soap_set_fault(struct soap *soap) *s = soap_set_validation_fault(soap, "nil not allowed", NULL); break; case SOAP_DUPLICATE_ID: - *s = soap_set_validation_fault(soap, "multiple definitions (use the SOAP_XML_TREE flag) of the same id ", soap->id); + *s = soap_set_validation_fault(soap, "multiple elements (use the SOAP_XML_TREE flag) with duplicate id ", soap->id); if (soap->version == 2) *soap_faultsubcode(soap) = "SOAP-ENC:DuplicateID"; break; @@ -15689,7 +16319,7 @@ soap_set_fault(struct soap *soap) *soap_faultsubcode(soap) = "SOAP-ENC:MissingID"; break; case SOAP_HREF: - *s = soap_set_validation_fault(soap, "incompatible object type ref/id pair ", soap->id); + *s = soap_set_validation_fault(soap, "incompatible object type id-ref ", soap->id); break; case SOAP_FAULT: break; @@ -15718,7 +16348,7 @@ soap_set_fault(struct soap *soap) *s = "Plugin registry error"; break; case SOAP_DIME_ERROR: - *s = "DIME format error"; + *s = "DIME format error or max DIME size exceeds SOAP_MAXDIMESIZE"; break; case SOAP_DIME_HREF: *s = "DIME href to missing attachment"; @@ -15774,14 +16404,13 @@ soap_set_fault(struct soap *soap) #endif case SOAP_EOF: #ifndef WITH_NOIO - strcpy(soap->msgbuf, soap_strerror(soap)); + *s = soap_strerror(soap); /* *s = soap->msgbuf */ #ifndef WITH_LEAN if (strlen(soap->msgbuf) + 25 < sizeof(soap->msgbuf)) { memmove(soap->msgbuf + 25, soap->msgbuf, strlen(soap->msgbuf) + 1); memcpy(soap->msgbuf, "End of file or no input: ", 25); } #endif - *s = soap->msgbuf; break; #else *s = "End of file or no input"; @@ -15802,7 +16431,12 @@ soap_set_fault(struct soap *soap) else #endif #endif - { sprintf(soap->msgbuf, "Error %d", soap->error); + { +#ifdef HAVE_SNPRINTF + soap_snprintf(soap->msgbuf, sizeof(soap->msgbuf), "Error %d", soap->error); +#else + sprintf(soap->msgbuf, "Error %d", soap->error); +#endif *s = soap->msgbuf; } } @@ -15816,7 +16450,7 @@ int SOAP_FMAC2 soap_send_fault(struct soap *soap) { register int status = soap->error; - if (status == SOAP_STOP) + if (status == SOAP_OK || status == SOAP_STOP) return soap_closesock(soap); DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Sending back fault struct for error code %d\n", soap->error)); soap->keep_alive = 0; /* to terminate connection */ @@ -15824,7 +16458,7 @@ soap_send_fault(struct soap *soap) if (soap->error < 200 && soap->error != SOAP_FAULT) soap->header = NULL; if (status != SOAP_EOF || (!soap->recv_timeout && !soap->send_timeout)) - { int r = 1; + { register int r = 1; #ifndef WITH_NOIO if (soap->fpoll && soap->fpoll(soap)) r = 0; @@ -15832,9 +16466,10 @@ soap_send_fault(struct soap *soap) else if (soap_valid_socket(soap->socket)) { r = tcp_select(soap, soap->socket, SOAP_TCP_SELECT_RCV | SOAP_TCP_SELECT_SND, 0); if (r > 0) - { if (!(r & SOAP_TCP_SELECT_SND) + { int t; + if (!(r & SOAP_TCP_SELECT_SND) || ((r & SOAP_TCP_SELECT_RCV) - && recv(soap->socket, soap->tmpbuf, 1, MSG_PEEK) < 0)) + && recv(soap->socket, (char*)&t, 1, MSG_PEEK) < 0)) r = 0; } } @@ -15842,6 +16477,7 @@ soap_send_fault(struct soap *soap) #endif if (r > 0) { soap->error = SOAP_OK; + soap->encodingStyle = NULL; /* no encodingStyle in Faults */ soap_serializeheader(soap); soap_serializefault(soap); soap_begin_count(soap); @@ -15884,6 +16520,8 @@ soap_recv_fault(struct soap *soap, int check) && (soap->error != SOAP_TAG_MISMATCH || soap->level != 2)) return soap->error; } + else if (soap->version == 0) /* check == 1 but no SOAP: do not parse SOAP Fault */ + return SOAP_OK; soap->error = SOAP_OK; if (soap_getfault(soap)) { /* check flag set: check if SOAP Fault is present, if not just return */ @@ -15929,8 +16567,9 @@ soap_send_empty_response(struct soap *soap, int httpstatuscode) { soap->count = 0; if ((m & SOAP_IO) == SOAP_IO_CHUNK) soap->omode = (m & ~SOAP_IO) | SOAP_IO_BUFFER; - if (!soap_response(soap, httpstatuscode) && !soap_end_send(soap)) - soap->error = SOAP_STOP; /* stops the server's processing of request */ + soap_response(soap, httpstatuscode); + soap_end_send(soap); /* force end of sends */ + soap->error = SOAP_STOP; /* stops the server (from returning a response) */ soap->omode = m; } return soap_closesock(soap); @@ -15947,7 +16586,13 @@ SOAP_FMAC2 soap_recv_empty_response(struct soap *soap) { if (!(soap->omode & SOAP_IO_UDP)) { if (!soap_begin_recv(soap)) + { +#ifndef WITH_LEAN + if (soap->body) + soap_get_http_body(soap, NULL); /* read (empty?) HTTP body and discard */ +#endif soap_end_recv(soap); + } else if (soap->error == SOAP_NO_DATA || soap->error == 202) soap->error = SOAP_OK; } @@ -15962,11 +16607,16 @@ soap_recv_empty_response(struct soap *soap) static const char* soap_strerror(struct soap *soap) { register int err = soap->errnum; + *soap->msgbuf = '\0'; if (err) { #ifndef WIN32 # ifdef HAVE_STRERROR_R - strerror_r(err, soap->msgbuf, sizeof(soap->msgbuf)); +# ifdef _GNU_SOURCE + return strerror_r(err, soap->msgbuf, sizeof(soap->msgbuf)); /* GNU-specific */ +# else + strerror_r(err, soap->msgbuf, sizeof(soap->msgbuf)); /* XSI-compliant */ +# endif # else return strerror(err); # endif @@ -16005,10 +16655,24 @@ soap_strerror(struct soap *soap) su = 'u'; } if (rt) - sprintf(s + strlen(s), " (%d%cs receive delay)", rt, ru); + { +#ifdef HAVE_SNPRINTF + size_t l = strlen(s); + soap_snprintf(s + l, sizeof(soap->msgbuf) - l, " (%d%cs recv delay)", rt, ru); +#else + sprintf(s + strlen(s), " (%d%cs recv delay)", rt, ru); +#endif + } if (st) + { +#ifdef HAVE_SNPRINTF + size_t l = strlen(s); + soap_snprintf(s + l, sizeof(soap->msgbuf) - l, " (%d%cs send delay)", st, su); +#else sprintf(s + strlen(s), " (%d%cs send delay)", st, su); #endif + } +#endif } return soap->msgbuf; } @@ -16175,7 +16839,9 @@ char* SOAP_FMAC2 soap_sprint_fault(struct soap *soap, char *buf, size_t len) { if (soap_check_state(soap)) - strncpy(buf, "Error: soap struct not initialized", len); + { strncpy(buf, "Error: soap struct not initialized", len); + buf[len - 1] = '\0'; + } else if (soap->error) { const char **c, *v = NULL, *s, *d; c = soap_faultcode(soap); diff --git a/dep/gsoap/stdsoap2.h b/dep/gsoap/stdsoap2.h index 87185924135..31aece07f1a 100644 --- a/dep/gsoap/stdsoap2.h +++ b/dep/gsoap/stdsoap2.h @@ -1,10 +1,10 @@ /* - stdsoap2.h 2.8.10 + stdsoap2.h 2.8.17r gSOAP runtime engine gSOAP XML Web services tools -Copyright (C) 2000-2012, Robert van Engelen, Genivia Inc., All Rights Reserved. +Copyright (C) 2000-2013, Robert van Engelen, Genivia Inc., All Rights Reserved. This part of the software is released under ONE of the following licenses: GPL, or the gSOAP public license, or Genivia's license for commercial use. -------------------------------------------------------------------------------- @@ -24,7 +24,7 @@ WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Initial Developer of the Original Code is Robert A. van Engelen. -Copyright (C) 2000-2012, Robert van Engelen, Genivia Inc., All Rights Reserved. +Copyright (C) 2000-2013, Robert van Engelen, Genivia Inc., All Rights Reserved. -------------------------------------------------------------------------------- GPL license. @@ -51,7 +51,7 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com -------------------------------------------------------------------------------- */ -#define GSOAP_VERSION 20810 +#define GSOAP_VERSION 20817 #ifdef WITH_SOAPDEFS_H # include "soapdefs.h" /* include user-defined stuff */ @@ -216,7 +216,9 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com # define WITH_LEAN # define HAVE_SSCANF # elif defined(WIN32) -# define HAVE_SNPRINTF +# if _MSC_VER >= 1400 +# define HAVE_SNPRINTF +# endif # define HAVE_STRRCHR # define HAVE_STRTOD # define HAVE_SSCANF @@ -225,8 +227,8 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com # define HAVE_STRTOL # define HAVE_STRTOUL # if _MSC_VER >= 1300 -# define HAVE_STRTOLL // use _strtoi64 -# define HAVE_STRTOULL // use _strtoui64 +# define HAVE_STRTOLL /* use _strtoi64 */ +# define HAVE_STRTOULL /* use _strtoui64 */ # endif # define HAVE_SYS_TIMEB_H # define HAVE_FTIME @@ -264,11 +266,14 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com # define HAVE_STRTOUL # define HAVE_RAND_R # define HAVE_GMTIME_R +# define HAVE_TM_GMTOFF +# define HAVE_GETTIMEOFDAY # define HAVE_LOCALTIME_R # define HAVE_STRERROR_R # define HAVE_TIMEGM # define HAVE_WCTOMB # define HAVE_MBTOWC +# define HAVE_INTTYPES_H # elif defined(_AIX43) # define HAVE_SNPRINTF # define HAVE_STRRCHR @@ -482,10 +487,6 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com # define HAVE_STRERROR_R # define HAVE_WCTOMB # define HAVE_MBTOWC -# define LONG64 long -# define ULONG64 unsigned LONG64 -# define SOAP_LONG_FORMAT "%ld" -# define SOAP_ULONG_FORMAT "%lu" # elif defined(SUN_OS) # define HAVE_SNPRINTF # define HAVE_STRRCHR @@ -543,7 +544,7 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com #endif /* native Win, HP-UX, and AIX compilers don't like empty structs */ -#if defined(WIN32) || defined(HP_UX) || defined(_AIX41) || defined(_AIX43) || defined(VXWORKS) +#if defined(WIN32) || defined(HP_UX) || defined(_AIX) || defined(AIX) || defined(VXWORKS) # define WITH_NOEMPTYSTRUCT #endif @@ -579,7 +580,7 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com # undef HAVE_SPRINTF_L #endif -#ifdef TANDEM_NONSTOP +#ifdef TANDEM_NONSTOP /* Support for Guardian */ # define SOAP_BUFLEN (32767) /*# define WITH_NOSTDLIB */ /* uncommment to remove stdlib dependences */ # define WITH_NOIO /* no IO dependences, e.g. remove TCP/IP */ @@ -610,6 +611,9 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com # include <cextdecs.h(TIME,FILE_CLOSE_,AWAITIOX,DELAY,FILEINFO,FILE_GETINFO_)> # define INET_ERROR 4294967295 #pragma list +#elif defined(__TANDEM) /* Support for OSS */ +# define int32_t int +# define SOAP_BUFLEN (32767) #endif #ifndef WITH_NOSTDLIB @@ -723,17 +727,18 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com # include <io.h> # include <fcntl.h> # endif -// When you get macro redefinition errors when compiling the code below: -// try arrange your include list that <windows.h> is included after "stdsoap2.h" -// or define _WINSOCKAPI_ first: -// #define _WINSOCKAPI_ // stops windows.h including winsock.h -// #include <windows.h> -// #include "stdsoap2.h" -# include <winsock2.h> /* Visual Studio 2005 users: install Platform SDK (R2) */ +// When you get macro redefinition errors when compiling the code below, then: +// a) try arrange your includes so <windows.h> is included after "stdsoap2.h" +// b) or define _WINSOCKAPI_ first: +// #define _WINSOCKAPI_ // stops windows.h including winsock.h +// #include <windows.h> +// #include "stdsoap2.h" +// c) or compile with the -DWIN32_LEAN_AND_MEAN switch +# include <winsock2.h> // Visual Studio 2005 users: install Platform SDK (R2) # include <ws2tcpip.h> -// # define _WSPIAPI_COUNTOF /* DEV NOTE: enble to fix problems with VC6 */ +// # define _WSPIAPI_COUNTOF // DEV NOTE: enble to fix problems with VC6 // # include <wspiapi.h> -# include <ws2spi.h> /* DEV NOTE: replaces older wspiapi.h above */ +# include <ws2spi.h> // DEV NOTE: replaces older wspiapi.h above # ifdef WITH_IPV6 # define SOAP_GAI_STRERROR gai_strerrorA # endif @@ -815,14 +820,16 @@ A commercial use license is available from Genivia, Inc., contact@genivia.com # endif #endif -/* #define DEBUG */ /* Uncomment to debug sending (in file SENT.log) receiving (in file RECV.log) and messages (in file TEST.log) */ +/* #define DEBUG */ /* Uncomment to debug sending (in file SENT.log) receiving (in file RECV.log) and internal operations (in file TEST.log) */ + +/* #define DEBUG_STAMP */ /* Uncomment to debug sending (in file SENT.log) receiving (in file RECV.log) and time-stamped operations (in file TEST.log) */ #ifdef __cplusplus extern "C" { #endif /* Portability: define SOAP_SOCKLEN_T */ -#if defined(_AIX) +#if defined(_AIX) || defined(AIX) # if defined(_AIX43) # define SOAP_SOCKLEN_T socklen_t # else @@ -830,7 +837,7 @@ extern "C" { # endif #elif defined(SOCKLEN_T) # define SOAP_SOCKLEN_T SOCKLEN_T -#elif defined(__socklen_t_defined) || defined(_SOCKLEN_T) || defined(CYGWIN) || defined(FREEBSD) || defined(__FreeBSD__) || defined(OPENBSD) || defined(__QNX__) || defined(QNX) || defined(OS390) +#elif defined(__socklen_t_defined) || defined(_SOCKLEN_T) || defined(CYGWIN) || defined(FREEBSD) || defined(__FreeBSD__) || defined(OPENBSD) || defined(__QNX__) || defined(QNX) || defined(OS390) || defined(__ANDROID__) # define SOAP_SOCKLEN_T socklen_t #elif defined(IRIX) || defined(WIN32) || defined(__APPLE__) || defined(SUN_OS) || defined(OPENSERVER) || defined(TRU64) || defined(VXWORKS) || defined(HP_UX) # define SOAP_SOCKLEN_T int @@ -868,12 +875,29 @@ extern "C" { # define SOAP_CHK_EOF (soap->error ? soap->error : SOAP_EOF) #endif +#ifdef __cplusplus +# ifndef __STDC_FORMAT_MACROS +# define __STDC_FORMAT_MACROS +# endif +#endif + #if defined(SYMBIAN) # define LONG64 long # define ULONG64 unsigned LONG64 #elif !defined(WIN32) || defined(CYGWIN) || defined(__GLIBC__) || defined(__GNU__) # ifndef LONG64 -# if defined(HAVE_STDINT_H) +# if defined(HAVE_INTTYPES_H) +# ifdef HAVE_STDINT_H +# include <stdint.h> +# endif +# include <inttypes.h> +# define LONG64 int64_t +# define ULONG64 uint64_t +# elif defined(HAVE_SYS_INTTYPES_H) +# include <sys/inttypes.h> +# define LONG64 int64_t +# define ULONG64 uint64_t +# elif defined(HAVE_STDINT_H) # include <stdint.h> # define LONG64 int64_t # define ULONG64 uint64_t @@ -905,6 +929,18 @@ extern "C" { # define ULONG64 unsigned LONG64 #endif +#ifdef PRId64 +# ifndef SOAP_LONG_FORMAT +# define SOAP_LONG_FORMAT "%" PRId64 +# endif +#endif + +#ifdef PRIu64 +# ifndef SOAP_ULONG_FORMAT +# define SOAP_ULONG_FORMAT "%" PRIu64 +# endif +#endif + #ifndef SOAP_LONG_FORMAT # define SOAP_LONG_FORMAT "%lld" /* printf format for 64 bit ints */ #endif @@ -919,7 +955,7 @@ extern "C" { # define soap_int32 long #elif defined(PALM) # define soap_int32 Int32 -#elif defined(_AIX) +#elif defined(_AIX) || defined(AIX) # if defined(_AIX43) # define soap_int32 int32_t # else @@ -1035,6 +1071,12 @@ extern "C" { # define SOAP_INDEX_TEST (2) #endif +/* Max number of EINTR while poll/select on a socket */ +/* Each EINTR can lengthen the I/O blocking time by at most one second */ +#ifndef SOAP_MAXEINTR +# define SOAP_MAXEINTR (10) +#endif + /* Max iterations in soap_serve() to keep server connection alive */ #ifndef SOAP_MAXKEEPALIVE # define SOAP_MAXKEEPALIVE (100) @@ -1047,6 +1089,14 @@ extern "C" { # define SOAP_MAXARRAYSIZE (1000000) #endif +/* Trusted max size of inbound DIME data. + Increase if necessary to allow larger attachments, or decrease when server + resources are limited. +*/ +#ifndef SOAP_MAXDIMESIZE +# define SOAP_MAXDIMESIZE (8388608) /* 8 MB */ +#endif + #ifdef VXWORKS # ifdef WMW_RPM_IO # include "httpLib.h" @@ -1180,13 +1230,15 @@ extern const char soap_base64o[], soap_base64i[]; #ifdef HAVE_SNPRINTF # ifdef WIN32 -# define soap_snprintf _snprintf +# define soap_snprintf(buf, len, ...) (_snprintf((buf), (len), __VA_ARGS__), (buf)[(len)-1] = '\0') # else # define soap_snprintf snprintf # endif #endif -/* gSOAP error codes */ +/* gSOAP status/error codes */ + +typedef soap_int32 soap_status; #define SOAP_EOF EOF #define SOAP_ERR EOF @@ -1306,14 +1358,14 @@ typedef soap_int32 soap_mode; #define SOAP_ENC_ZLIB 0x00000400 #define SOAP_ENC_SSL 0x00000800 -#define SOAP_XML_STRICT 0x00001000 /* in: strict validation */ +#define SOAP_XML_STRICT 0x00001000 /* in: strict validation */ #define SOAP_XML_INDENT 0x00002000 /* out: emit indented XML */ -#define SOAP_XML_IGNORENS 0x00004000 /* in: ignore namespaces */ +#define SOAP_XML_IGNORENS 0x00004000 /* in: ignore namespaces */ #define SOAP_XML_DEFAULTNS 0x00008000 /* out: emit xmlns="..." */ #define SOAP_XML_CANONICAL 0x00010000 /* out: excC14N canonical XML */ -#define SOAP_XML_TREE 0x00020000 /* out: XML tree (no id/ref) */ +#define SOAP_XML_TREE 0x00020000 /* in/out: XML tree (no id/ref) */ #define SOAP_XML_NIL 0x00040000 /* out: NULLs as xsi:nil */ -#define SOAP_XML_NOTYPE 0x00080000 /* out: NULLs as xsi:nil */ +#define SOAP_XML_NOTYPE 0x00080000 /* out: do not add xsi:type */ #define SOAP_DOM_TREE 0x00100000 /* see DOM manual */ #define SOAP_DOM_NODE 0x00200000 @@ -1325,7 +1377,7 @@ typedef soap_int32 soap_mode; #define SOAP_C_NILSTRING 0x08000000 /* serialize empty strings as nil (omitted) */ #define SOAP_XML_DOM 0x10000000 /* enable internal DOM */ -#define SOAP_XML_GRAPH 0x20000000 /* id-ref graph in DOM */ +#define SOAP_XML_GRAPH 0x20000000 /* force id-ref XML graph */ #define SOAP_MIME_POSTCHECK 0x40000000 /* MIME flag (internal) */ @@ -1383,6 +1435,11 @@ typedef soap_int32 soap_mode; /* DEBUG macros */ #ifndef WITH_LEAN +# ifdef DEBUG_STAMP +# ifndef DEBUG +# define DEBUG +# endif +# endif # ifdef DEBUG # ifndef SOAP_DEBUG # define SOAP_DEBUG @@ -1410,20 +1467,40 @@ typedef soap_int32 soap_mode; # define SOAP_FREE(soap, ptr) free(ptr) #endif -#ifndef SOAP_NEW /* use C++ new operator */ -# if (defined(__GNUC__) && (__GNUC__ <= 2) && !defined(__BORLANDC__)) || defined(__clang__) || defined(_AIX) -# define SOAP_NEW(type) new type /* old form w/o parenthesis */ -# else -# define SOAP_NEW(type) new (type) /* prefer with parenthesis */ -# endif +#if defined(__GNUC__) && (__GNUC__ <= 2) +# define SOAP_NOTHROW +#elif !defined(WITH_LEAN) && !defined(WITH_COMPAT) && !defined(SOAP_NOTHROW) +# define SOAP_NOTHROW (std::nothrow) +#else +# define SOAP_NOTHROW #endif -#ifndef SOAP_PLACEMENT_NEW -# define SOAP_PLACEMENT_NEW(buf, type) new (buf) type +#if (defined(__GNUC__) && (__GNUC__ <= 2) && !defined(__BORLANDC__)) || defined(__clang__) || defined(_AIX) || defined(AIX) +/* old form w/o parenthesis */ +# ifndef SOAP_NEW +# define SOAP_NEW(type) new SOAP_NOTHROW type +# endif +# ifndef SOAP_NEW_ARRAY +# define SOAP_NEW_ARRAY(type, n) new SOAP_NOTHROW type[n] +# endif +# ifndef SOAP_PLACEMENT_NEW +# define SOAP_PLACEMENT_NEW(buf, type) new (buf) type +# endif +#else +/* new form with parenthesis */ +# ifndef SOAP_NEW +# define SOAP_NEW(type) new SOAP_NOTHROW (type) +# endif +# ifndef SOAP_NEW_ARRAY +# define SOAP_NEW_ARRAY(type, n) new SOAP_NOTHROW type[n] +# endif +# ifndef SOAP_PLACEMENT_NEW +# define SOAP_PLACEMENT_NEW(buf, type) new (buf) (type) +# endif #endif #ifndef SOAP_NEW_COPY /* use C++ new operator for ::copy() */ -# define SOAP_NEW_COPY(clas) new clas /* prefer w/o parenthesis */ +# define SOAP_NEW_COPY(clas) new SOAP_NOTHROW clas #endif #ifndef SOAP_DELETE /* use C++ delete operator */ @@ -1439,17 +1516,53 @@ typedef soap_int32 soap_mode; # define SOAP_MESSAGE fprintf # endif # ifndef DBGLOG -# define DBGLOG(DBGFILE, CMD) \ +# ifdef DEBUG_STAMP +# ifdef WIN32 +# define DBGLOG(DBGFILE, CMD) \ { if (soap)\ { if (!soap->fdebug[SOAP_INDEX_##DBGFILE])\ soap_open_logfile((struct soap*)soap, SOAP_INDEX_##DBGFILE);\ if (soap->fdebug[SOAP_INDEX_##DBGFILE])\ { FILE *fdebug = soap->fdebug[SOAP_INDEX_##DBGFILE];\ + SYSTEMTIME _localTime;\ + ::GetLocalTime(&_localTime); \ + fprintf(fdebug, "%02d%02d%02d %02d:%02d:%02d.%03d|", _localTime.wYear%100, _localTime.wMonth, _localTime.wDay, _localTime.wHour, _localTime.wMinute, _localTime.wSecond, _localTime.wMilliseconds);\ CMD;\ fflush(fdebug);\ }\ }\ } +# else +# define DBGLOG(DBGFILE, CMD) \ +{ if (soap)\ + { if (!soap->fdebug[SOAP_INDEX_##DBGFILE])\ + soap_open_logfile((struct soap*)soap, SOAP_INDEX_##DBGFILE);\ + if (soap->fdebug[SOAP_INDEX_##DBGFILE])\ + { FILE *fdebug = soap->fdebug[SOAP_INDEX_##DBGFILE];\ + struct timeval _tv;\ + struct tm _tm;\ + gettimeofday(&_tv, NULL);\ + localtime_r(&_tv.tv_sec, &_tm);\ + fprintf(fdebug, "%02d%02d%02d %02d:%02d:%02d.%06d|", _tm.tm_year%100, _tm.tm_mon+1, _tm.tm_mday, _tm.tm_hour, _tm.tm_min, _tm.tm_sec, _tv.tv_usec);\ + CMD;\ + fflush(fdebug);\ + }\ + }\ +} +# endif +# else +# define DBGLOG(DBGFILE, CMD) \ +{ if (soap)\ + { if (!soap->fdebug[SOAP_INDEX_##DBGFILE])\ + soap_open_logfile((struct soap*)soap, SOAP_INDEX_##DBGFILE);\ + if (soap->fdebug[SOAP_INDEX_##DBGFILE])\ + { FILE *fdebug = soap->fdebug[SOAP_INDEX_##DBGFILE];\ + CMD;\ + fflush(fdebug);\ + }\ + }\ +} +# endif # endif # ifndef DBGMSG # define DBGMSG(DBGFILE, MSG, LEN) \ @@ -1465,9 +1578,9 @@ typedef soap_int32 soap_mode; # endif # ifndef DBGFUN # define DBGFUN(FNAME) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s()\n", __FILE__, __LINE__, FNAME)) -# define DBGFUN1(FNAME, FMT, ARG) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s("FMT")\n", __FILE__, __LINE__, FNAME, (ARG))) -# define DBGFUN2(FNAME, FMT1, ARG1, FMT2, ARG2) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s("FMT1", "FMT2")\n", __FILE__, __LINE__, FNAME, (ARG1), (ARG2))) -# define DBGFUN3(FNAME, FMT1, ARG1, FMT2, ARG2, FMT3, ARG3) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s("FMT1", "FMT2", "FMT3")\n", __FILE__, __LINE__, FNAME, (ARG1), (ARG2), (ARG3))) +# define DBGFUN1(FNAME, FMT, ARG) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s(" FMT ")\n", __FILE__, __LINE__, FNAME, (ARG))) +# define DBGFUN2(FNAME, FMT1, ARG1, FMT2, ARG2) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s(" FMT1 ", " FMT2 ")\n", __FILE__, __LINE__, FNAME, (ARG1), (ARG2))) +# define DBGFUN3(FNAME, FMT1, ARG1, FMT2, ARG2, FMT3, ARG3) DBGLOG(TEST, SOAP_MESSAGE(fdebug, "%s(%d): %s(" FMT1 ", " FMT2 ", " FMT3 ")\n", __FILE__, __LINE__, FNAME, (ARG1), (ARG2), (ARG3))) # endif # ifndef DBGHEX # define DBGHEX(DBGFILE, MSG, LEN) \ @@ -1533,7 +1646,7 @@ struct soap_plist const struct soap_array *array; int type; int id; - char mark1; + char mark1; /* 0=single-ref, 1=embedded-multi-ref (SOAP1.1), 2=multi-ref, 3=attachment */ char mark2; }; @@ -1819,7 +1932,7 @@ extern "C" { struct SOAP_STD_API soap { short state; /* 0 = uninitialized, 1 = initialized, 2 = copy of another soap struct */ - short version; /* 1 = SOAP1.1 and 2 = SOAP1.2 (set automatically from namespace URI in nsmap table) */ + short version; /* 1 = SOAP1.1 and 2 = SOAP1.2 (set automatically from namespace URI in nsmap table), 0 indicates non-SOAP content */ soap_mode mode; soap_mode imode; soap_mode omode; @@ -1828,7 +1941,7 @@ struct SOAP_STD_API soap const char *dime_id_format; /* user-definable format string for integer DIME id (<SOAP_TAGLEN chars) */ const char *http_version; /* HTTP version used "1.0" or "1.1" */ const char *http_content; /* optional custom response content type (with SOAP_FILE) */ - const char *encodingStyle; /* default = NULL which means that SOAP encoding is used */ + const char *encodingStyle; /* default = "" which means that SOAP encoding is used */ const char *actor; /* SOAP-ENV:actor or role attribute value */ const char *lang; /* xml:lang attribute value of SOAP-ENV:Text */ int recv_timeout; /* when > 0, gives socket recv timeout in seconds, < 0 in usec */ @@ -1846,10 +1959,12 @@ struct SOAP_STD_API soap struct soap_blist *blist; /* block allocation stack */ struct soap_clist *clist; /* class instance allocation list */ void *alist; /* memory allocation (malloc) list */ +#if !defined(WITH_LEAN) || !defined(WITH_NOIDREF) struct soap_ilist *iht[SOAP_IDHASH]; struct soap_plist *pht[SOAP_PTRHASH]; struct soap_pblk *pblk; /* plist block allocation */ short pidx; /* plist block allocation */ +#endif struct SOAP_ENV__Header *header; struct SOAP_ENV__Fault *fault; int idnum; @@ -1941,7 +2056,7 @@ struct SOAP_STD_API soap size_t buflen; /* length of soap.buf[] content */ soap_wchar ahead; /* parser lookahead */ short cdata; /* CDATA parser state */ - short body; /* parsed XML element has a body or not */ + short body; /* HTTP or XML element has a body (1) or not (0) */ unsigned int level; /* XML nesting level */ size_t count; /* message length counter */ size_t length; /* message length as set by HTTP header */ @@ -2017,7 +2132,7 @@ struct SOAP_STD_API soap int cookie_max; #endif #ifndef WITH_NOIO - int ipv6_multicast_if; /* in6addr->sin6_scope_id IPv6 value */ + unsigned int ipv6_multicast_if; /* in_addr_t in6addr->sin6_scope_id IPv6 value */ char* ipv4_multicast_if; /* IP_MULTICAST_IF IPv4 setsockopt interface_addr */ unsigned char ipv4_multicast_ttl; /* IP_MULTICAST_TTL value 0..255 */ #ifdef WITH_IPV6 @@ -2057,6 +2172,7 @@ struct SOAP_STD_API soap #endif unsigned short ssl_flags; const char *keyfile; + const char *keyid; const char *password; const char *cafile; const char *capath; @@ -2139,9 +2255,7 @@ struct soap_plugin void (*fdelete)(struct soap *soap, struct soap_plugin *p); /* should delete fields of plugin only and not free(p) */ }; -#ifndef WITH_NONAMESPACES extern SOAP_NMAC struct Namespace namespaces[]; -#endif #ifndef WITH_LEAN # define soap_get0(soap) (((soap)->bufidx>=(soap)->buflen && soap_recv(soap)) ? EOF : (unsigned char)(soap)->buf[(soap)->bufidx]) @@ -2151,6 +2265,9 @@ soap_wchar soap_get0(struct soap*); soap_wchar soap_get1(struct soap*); #endif +#define SOAP_XSTRINGIFY(s) SOAP_STRINGIFY(s) +#define SOAP_STRINGIFY(s) #s + #define soap_versioning_paste(name, ext) name##_LIBRARY_VERSION_REQUIRED_##ext #define soap_versioning_ext(name, ext) soap_versioning_paste(name, ext) #define soap_versioning(name) soap_versioning_ext(name, GSOAP_VERSION) @@ -2195,13 +2312,13 @@ soap_wchar soap_get1(struct soap*); SOAP_FMAC1 unsigned long SOAP_FMAC2 soap_strtoul(const char *s, char **t, int b); #endif -#ifdef WIN32 +#if defined(WIN32) && !defined(__MINGW32__) # define soap_strtoll _strtoi64 #else # define soap_strtoll strtoll #endif -#ifdef WIN32 +#if defined(WIN32) && !defined(__MINGW32__) # define soap_strtoull _strtoui64 #else # define soap_strtoull strtoull @@ -2229,8 +2346,6 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_rand(void); # define soap_lookup_type(s, i) (0) # define soap_getindependent(s) (0) # define soap_putindependent(s) (0) -# define soap_getelement(s, n) (n) -# define soap_putelement(s, p, t, i, n) (0) # define soap_markelement(s, p, n) (0) #endif @@ -2240,7 +2355,6 @@ typedef void soap_walker(struct soap*, void*, int, const char*, const char*); SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap *soap); SOAP_FMAC5 int SOAP_FMAC6 soap_serve_request(struct soap *soap); -#ifndef WITH_NOGLOBAL SOAP_FMAC3 void SOAP_FMAC4 soap_header(struct soap*); SOAP_FMAC3 void SOAP_FMAC4 soap_fault(struct soap*); SOAP_FMAC3 const char** SOAP_FMAC4 soap_faultcode(struct soap*); @@ -2250,7 +2364,6 @@ SOAP_FMAC3 const char** SOAP_FMAC4 soap_faultdetail(struct soap*); SOAP_FMAC3 const char* SOAP_FMAC4 soap_check_faultsubcode(struct soap*); SOAP_FMAC3 const char* SOAP_FMAC4 soap_check_faultdetail(struct soap*); SOAP_FMAC3 void SOAP_FMAC4 soap_serializefault(struct soap*); -#endif SOAP_FMAC1 void SOAP_FMAC2 soap_serializeheader(struct soap*); SOAP_FMAC1 int SOAP_FMAC2 soap_getheader(struct soap*); @@ -2267,8 +2380,16 @@ SOAP_FMAC1 SOAP_SOCKET SOAP_FMAC2 soap_accept(struct soap*); SOAP_FMAC1 int SOAP_FMAC2 soap_ssl_accept(struct soap*); SOAP_FMAC1 const char * SOAP_FMAC2 soap_ssl_error(struct soap*, int); -SOAP_FMAC1 int SOAP_FMAC2 soap_ssl_server_context(struct soap*, unsigned short, const char*, const char*, const char*, const char*, const char*, const char*, const char*); -SOAP_FMAC1 int SOAP_FMAC2 soap_ssl_client_context(struct soap*, unsigned short, const char*, const char*, const char*, const char*, const char*); +#if defined(VXWORKS) && defined(WM_SECURE_KEY_STORAGE) +SOAP_FMAC1 int SOAP_FMAC2 soap_ssl_server_context(struct soap *soap, unsigned short flags, const char *keyfile, const char *keyid, const char *password, const char *cafile, const char *capath, const char *dhfile, const char *randfile, const char *sid); +#else +SOAP_FMAC1 int SOAP_FMAC2 soap_ssl_server_context(struct soap *soap, unsigned short flags, const char *keyfile, const char *password, const char *cafile, const char *capath, const char *dhfile, const char *randfile, const char *sid); +#endif +#if defined(VXWORKS) && defined(WM_SECURE_KEY_STORAGE) +SOAP_FMAC1 int SOAP_FMAC2 soap_ssl_client_context(struct soap *soap, unsigned short flags, const char *keyfile, const char *keyid, const char *password, const char *cafile, const char *capath, const char *randfile); +#else +SOAP_FMAC1 int SOAP_FMAC2 soap_ssl_client_context(struct soap *soap, unsigned short flags, const char *keyfile, const char *password, const char *cafile, const char *capath, const char *randfile); +#endif SOAP_FMAC1 int SOAP_FMAC2 soap_puthttphdr(struct soap*, int status, size_t count); @@ -2333,6 +2454,7 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_begin_count(struct soap*); SOAP_FMAC1 int SOAP_FMAC2 soap_end_count(struct soap*); SOAP_FMAC1 int SOAP_FMAC2 soap_begin_send(struct soap*); SOAP_FMAC1 int SOAP_FMAC2 soap_end_send(struct soap*); +SOAP_FMAC1 int SOAP_FMAC2 soap_end_send_flush(struct soap*); SOAP_FMAC1 const struct soap_code_map* SOAP_FMAC2 soap_code(const struct soap_code_map*, const char*); SOAP_FMAC1 long SOAP_FMAC2 soap_code_int(const struct soap_code_map*, const char*, long); @@ -2385,6 +2507,7 @@ SOAP_FMAC1 struct soap *SOAP_FMAC2 soap_copy_context(struct soap*, const struct SOAP_FMAC1 void SOAP_FMAC2 soap_copy_stream(struct soap*, struct soap*); SOAP_FMAC1 void SOAP_FMAC2 soap_free_stream(struct soap*); SOAP_FMAC1 void SOAP_FMAC2 soap_versioning(soap_init)(struct soap*, soap_mode, soap_mode); +SOAP_FMAC1 void SOAP_FMAC2 soap_initialize(struct soap*); SOAP_FMAC1 void SOAP_FMAC2 soap_done(struct soap*); SOAP_FMAC1 void SOAP_FMAC2 soap_cleanup(struct soap*); SOAP_FMAC1 void SOAP_FMAC2 soap_begin(struct soap*); @@ -2443,6 +2566,7 @@ SOAP_FMAC1 wchar_t* SOAP_FMAC2 soap_wstring_in(struct soap*, int, long, long); SOAP_FMAC1 int SOAP_FMAC2 soap_match_namespace(struct soap*, const char *, const char*, size_t n1, size_t n2); +SOAP_FMAC1 void SOAP_FMAC2 soap_set_version(struct soap*, short); SOAP_FMAC1 int SOAP_FMAC2 soap_set_namespaces(struct soap*, const struct Namespace*); SOAP_FMAC1 void SOAP_FMAC2 soap_set_local_namespaces(struct soap*); @@ -2467,9 +2591,9 @@ SOAP_FMAC1 void SOAP_FMAC2 soap_end_block(struct soap*, struct soap_blist*); SOAP_FMAC1 void SOAP_FMAC2 soap_update_pointers(struct soap *soap, char *start, char *end, char *p1, char *p2); SOAP_FMAC1 int SOAP_FMAC2 soap_envelope_begin_out(struct soap*); -SOAP_FMAC1 int soap_envelope_end_out(struct soap*); +SOAP_FMAC1 int SOAP_FMAC2 soap_envelope_end_out(struct soap*); -SOAP_FMAC1 char * SOAP_FMAC2 soap_get_http_body(struct soap*); +SOAP_FMAC1 char * SOAP_FMAC2 soap_get_http_body(struct soap*, size_t *len); SOAP_FMAC1 int SOAP_FMAC2 soap_envelope_begin_in(struct soap*); SOAP_FMAC1 int SOAP_FMAC2 soap_envelope_end_in(struct soap*); @@ -2598,7 +2722,7 @@ SOAP_FMAC1 int SOAP_FMAC2 soap_outwliteral(struct soap*, const char *tag, wchar_ #ifndef WITH_LEANER SOAP_FMAC1 int SOAP_FMAC2 soap_attachment(struct soap *, const char*, int, const void*, const struct soap_array*, const char*, const char*, const char*, int, const char*, int); -SOAP_FMAC1 int SOAP_FMAC2 soap_move(struct soap*, long); +SOAP_FMAC1 int SOAP_FMAC2 soap_move(struct soap*, size_t); SOAP_FMAC1 size_t SOAP_FMAC2 soap_tell(struct soap*); SOAP_FMAC1 char* SOAP_FMAC2 soap_dime_option(struct soap*, unsigned short, const char*); SOAP_FMAC1 int SOAP_FMAC2 soap_getdimehdr(struct soap*); @@ -2628,9 +2752,11 @@ SOAP_FMAC1 const char* SOAP_FMAC2 soap_attr_value(struct soap *soap, const char SOAP_FMAC1 int SOAP_FMAC2 soap_set_attr(struct soap *soap, const char *name, const char *value, int flag); SOAP_FMAC1 void SOAP_FMAC2 soap_clr_attr(struct soap *soap); +SOAP_FMAC1 const char* SOAP_FMAC2 soap_url(struct soap *soap, const char*, const char*); +SOAP_FMAC1 size_t SOAP_FMAC2 soap_encode_url(const char*, char*, size_t); +SOAP_FMAC1 const char* SOAP_FMAC2 soap_encode_url_string(struct soap*, const char*); #ifdef WITH_COOKIES SOAP_FMAC1 void SOAP_FMAC2 soap_getcookies(struct soap *soap, const char *val); -SOAP_FMAC1 size_t SOAP_FMAC2 soap_encode_cookie(const char*, char*, size_t); SOAP_FMAC1 extern struct soap_cookie* SOAP_FMAC2 soap_set_cookie(struct soap*, const char*, const char*, const char*, const char*); SOAP_FMAC1 extern struct soap_cookie* SOAP_FMAC2 soap_cookie(struct soap*, const char*, const char*, const char*); SOAP_FMAC1 extern char* SOAP_FMAC2 soap_cookie_value(struct soap*, const char*, const char*, const char*); diff --git a/dep/jemalloc/COPYING b/dep/jemalloc/COPYING index 019e8132275..bdda0feb9e5 100644 --- a/dep/jemalloc/COPYING +++ b/dep/jemalloc/COPYING @@ -1,10 +1,10 @@ Unless otherwise specified, files in the jemalloc source distribution are subject to the following license: -------------------------------------------------------------------------------- -Copyright (C) 2002-2013 Jason Evans <jasone@canonware.com>. +Copyright (C) 2002-2014 Jason Evans <jasone@canonware.com>. All rights reserved. Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved. -Copyright (C) 2009-2013 Facebook, Inc. All rights reserved. +Copyright (C) 2009-2014 Facebook, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/dep/jemalloc/ChangeLog b/dep/jemalloc/ChangeLog index fc096d8f42f..d56ee999e69 100644 --- a/dep/jemalloc/ChangeLog +++ b/dep/jemalloc/ChangeLog @@ -3,8 +3,131 @@ bug fixes are all mentioned, but internal enhancements are omitted here for brevity (even though they are more fun to write about). Much more detail can be found in the git revision history: - http://www.canonware.com/cgi-bin/gitweb.cgi?p=jemalloc.git - git://canonware.com/jemalloc.git + https://github.com/jemalloc/jemalloc + +* 3.6.0 (March 31, 2014) + + This version contains a critical bug fix for a regression present in 3.5.0 and + 3.5.1. + + Bug fixes: + - Fix a regression in arena_chunk_alloc() that caused crashes during + small/large allocation if chunk allocation failed. In the absence of this + bug, chunk allocation failure would result in allocation failure, e.g. NULL + return from malloc(). This regression was introduced in 3.5.0. + - Fix backtracing for gcc intrinsics-based backtracing by specifying + -fno-omit-frame-pointer to gcc. Note that the application (and all the + libraries it links to) must also be compiled with this option for + backtracing to be reliable. + - Use dss allocation precedence for huge allocations as well as small/large + allocations. + - Fix test assertion failure message formatting. This bug did not manifect on + x86_64 systems because of implementation subtleties in va_list. + - Fix inconsequential test failures for hash and SFMT code. + + New features: + - Support heap profiling on FreeBSD. This feature depends on the proc + filesystem being mounted during heap profile dumping. + +* 3.5.1 (February 25, 2014) + + This version primarily addresses minor bugs in test code. + + Bug fixes: + - Configure Solaris/Illumos to use MADV_FREE. + - Fix junk filling for mremap(2)-based huge reallocation. This is only + relevant if configuring with the --enable-mremap option specified. + - Avoid compilation failure if 'restrict' C99 keyword is not supported by the + compiler. + - Add a configure test for SSE2 rather than assuming it is usable on i686 + systems. This fixes test compilation errors, especially on 32-bit Linux + systems. + - Fix mallctl argument size mismatches (size_t vs. uint64_t) in the stats unit + test. + - Fix/remove flawed alignment-related overflow tests. + - Prevent compiler optimizations that could change backtraces in the + prof_accum unit test. + +* 3.5.0 (January 22, 2014) + + This version focuses on refactoring and automated testing, though it also + includes some non-trivial heap profiling optimizations not mentioned below. + + New features: + - Add the *allocx() API, which is a successor to the experimental *allocm() + API. The *allocx() functions are slightly simpler to use because they have + fewer parameters, they directly return the results of primary interest, and + mallocx()/rallocx() avoid the strict aliasing pitfall that + allocm()/rallocm() share with posix_memalign(). Note that *allocm() is + slated for removal in the next non-bugfix release. + - Add support for LinuxThreads. + + Bug fixes: + - Unless heap profiling is enabled, disable floating point code and don't link + with libm. This, in combination with e.g. EXTRA_CFLAGS=-mno-sse on x64 + systems, makes it possible to completely disable floating point register + use. Some versions of glibc neglect to save/restore caller-saved floating + point registers during dynamic lazy symbol loading, and the symbol loading + code uses whatever malloc the application happens to have linked/loaded + with, the result being potential floating point register corruption. + - Report ENOMEM rather than EINVAL if an OOM occurs during heap profiling + backtrace creation in imemalign(). This bug impacted posix_memalign() and + aligned_alloc(). + - Fix a file descriptor leak in a prof_dump_maps() error path. + - Fix prof_dump() to close the dump file descriptor for all relevant error + paths. + - Fix rallocm() to use the arena specified by the ALLOCM_ARENA(s) flag for + allocation, not just deallocation. + - Fix a data race for large allocation stats counters. + - Fix a potential infinite loop during thread exit. This bug occurred on + Solaris, and could affect other platforms with similar pthreads TSD + implementations. + - Don't junk-fill reallocations unless usable size changes. This fixes a + violation of the *allocx()/*allocm() semantics. + - Fix growing large reallocation to junk fill new space. + - Fix huge deallocation to junk fill when munmap is disabled. + - Change the default private namespace prefix from empty to je_, and change + --with-private-namespace-prefix so that it prepends an additional prefix + rather than replacing je_. This reduces the likelihood of applications + which statically link jemalloc experiencing symbol name collisions. + - Add missing private namespace mangling (relevant when + --with-private-namespace is specified). + - Add and use JEMALLOC_INLINE_C so that static inline functions are marked as + static even for debug builds. + - Add a missing mutex unlock in a malloc_init_hard() error path. In practice + this error path is never executed. + - Fix numerous bugs in malloc_strotumax() error handling/reporting. These + bugs had no impact except for malformed inputs. + - Fix numerous bugs in malloc_snprintf(). These bugs were not exercised by + existing calls, so they had no impact. + +* 3.4.1 (October 20, 2013) + + Bug fixes: + - Fix a race in the "arenas.extend" mallctl that could cause memory corruption + of internal data structures and subsequent crashes. + - Fix Valgrind integration flaws that caused Valgrind warnings about reads of + uninitialized memory in: + + arena chunk headers + + internal zero-initialized data structures (relevant to tcache and prof + code) + - Preserve errno during the first allocation. A readlink(2) call during + initialization fails unless /etc/malloc.conf exists, so errno was typically + set during the first allocation prior to this fix. + - Fix compilation warnings reported by gcc 4.8.1. + +* 3.4.0 (June 2, 2013) + + This version is essentially a small bugfix release, but the addition of + aarch64 support requires that the minor version be incremented. + + Bug fixes: + - Fix race-triggered deadlocks in chunk_record(). These deadlocks were + typically triggered by multiple threads concurrently deallocating huge + objects. + + New features: + - Add support for the aarch64 architecture. * 3.3.1 (March 6, 2013) @@ -15,7 +138,7 @@ found in the git revision history: - Fix a locking order bug that could cause deadlock during fork if heap profiling were enabled. - Fix a chunk recycling bug that could cause the allocator to lose track of - whether a chunk was zeroed. On FreeBSD, NetBSD, and OS X, it could cause + whether a chunk was zeroed. On FreeBSD, NetBSD, and OS X, it could cause corruption if allocating via sbrk(2) (unlikely unless running with the "dss:primary" option specified). This was completely harmless on Linux unless using mlockall(2) (and unlikely even then, unless the @@ -47,7 +170,7 @@ found in the git revision history: Bug fixes: - Fix "arenas.extend" mallctl to output the number of arenas. - - Fix chunk_recycyle() to unconditionally inform Valgrind that returned memory + - Fix chunk_recycle() to unconditionally inform Valgrind that returned memory is undefined. - Fix build break on FreeBSD related to alloca.h. diff --git a/dep/jemalloc/README b/dep/jemalloc/README index 7661683bae7..9b268f42288 100644 --- a/dep/jemalloc/README +++ b/dep/jemalloc/README @@ -1,10 +1,14 @@ -jemalloc is a general-purpose scalable concurrent malloc(3) implementation. -This distribution is a "portable" implementation that currently targets -FreeBSD, Linux, Apple OS X, and MinGW. jemalloc is included as the default -allocator in the FreeBSD and NetBSD operating systems, and it is used by the -Mozilla Firefox web browser on Microsoft Windows-related platforms. Depending -on your needs, one of the other divergent versions may suit your needs better -than this distribution. +jemalloc is a general purpose malloc(3) implementation that emphasizes +fragmentation avoidance and scalable concurrency support. jemalloc first came +into use as the FreeBSD libc allocator in 2005, and since then it has found its +way into numerous applications that rely on its predictable behavior. In 2010 +jemalloc development efforts broadened to include developer support features +such as heap profiling, Valgrind integration, and extensive monitoring/tuning +hooks. Modern jemalloc releases continue to be integrated back into FreeBSD, +and therefore versatility remains critical. Ongoing development efforts trend +toward making jemalloc among the best allocators for a broad range of demanding +applications, and eliminating/mitigating weaknesses that have practical +repercussions for real world applications. The COPYING file contains copyright and licensing information. diff --git a/dep/jemalloc/VERSION b/dep/jemalloc/VERSION index 900c82d1043..dace31ba7b6 100644 --- a/dep/jemalloc/VERSION +++ b/dep/jemalloc/VERSION @@ -1 +1 @@ -3.3.1-0-g9ef9d9e8c271cdf14f664b871a8f98c827714784 +3.6.0-0-g46c0af68bd248b04df75e4f92d5fb804c3d75340 diff --git a/dep/jemalloc/include/jemalloc/internal/arena.h b/dep/jemalloc/include/jemalloc/internal/arena.h index f2c18f43543..9d000c03dec 100644 --- a/dep/jemalloc/include/jemalloc/internal/arena.h +++ b/dep/jemalloc/include/jemalloc/internal/arena.h @@ -158,6 +158,7 @@ struct arena_chunk_map_s { }; typedef rb_tree(arena_chunk_map_t) arena_avail_tree_t; typedef rb_tree(arena_chunk_map_t) arena_run_tree_t; +typedef ql_head(arena_chunk_map_t) arena_chunk_mapelms_t; /* Arena chunk header. */ struct arena_chunk_s { @@ -174,11 +175,12 @@ struct arena_chunk_s { size_t nruns_avail; /* - * Number of available run adjacencies. Clean and dirty available runs - * are not coalesced, which causes virtual memory fragmentation. The - * ratio of (nruns_avail-nruns_adjac):nruns_adjac is used for tracking - * this fragmentation. - * */ + * Number of available run adjacencies that purging could coalesce. + * Clean and dirty available runs are not coalesced, which causes + * virtual memory fragmentation. The ratio of + * (nruns_avail-nruns_adjac):nruns_adjac is used for tracking this + * fragmentation. + */ size_t nruns_adjac; /* @@ -404,7 +406,16 @@ void arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind, uint64_t prof_accumbytes); void arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info, bool zero); +#ifdef JEMALLOC_JET +typedef void (arena_redzone_corruption_t)(void *, size_t, bool, size_t, + uint8_t); +extern arena_redzone_corruption_t *arena_redzone_corruption; +typedef void (arena_dalloc_junk_small_t)(void *, arena_bin_info_t *); +extern arena_dalloc_junk_small_t *arena_dalloc_junk_small; +#else void arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info); +#endif +void arena_quarantine_junk_small(void *ptr, size_t usize); void *arena_malloc_small(arena_t *arena, size_t size, bool zero); void *arena_malloc_large(arena_t *arena, size_t size, bool zero); void *arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero); @@ -415,10 +426,18 @@ void arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr, size_t pageind, arena_chunk_map_t *mapelm); void arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, size_t pageind); +#ifdef JEMALLOC_JET +typedef void (arena_dalloc_junk_large_t)(void *, size_t); +extern arena_dalloc_junk_large_t *arena_dalloc_junk_large; +#endif void arena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr); void arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr); -void *arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, +#ifdef JEMALLOC_JET +typedef void (arena_ralloc_junk_large_t)(void *, size_t, size_t); +extern arena_ralloc_junk_large_t *arena_ralloc_junk_large; +#endif +bool arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra, bool zero); void *arena_ralloc(arena_t *arena, void *ptr, size_t oldsize, size_t size, size_t extra, size_t alignment, bool zero, bool try_tcache_alloc, @@ -441,6 +460,7 @@ void arena_postfork_child(arena_t *arena); #ifndef JEMALLOC_ENABLE_INLINE arena_chunk_map_t *arena_mapp_get(arena_chunk_t *chunk, size_t pageind); size_t *arena_mapbitsp_get(arena_chunk_t *chunk, size_t pageind); +size_t arena_mapbitsp_read(size_t *mapbitsp); size_t arena_mapbits_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbits_unallocated_size_get(arena_chunk_t *chunk, size_t pageind); @@ -451,6 +471,7 @@ size_t arena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbits_allocated_get(arena_chunk_t *chunk, size_t pageind); +void arena_mapbitsp_write(size_t *mapbitsp, size_t mapbits); void arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind, size_t size, size_t flags); void arena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind, @@ -471,7 +492,7 @@ size_t arena_bin_index(arena_t *arena, arena_bin_t *bin); unsigned arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info, const void *ptr); prof_ctx_t *arena_prof_ctx_get(const void *ptr); -void arena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx); +void arena_prof_ctx_set(const void *ptr, size_t usize, prof_ctx_t *ctx); void *arena_malloc(arena_t *arena, size_t size, bool zero, bool try_tcache); size_t arena_salloc(const void *ptr, bool demote); void arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr, @@ -498,10 +519,17 @@ arena_mapbitsp_get(arena_chunk_t *chunk, size_t pageind) } JEMALLOC_ALWAYS_INLINE size_t +arena_mapbitsp_read(size_t *mapbitsp) +{ + + return (*mapbitsp); +} + +JEMALLOC_ALWAYS_INLINE size_t arena_mapbits_get(arena_chunk_t *chunk, size_t pageind) { - return (*arena_mapbitsp_get(chunk, pageind)); + return (arena_mapbitsp_read(arena_mapbitsp_get(chunk, pageind))); } JEMALLOC_ALWAYS_INLINE size_t @@ -585,82 +613,89 @@ arena_mapbits_allocated_get(arena_chunk_t *chunk, size_t pageind) } JEMALLOC_ALWAYS_INLINE void +arena_mapbitsp_write(size_t *mapbitsp, size_t mapbits) +{ + + *mapbitsp = mapbits; +} + +JEMALLOC_ALWAYS_INLINE void arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind, size_t size, size_t flags) { - size_t *mapbitsp; + size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); - mapbitsp = arena_mapbitsp_get(chunk, pageind); assert((size & PAGE_MASK) == 0); assert((flags & ~CHUNK_MAP_FLAGS_MASK) == 0); assert((flags & (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == flags); - *mapbitsp = size | CHUNK_MAP_BININD_INVALID | flags; + arena_mapbitsp_write(mapbitsp, size | CHUNK_MAP_BININD_INVALID | flags); } JEMALLOC_ALWAYS_INLINE void arena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind, size_t size) { - size_t *mapbitsp; + size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); + size_t mapbits = arena_mapbitsp_read(mapbitsp); - mapbitsp = arena_mapbitsp_get(chunk, pageind); assert((size & PAGE_MASK) == 0); - assert((*mapbitsp & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) == 0); - *mapbitsp = size | (*mapbitsp & PAGE_MASK); + assert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) == 0); + arena_mapbitsp_write(mapbitsp, size | (mapbits & PAGE_MASK)); } JEMALLOC_ALWAYS_INLINE void arena_mapbits_large_set(arena_chunk_t *chunk, size_t pageind, size_t size, size_t flags) { - size_t *mapbitsp; + size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); + size_t mapbits = arena_mapbitsp_read(mapbitsp); size_t unzeroed; - mapbitsp = arena_mapbitsp_get(chunk, pageind); assert((size & PAGE_MASK) == 0); assert((flags & CHUNK_MAP_DIRTY) == flags); - unzeroed = *mapbitsp & CHUNK_MAP_UNZEROED; /* Preserve unzeroed. */ - *mapbitsp = size | CHUNK_MAP_BININD_INVALID | flags | unzeroed | - CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED; + unzeroed = mapbits & CHUNK_MAP_UNZEROED; /* Preserve unzeroed. */ + arena_mapbitsp_write(mapbitsp, size | CHUNK_MAP_BININD_INVALID | flags + | unzeroed | CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED); } JEMALLOC_ALWAYS_INLINE void arena_mapbits_large_binind_set(arena_chunk_t *chunk, size_t pageind, size_t binind) { - size_t *mapbitsp; + size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); + size_t mapbits = arena_mapbitsp_read(mapbitsp); assert(binind <= BININD_INVALID); - mapbitsp = arena_mapbitsp_get(chunk, pageind); assert(arena_mapbits_large_size_get(chunk, pageind) == PAGE); - *mapbitsp = (*mapbitsp & ~CHUNK_MAP_BININD_MASK) | (binind << - CHUNK_MAP_BININD_SHIFT); + arena_mapbitsp_write(mapbitsp, (mapbits & ~CHUNK_MAP_BININD_MASK) | + (binind << CHUNK_MAP_BININD_SHIFT)); } JEMALLOC_ALWAYS_INLINE void arena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind, size_t runind, size_t binind, size_t flags) { - size_t *mapbitsp; + size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); + size_t mapbits = arena_mapbitsp_read(mapbitsp); size_t unzeroed; assert(binind < BININD_INVALID); - mapbitsp = arena_mapbitsp_get(chunk, pageind); assert(pageind - runind >= map_bias); assert((flags & CHUNK_MAP_DIRTY) == flags); - unzeroed = *mapbitsp & CHUNK_MAP_UNZEROED; /* Preserve unzeroed. */ - *mapbitsp = (runind << LG_PAGE) | (binind << CHUNK_MAP_BININD_SHIFT) | - flags | unzeroed | CHUNK_MAP_ALLOCATED; + unzeroed = mapbits & CHUNK_MAP_UNZEROED; /* Preserve unzeroed. */ + arena_mapbitsp_write(mapbitsp, (runind << LG_PAGE) | (binind << + CHUNK_MAP_BININD_SHIFT) | flags | unzeroed | CHUNK_MAP_ALLOCATED); } JEMALLOC_ALWAYS_INLINE void arena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind, size_t unzeroed) { - size_t *mapbitsp; + size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); + size_t mapbits = arena_mapbitsp_read(mapbitsp); - mapbitsp = arena_mapbitsp_get(chunk, pageind); - *mapbitsp = (*mapbitsp & ~CHUNK_MAP_UNZEROED) | unzeroed; + arena_mapbitsp_write(mapbitsp, (mapbits & ~CHUNK_MAP_UNZEROED) | + unzeroed); } JEMALLOC_INLINE bool @@ -869,10 +904,10 @@ arena_prof_ctx_get(const void *ptr) } JEMALLOC_INLINE void -arena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx) +arena_prof_ctx_set(const void *ptr, size_t usize, prof_ctx_t *ctx) { arena_chunk_t *chunk; - size_t pageind, mapbits; + size_t pageind; cassert(config_prof); assert(ptr != NULL); @@ -880,10 +915,17 @@ arena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx) chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - mapbits = arena_mapbits_get(chunk, pageind); - assert((mapbits & CHUNK_MAP_ALLOCATED) != 0); - if ((mapbits & CHUNK_MAP_LARGE) == 0) { + assert(arena_mapbits_allocated_get(chunk, pageind) != 0); + + if (usize > SMALL_MAXCLASS || (prof_promote && + ((uintptr_t)ctx != (uintptr_t)1U || arena_mapbits_large_get(chunk, + pageind) != 0))) { + assert(arena_mapbits_large_get(chunk, pageind) != 0); + arena_mapp_get(chunk, pageind)->prof_ctx = ctx; + } else { + assert(arena_mapbits_large_get(chunk, pageind) == 0); if (prof_promote == false) { + size_t mapbits = arena_mapbits_get(chunk, pageind); arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - (mapbits >> LG_PAGE)) << LG_PAGE)); @@ -895,12 +937,11 @@ arena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx) bin_info = &arena_bin_info[binind]; regind = arena_run_regind(run, bin_info, ptr); - *((prof_ctx_t **)((uintptr_t)run + bin_info->ctx0_offset - + (regind * sizeof(prof_ctx_t *)))) = ctx; - } else - assert((uintptr_t)ctx == (uintptr_t)1U); - } else - arena_mapp_get(chunk, pageind)->prof_ctx = ctx; + *((prof_ctx_t **)((uintptr_t)run + + bin_info->ctx0_offset + (regind * sizeof(prof_ctx_t + *)))) = ctx; + } + } } JEMALLOC_ALWAYS_INLINE void * diff --git a/dep/jemalloc/include/jemalloc/internal/chunk_dss.h b/dep/jemalloc/include/jemalloc/internal/chunk_dss.h index 6585f071bbe..4535ce09c09 100644 --- a/dep/jemalloc/include/jemalloc/internal/chunk_dss.h +++ b/dep/jemalloc/include/jemalloc/internal/chunk_dss.h @@ -7,7 +7,7 @@ typedef enum { dss_prec_secondary = 2, dss_prec_limit = 3 -} dss_prec_t ; +} dss_prec_t; #define DSS_PREC_DEFAULT dss_prec_secondary #define DSS_DEFAULT "secondary" diff --git a/dep/jemalloc/include/jemalloc/internal/ckh.h b/dep/jemalloc/include/jemalloc/internal/ckh.h index 50c39ed9581..58712a6a763 100644 --- a/dep/jemalloc/include/jemalloc/internal/ckh.h +++ b/dep/jemalloc/include/jemalloc/internal/ckh.h @@ -17,7 +17,7 @@ typedef bool ckh_keycomp_t (const void *, const void *); * There are 2^LG_CKH_BUCKET_CELLS cells in each hash table bucket. Try to fit * one bucket per L1 cache line. */ -#define LG_CKH_BUCKET_CELLS (LG_CACHELINE - LG_SIZEOF_PTR - 1) +#define LG_CKH_BUCKET_CELLS (LG_CACHELINE - LG_SIZEOF_PTR - 1) #endif /* JEMALLOC_H_TYPES */ /******************************************************************************/ diff --git a/dep/jemalloc/include/jemalloc/internal/hash.h b/dep/jemalloc/include/jemalloc/internal/hash.h index 56ecc793b36..c7183ede82d 100644 --- a/dep/jemalloc/include/jemalloc/internal/hash.h +++ b/dep/jemalloc/include/jemalloc/internal/hash.h @@ -19,6 +19,11 @@ #ifdef JEMALLOC_H_INLINES #ifndef JEMALLOC_ENABLE_INLINE +uint32_t hash_x86_32(const void *key, int len, uint32_t seed); +void hash_x86_128(const void *key, const int len, uint32_t seed, + uint64_t r_out[2]); +void hash_x64_128(const void *key, const int len, const uint32_t seed, + uint64_t r_out[2]); void hash(const void *key, size_t len, const uint32_t seed, size_t r_hash[2]); #endif @@ -43,14 +48,14 @@ JEMALLOC_INLINE uint32_t hash_get_block_32(const uint32_t *p, int i) { - return p[i]; + return (p[i]); } JEMALLOC_INLINE uint64_t hash_get_block_64(const uint64_t *p, int i) { - return p[i]; + return (p[i]); } JEMALLOC_INLINE uint32_t @@ -63,7 +68,7 @@ hash_fmix_32(uint32_t h) h *= 0xc2b2ae35; h ^= h >> 16; - return h; + return (h); } JEMALLOC_INLINE uint64_t @@ -76,7 +81,7 @@ hash_fmix_64(uint64_t k) k *= QU(0xc4ceb9fe1a85ec53LLU); k ^= k >> 33; - return k; + return (k); } JEMALLOC_INLINE uint32_t @@ -127,12 +132,12 @@ hash_x86_32(const void *key, int len, uint32_t seed) h1 = hash_fmix_32(h1); - return h1; + return (h1); } UNUSED JEMALLOC_INLINE void hash_x86_128(const void *key, const int len, uint32_t seed, - uint64_t r_out[2]) + uint64_t r_out[2]) { const uint8_t * data = (const uint8_t *) key; const int nblocks = len / 16; @@ -234,7 +239,7 @@ hash_x86_128(const void *key, const int len, uint32_t seed, UNUSED JEMALLOC_INLINE void hash_x64_128(const void *key, const int len, const uint32_t seed, - uint64_t r_out[2]) + uint64_t r_out[2]) { const uint8_t *data = (const uint8_t *) key; const int nblocks = len / 16; @@ -310,13 +315,12 @@ hash_x64_128(const void *key, const int len, const uint32_t seed, r_out[1] = h2; } - /******************************************************************************/ /* API. */ JEMALLOC_INLINE void hash(const void *key, size_t len, const uint32_t seed, size_t r_hash[2]) { -#if (LG_SIZEOF_PTR == 3) +#if (LG_SIZEOF_PTR == 3 && !defined(JEMALLOC_BIG_ENDIAN)) hash_x64_128(key, len, seed, (uint64_t *)r_hash); #else uint64_t hashes[2]; diff --git a/dep/jemalloc/include/jemalloc/internal/huge.h b/dep/jemalloc/include/jemalloc/internal/huge.h index d987d370767..a2b9c779191 100644 --- a/dep/jemalloc/include/jemalloc/internal/huge.h +++ b/dep/jemalloc/include/jemalloc/internal/huge.h @@ -17,14 +17,20 @@ extern size_t huge_allocated; /* Protects chunk-related data structures. */ extern malloc_mutex_t huge_mtx; -void *huge_malloc(size_t size, bool zero); -void *huge_palloc(size_t size, size_t alignment, bool zero); -void *huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, +void *huge_malloc(size_t size, bool zero, dss_prec_t dss_prec); +void *huge_palloc(size_t size, size_t alignment, bool zero, + dss_prec_t dss_prec); +bool huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra); void *huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra, - size_t alignment, bool zero, bool try_tcache_dalloc); + size_t alignment, bool zero, bool try_tcache_dalloc, dss_prec_t dss_prec); +#ifdef JEMALLOC_JET +typedef void (huge_dalloc_junk_t)(void *, size_t); +extern huge_dalloc_junk_t *huge_dalloc_junk; +#endif void huge_dalloc(void *ptr, bool unmap); size_t huge_salloc(const void *ptr); +dss_prec_t huge_dss_prec_get(arena_t *arena); prof_ctx_t *huge_prof_ctx_get(const void *ptr); void huge_prof_ctx_set(const void *ptr, prof_ctx_t *ctx); bool huge_boot(void); diff --git a/dep/jemalloc/include/jemalloc/internal/jemalloc_internal.h b/dep/jemalloc/include/jemalloc/internal/jemalloc_internal.h index 80045bda4bd..cf171326c29 100644 --- a/dep/jemalloc/include/jemalloc/internal/jemalloc_internal.h +++ b/dep/jemalloc/include/jemalloc/internal/jemalloc_internal.h @@ -1,5 +1,5 @@ #ifndef JEMALLOC_INTERNAL_H -#define JEMALLOC_INTERNAL_H +#define JEMALLOC_INTERNAL_H #include <math.h> #ifdef _WIN32 # include <windows.h> @@ -54,8 +54,7 @@ typedef intptr_t ssize_t; #endif #include <fcntl.h> -#define JEMALLOC_NO_DEMANGLE -#include "../jemalloc.h" +#include "jemalloc_defs.h" #ifdef JEMALLOC_UTRACE #include <sys/ktrace.h> @@ -66,6 +65,8 @@ typedef intptr_t ssize_t; #include <valgrind/memcheck.h> #endif +#define JEMALLOC_NO_DEMANGLE +#include "../jemalloc.h" #include "jemalloc/internal/private_namespace.h" #ifdef JEMALLOC_CC_SILENCE @@ -221,8 +222,13 @@ static const bool config_ivsalloc = * JEMALLOC_H_INLINES : Inline functions. */ /******************************************************************************/ -#define JEMALLOC_H_TYPES +#define JEMALLOC_H_TYPES + +#ifndef JEMALLOC_HAS_RESTRICT +# define restrict +#endif +#define MALLOCX_LG_ALIGN_MASK ((int)0x3f) #define ALLOCM_LG_ALIGN_MASK ((int)0x3f) #define ZU(z) ((size_t)z) @@ -232,20 +238,26 @@ static const bool config_ivsalloc = # define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) #endif -#ifdef JEMALLOC_DEBUG +#if defined(JEMALLOC_DEBUG) /* Disable inlining to make debugging easier. */ # define JEMALLOC_ALWAYS_INLINE +# define JEMALLOC_ALWAYS_INLINE_C static # define JEMALLOC_INLINE +# define JEMALLOC_INLINE_C static # define inline #else # define JEMALLOC_ENABLE_INLINE # ifdef JEMALLOC_HAVE_ATTR # define JEMALLOC_ALWAYS_INLINE \ static inline JEMALLOC_ATTR(unused) JEMALLOC_ATTR(always_inline) +# define JEMALLOC_ALWAYS_INLINE_C \ + static inline JEMALLOC_ATTR(always_inline) # else # define JEMALLOC_ALWAYS_INLINE static inline +# define JEMALLOC_ALWAYS_INLINE_C static inline # endif # define JEMALLOC_INLINE static inline +# define JEMALLOC_INLINE_C static inline # ifdef _MSC_VER # define inline _inline # endif @@ -278,6 +290,9 @@ static const bool config_ivsalloc = # ifdef __arm__ # define LG_QUANTUM 3 # endif +# ifdef __aarch64__ +# define LG_QUANTUM 4 +# endif # ifdef __hppa__ # define LG_QUANTUM 4 # endif @@ -478,7 +493,7 @@ static const bool config_ivsalloc = #undef JEMALLOC_H_TYPES /******************************************************************************/ -#define JEMALLOC_H_STRUCTS +#define JEMALLOC_H_STRUCTS #include "jemalloc/internal/util.h" #include "jemalloc/internal/atomic.h" @@ -507,14 +522,14 @@ typedef struct { uint64_t deallocated; } thread_allocated_t; /* - * The JEMALLOC_CONCAT() wrapper is necessary to pass {0, 0} via a cpp macro + * The JEMALLOC_ARG_CONCAT() wrapper is necessary to pass {0, 0} via a cpp macro * argument. */ -#define THREAD_ALLOCATED_INITIALIZER JEMALLOC_CONCAT({0, 0}) +#define THREAD_ALLOCATED_INITIALIZER JEMALLOC_ARG_CONCAT({0, 0}) #undef JEMALLOC_H_STRUCTS /******************************************************************************/ -#define JEMALLOC_H_EXTERNS +#define JEMALLOC_H_EXTERNS extern bool opt_abort; extern bool opt_junk; @@ -574,7 +589,7 @@ void jemalloc_postfork_child(void); #undef JEMALLOC_H_EXTERNS /******************************************************************************/ -#define JEMALLOC_H_INLINES +#define JEMALLOC_H_INLINES #include "jemalloc/internal/util.h" #include "jemalloc/internal/atomic.h" @@ -749,32 +764,36 @@ choose_arena(arena_t *arena) #include "jemalloc/internal/quarantine.h" #ifndef JEMALLOC_ENABLE_INLINE -void *imallocx(size_t size, bool try_tcache, arena_t *arena); +void *imalloct(size_t size, bool try_tcache, arena_t *arena); void *imalloc(size_t size); -void *icallocx(size_t size, bool try_tcache, arena_t *arena); +void *icalloct(size_t size, bool try_tcache, arena_t *arena); void *icalloc(size_t size); -void *ipallocx(size_t usize, size_t alignment, bool zero, bool try_tcache, +void *ipalloct(size_t usize, size_t alignment, bool zero, bool try_tcache, arena_t *arena); void *ipalloc(size_t usize, size_t alignment, bool zero); size_t isalloc(const void *ptr, bool demote); size_t ivsalloc(const void *ptr, bool demote); size_t u2rz(size_t usize); size_t p2rz(const void *ptr); -void idallocx(void *ptr, bool try_tcache); +void idalloct(void *ptr, bool try_tcache); void idalloc(void *ptr); -void iqallocx(void *ptr, bool try_tcache); +void iqalloct(void *ptr, bool try_tcache); void iqalloc(void *ptr); -void *irallocx(void *ptr, size_t size, size_t extra, size_t alignment, - bool zero, bool no_move, bool try_tcache_alloc, bool try_tcache_dalloc, +void *iralloct_realign(void *ptr, size_t oldsize, size_t size, size_t extra, + size_t alignment, bool zero, bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena); +void *iralloct(void *ptr, size_t size, size_t extra, size_t alignment, + bool zero, bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena); void *iralloc(void *ptr, size_t size, size_t extra, size_t alignment, - bool zero, bool no_move); + bool zero); +bool ixalloc(void *ptr, size_t size, size_t extra, size_t alignment, + bool zero); malloc_tsd_protos(JEMALLOC_ATTR(unused), thread_allocated, thread_allocated_t) #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_)) JEMALLOC_ALWAYS_INLINE void * -imallocx(size_t size, bool try_tcache, arena_t *arena) +imalloct(size_t size, bool try_tcache, arena_t *arena) { assert(size != 0); @@ -782,35 +801,35 @@ imallocx(size_t size, bool try_tcache, arena_t *arena) if (size <= arena_maxclass) return (arena_malloc(arena, size, false, try_tcache)); else - return (huge_malloc(size, false)); + return (huge_malloc(size, false, huge_dss_prec_get(arena))); } JEMALLOC_ALWAYS_INLINE void * imalloc(size_t size) { - return (imallocx(size, true, NULL)); + return (imalloct(size, true, NULL)); } JEMALLOC_ALWAYS_INLINE void * -icallocx(size_t size, bool try_tcache, arena_t *arena) +icalloct(size_t size, bool try_tcache, arena_t *arena) { if (size <= arena_maxclass) return (arena_malloc(arena, size, true, try_tcache)); else - return (huge_malloc(size, true)); + return (huge_malloc(size, true, huge_dss_prec_get(arena))); } JEMALLOC_ALWAYS_INLINE void * icalloc(size_t size) { - return (icallocx(size, true, NULL)); + return (icalloct(size, true, NULL)); } JEMALLOC_ALWAYS_INLINE void * -ipallocx(size_t usize, size_t alignment, bool zero, bool try_tcache, +ipalloct(size_t usize, size_t alignment, bool zero, bool try_tcache, arena_t *arena) { void *ret; @@ -825,9 +844,9 @@ ipallocx(size_t usize, size_t alignment, bool zero, bool try_tcache, ret = arena_palloc(choose_arena(arena), usize, alignment, zero); } else if (alignment <= chunksize) - ret = huge_malloc(usize, zero); + ret = huge_malloc(usize, zero, huge_dss_prec_get(arena)); else - ret = huge_palloc(usize, alignment, zero); + ret = huge_palloc(usize, alignment, zero, huge_dss_prec_get(arena)); } assert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret); @@ -838,7 +857,7 @@ JEMALLOC_ALWAYS_INLINE void * ipalloc(size_t usize, size_t alignment, bool zero) { - return (ipallocx(usize, alignment, zero, true, NULL)); + return (ipalloct(usize, alignment, zero, true, NULL)); } /* @@ -870,7 +889,7 @@ ivsalloc(const void *ptr, bool demote) { /* Return 0 if ptr is not within a chunk managed by jemalloc. */ - if (rtree_get(chunks_rtree, (uintptr_t)CHUNK_ADDR2BASE(ptr)) == NULL) + if (rtree_get(chunks_rtree, (uintptr_t)CHUNK_ADDR2BASE(ptr)) == 0) return (0); return (isalloc(ptr, demote)); @@ -899,7 +918,7 @@ p2rz(const void *ptr) } JEMALLOC_ALWAYS_INLINE void -idallocx(void *ptr, bool try_tcache) +idalloct(void *ptr, bool try_tcache) { arena_chunk_t *chunk; @@ -916,31 +935,63 @@ JEMALLOC_ALWAYS_INLINE void idalloc(void *ptr) { - idallocx(ptr, true); + idalloct(ptr, true); } JEMALLOC_ALWAYS_INLINE void -iqallocx(void *ptr, bool try_tcache) +iqalloct(void *ptr, bool try_tcache) { if (config_fill && opt_quarantine) quarantine(ptr); else - idallocx(ptr, try_tcache); + idalloct(ptr, try_tcache); } JEMALLOC_ALWAYS_INLINE void iqalloc(void *ptr) { - iqallocx(ptr, true); + iqalloct(ptr, true); } JEMALLOC_ALWAYS_INLINE void * -irallocx(void *ptr, size_t size, size_t extra, size_t alignment, bool zero, - bool no_move, bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena) +iralloct_realign(void *ptr, size_t oldsize, size_t size, size_t extra, + size_t alignment, bool zero, bool try_tcache_alloc, bool try_tcache_dalloc, + arena_t *arena) +{ + void *p; + size_t usize, copysize; + + usize = sa2u(size + extra, alignment); + if (usize == 0) + return (NULL); + p = ipalloct(usize, alignment, zero, try_tcache_alloc, arena); + if (p == NULL) { + if (extra == 0) + return (NULL); + /* Try again, without extra this time. */ + usize = sa2u(size, alignment); + if (usize == 0) + return (NULL); + p = ipalloct(usize, alignment, zero, try_tcache_alloc, arena); + if (p == NULL) + return (NULL); + } + /* + * Copy at most size bytes (not size+extra), since the caller has no + * expectation that the extra bytes will be reliably preserved. + */ + copysize = (size < oldsize) ? size : oldsize; + memcpy(p, ptr, copysize); + iqalloct(ptr, try_tcache_dalloc); + return (p); +} + +JEMALLOC_ALWAYS_INLINE void * +iralloct(void *ptr, size_t size, size_t extra, size_t alignment, bool zero, + bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena) { - void *ret; size_t oldsize; assert(ptr != NULL); @@ -950,68 +1001,50 @@ irallocx(void *ptr, size_t size, size_t extra, size_t alignment, bool zero, if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1)) != 0) { - size_t usize, copysize; - /* * Existing object alignment is inadequate; allocate new space * and copy. */ - if (no_move) - return (NULL); - usize = sa2u(size + extra, alignment); - if (usize == 0) - return (NULL); - ret = ipallocx(usize, alignment, zero, try_tcache_alloc, arena); - if (ret == NULL) { - if (extra == 0) - return (NULL); - /* Try again, without extra this time. */ - usize = sa2u(size, alignment); - if (usize == 0) - return (NULL); - ret = ipallocx(usize, alignment, zero, try_tcache_alloc, - arena); - if (ret == NULL) - return (NULL); - } - /* - * Copy at most size bytes (not size+extra), since the caller - * has no expectation that the extra bytes will be reliably - * preserved. - */ - copysize = (size < oldsize) ? size : oldsize; - memcpy(ret, ptr, copysize); - iqallocx(ptr, try_tcache_dalloc); - return (ret); + return (iralloct_realign(ptr, oldsize, size, extra, alignment, + zero, try_tcache_alloc, try_tcache_dalloc, arena)); } - if (no_move) { - if (size <= arena_maxclass) { - return (arena_ralloc_no_move(ptr, oldsize, size, - extra, zero)); - } else { - return (huge_ralloc_no_move(ptr, oldsize, size, - extra)); - } + if (size + extra <= arena_maxclass) { + return (arena_ralloc(arena, ptr, oldsize, size, extra, + alignment, zero, try_tcache_alloc, + try_tcache_dalloc)); } else { - if (size + extra <= arena_maxclass) { - return (arena_ralloc(arena, ptr, oldsize, size, extra, - alignment, zero, try_tcache_alloc, - try_tcache_dalloc)); - } else { - return (huge_ralloc(ptr, oldsize, size, extra, - alignment, zero, try_tcache_dalloc)); - } + return (huge_ralloc(ptr, oldsize, size, extra, + alignment, zero, try_tcache_dalloc, huge_dss_prec_get(arena))); } } JEMALLOC_ALWAYS_INLINE void * -iralloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero, - bool no_move) +iralloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero) { - return (irallocx(ptr, size, extra, alignment, zero, no_move, true, true, - NULL)); + return (iralloct(ptr, size, extra, alignment, zero, true, true, NULL)); +} + +JEMALLOC_ALWAYS_INLINE bool +ixalloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero) +{ + size_t oldsize; + + assert(ptr != NULL); + assert(size != 0); + + oldsize = isalloc(ptr, config_prof); + if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1)) + != 0) { + /* Existing object alignment is inadequate. */ + return (true); + } + + if (size <= arena_maxclass) + return (arena_ralloc_no_move(ptr, oldsize, size, extra, zero)); + else + return (huge_ralloc_no_move(ptr, oldsize, size, extra)); } malloc_tsd_externs(thread_allocated, thread_allocated_t) diff --git a/dep/jemalloc/include/jemalloc/internal/private_namespace.h b/dep/jemalloc/include/jemalloc/internal/private_namespace.h index 65de3163fd3..35c3b0c6c74 100644 --- a/dep/jemalloc/include/jemalloc/internal/private_namespace.h +++ b/dep/jemalloc/include/jemalloc/internal/private_namespace.h @@ -8,6 +8,7 @@ #define arena_dalloc JEMALLOC_N(arena_dalloc) #define arena_dalloc_bin JEMALLOC_N(arena_dalloc_bin) #define arena_dalloc_bin_locked JEMALLOC_N(arena_dalloc_bin_locked) +#define arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large) #define arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small) #define arena_dalloc_large JEMALLOC_N(arena_dalloc_large) #define arena_dalloc_large_locked JEMALLOC_N(arena_dalloc_large_locked) @@ -33,6 +34,8 @@ #define arena_mapbits_unzeroed_get JEMALLOC_N(arena_mapbits_unzeroed_get) #define arena_mapbits_unzeroed_set JEMALLOC_N(arena_mapbits_unzeroed_set) #define arena_mapbitsp_get JEMALLOC_N(arena_mapbitsp_get) +#define arena_mapbitsp_read JEMALLOC_N(arena_mapbitsp_read) +#define arena_mapbitsp_write JEMALLOC_N(arena_mapbitsp_write) #define arena_mapp_get JEMALLOC_N(arena_mapp_get) #define arena_maxclass JEMALLOC_N(arena_maxclass) #define arena_new JEMALLOC_N(arena_new) @@ -48,8 +51,11 @@ #define arena_prof_promoted JEMALLOC_N(arena_prof_promoted) #define arena_ptr_small_binind_get JEMALLOC_N(arena_ptr_small_binind_get) #define arena_purge_all JEMALLOC_N(arena_purge_all) +#define arena_quarantine_junk_small JEMALLOC_N(arena_quarantine_junk_small) #define arena_ralloc JEMALLOC_N(arena_ralloc) +#define arena_ralloc_junk_large JEMALLOC_N(arena_ralloc_junk_large) #define arena_ralloc_no_move JEMALLOC_N(arena_ralloc_no_move) +#define arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption) #define arena_run_regind JEMALLOC_N(arena_run_regind) #define arena_salloc JEMALLOC_N(arena_salloc) #define arena_stats_merge JEMALLOC_N(arena_stats_merge) @@ -66,6 +72,7 @@ #define arenas_tsd_cleanup_wrapper JEMALLOC_N(arenas_tsd_cleanup_wrapper) #define arenas_tsd_get JEMALLOC_N(arenas_tsd_get) #define arenas_tsd_get_wrapper JEMALLOC_N(arenas_tsd_get_wrapper) +#define arenas_tsd_init_head JEMALLOC_N(arenas_tsd_init_head) #define arenas_tsd_set JEMALLOC_N(arenas_tsd_set) #define atomic_add_u JEMALLOC_N(atomic_add_u) #define atomic_add_uint32 JEMALLOC_N(atomic_add_uint32) @@ -189,6 +196,8 @@ #define huge_allocated JEMALLOC_N(huge_allocated) #define huge_boot JEMALLOC_N(huge_boot) #define huge_dalloc JEMALLOC_N(huge_dalloc) +#define huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk) +#define huge_dss_prec_get JEMALLOC_N(huge_dss_prec_get) #define huge_malloc JEMALLOC_N(huge_malloc) #define huge_mtx JEMALLOC_N(huge_mtx) #define huge_ndalloc JEMALLOC_N(huge_ndalloc) @@ -204,20 +213,22 @@ #define huge_salloc JEMALLOC_N(huge_salloc) #define iallocm JEMALLOC_N(iallocm) #define icalloc JEMALLOC_N(icalloc) -#define icallocx JEMALLOC_N(icallocx) +#define icalloct JEMALLOC_N(icalloct) #define idalloc JEMALLOC_N(idalloc) -#define idallocx JEMALLOC_N(idallocx) +#define idalloct JEMALLOC_N(idalloct) #define imalloc JEMALLOC_N(imalloc) -#define imallocx JEMALLOC_N(imallocx) +#define imalloct JEMALLOC_N(imalloct) #define ipalloc JEMALLOC_N(ipalloc) -#define ipallocx JEMALLOC_N(ipallocx) +#define ipalloct JEMALLOC_N(ipalloct) #define iqalloc JEMALLOC_N(iqalloc) -#define iqallocx JEMALLOC_N(iqallocx) +#define iqalloct JEMALLOC_N(iqalloct) #define iralloc JEMALLOC_N(iralloc) -#define irallocx JEMALLOC_N(irallocx) +#define iralloct JEMALLOC_N(iralloct) +#define iralloct_realign JEMALLOC_N(iralloct_realign) #define isalloc JEMALLOC_N(isalloc) #define isthreaded JEMALLOC_N(isthreaded) #define ivsalloc JEMALLOC_N(ivsalloc) +#define ixalloc JEMALLOC_N(ixalloc) #define jemalloc_postfork_child JEMALLOC_N(jemalloc_postfork_child) #define jemalloc_postfork_parent JEMALLOC_N(jemalloc_postfork_parent) #define jemalloc_prefork JEMALLOC_N(jemalloc_prefork) @@ -248,6 +259,7 @@ #define ncpus JEMALLOC_N(ncpus) #define nhbins JEMALLOC_N(nhbins) #define opt_abort JEMALLOC_N(opt_abort) +#define opt_dss JEMALLOC_N(opt_dss) #define opt_junk JEMALLOC_N(opt_junk) #define opt_lg_chunk JEMALLOC_N(opt_lg_chunk) #define opt_lg_dirty_mult JEMALLOC_N(opt_lg_dirty_mult) @@ -277,8 +289,10 @@ #define prof_boot0 JEMALLOC_N(prof_boot0) #define prof_boot1 JEMALLOC_N(prof_boot1) #define prof_boot2 JEMALLOC_N(prof_boot2) +#define prof_bt_count JEMALLOC_N(prof_bt_count) #define prof_ctx_get JEMALLOC_N(prof_ctx_get) #define prof_ctx_set JEMALLOC_N(prof_ctx_set) +#define prof_dump_open JEMALLOC_N(prof_dump_open) #define prof_free JEMALLOC_N(prof_free) #define prof_gdump JEMALLOC_N(prof_gdump) #define prof_idump JEMALLOC_N(prof_idump) @@ -304,6 +318,7 @@ #define prof_tdata_tsd_cleanup_wrapper JEMALLOC_N(prof_tdata_tsd_cleanup_wrapper) #define prof_tdata_tsd_get JEMALLOC_N(prof_tdata_tsd_get) #define prof_tdata_tsd_get_wrapper JEMALLOC_N(prof_tdata_tsd_get_wrapper) +#define prof_tdata_tsd_init_head JEMALLOC_N(prof_tdata_tsd_init_head) #define prof_tdata_tsd_set JEMALLOC_N(prof_tdata_tsd_set) #define quarantine JEMALLOC_N(quarantine) #define quarantine_alloc_hook JEMALLOC_N(quarantine_alloc_hook) @@ -317,8 +332,10 @@ #define quarantine_tsd_cleanup_wrapper JEMALLOC_N(quarantine_tsd_cleanup_wrapper) #define quarantine_tsd_get JEMALLOC_N(quarantine_tsd_get) #define quarantine_tsd_get_wrapper JEMALLOC_N(quarantine_tsd_get_wrapper) +#define quarantine_tsd_init_head JEMALLOC_N(quarantine_tsd_init_head) #define quarantine_tsd_set JEMALLOC_N(quarantine_tsd_set) #define register_zone JEMALLOC_N(register_zone) +#define rtree_delete JEMALLOC_N(rtree_delete) #define rtree_get JEMALLOC_N(rtree_get) #define rtree_get_locked JEMALLOC_N(rtree_get_locked) #define rtree_new JEMALLOC_N(rtree_new) @@ -329,6 +346,7 @@ #define s2u JEMALLOC_N(s2u) #define sa2u JEMALLOC_N(sa2u) #define set_errno JEMALLOC_N(set_errno) +#define small_size2bin JEMALLOC_N(small_size2bin) #define stats_cactive JEMALLOC_N(stats_cactive) #define stats_cactive_add JEMALLOC_N(stats_cactive_add) #define stats_cactive_get JEMALLOC_N(stats_cactive_get) @@ -361,6 +379,7 @@ #define tcache_enabled_tsd_cleanup_wrapper JEMALLOC_N(tcache_enabled_tsd_cleanup_wrapper) #define tcache_enabled_tsd_get JEMALLOC_N(tcache_enabled_tsd_get) #define tcache_enabled_tsd_get_wrapper JEMALLOC_N(tcache_enabled_tsd_get_wrapper) +#define tcache_enabled_tsd_init_head JEMALLOC_N(tcache_enabled_tsd_init_head) #define tcache_enabled_tsd_set JEMALLOC_N(tcache_enabled_tsd_set) #define tcache_event JEMALLOC_N(tcache_event) #define tcache_event_hard JEMALLOC_N(tcache_event_hard) @@ -377,6 +396,7 @@ #define tcache_tsd_cleanup_wrapper JEMALLOC_N(tcache_tsd_cleanup_wrapper) #define tcache_tsd_get JEMALLOC_N(tcache_tsd_get) #define tcache_tsd_get_wrapper JEMALLOC_N(tcache_tsd_get_wrapper) +#define tcache_tsd_init_head JEMALLOC_N(tcache_tsd_init_head) #define tcache_tsd_set JEMALLOC_N(tcache_tsd_set) #define thread_allocated_booted JEMALLOC_N(thread_allocated_booted) #define thread_allocated_initialized JEMALLOC_N(thread_allocated_initialized) @@ -386,5 +406,8 @@ #define thread_allocated_tsd_cleanup_wrapper JEMALLOC_N(thread_allocated_tsd_cleanup_wrapper) #define thread_allocated_tsd_get JEMALLOC_N(thread_allocated_tsd_get) #define thread_allocated_tsd_get_wrapper JEMALLOC_N(thread_allocated_tsd_get_wrapper) +#define thread_allocated_tsd_init_head JEMALLOC_N(thread_allocated_tsd_init_head) #define thread_allocated_tsd_set JEMALLOC_N(thread_allocated_tsd_set) +#define tsd_init_check_recursion JEMALLOC_N(tsd_init_check_recursion) +#define tsd_init_finish JEMALLOC_N(tsd_init_finish) #define u2rz JEMALLOC_N(u2rz) diff --git a/dep/jemalloc/include/jemalloc/internal/prng.h b/dep/jemalloc/include/jemalloc/internal/prng.h index 83a5462b4dd..7b2b06512ff 100644 --- a/dep/jemalloc/include/jemalloc/internal/prng.h +++ b/dep/jemalloc/include/jemalloc/internal/prng.h @@ -25,7 +25,7 @@ * uint32_t state : Seed value. * const uint32_t a, c : See above discussion. */ -#define prng32(r, lg_range, state, a, c) do { \ +#define prng32(r, lg_range, state, a, c) do { \ assert(lg_range > 0); \ assert(lg_range <= 32); \ \ @@ -35,7 +35,7 @@ } while (false) /* Same as prng32(), but 64 bits of pseudo-randomness, using uint64_t. */ -#define prng64(r, lg_range, state, a, c) do { \ +#define prng64(r, lg_range, state, a, c) do { \ assert(lg_range > 0); \ assert(lg_range <= 64); \ \ diff --git a/dep/jemalloc/include/jemalloc/internal/prof.h b/dep/jemalloc/include/jemalloc/internal/prof.h index 119a5b1bcb7..6f162d21e84 100644 --- a/dep/jemalloc/include/jemalloc/internal/prof.h +++ b/dep/jemalloc/include/jemalloc/internal/prof.h @@ -8,7 +8,11 @@ typedef struct prof_ctx_s prof_ctx_t; typedef struct prof_tdata_s prof_tdata_t; /* Option defaults. */ -#define PROF_PREFIX_DEFAULT "jeprof" +#ifdef JEMALLOC_PROF +# define PROF_PREFIX_DEFAULT "jeprof" +#else +# define PROF_PREFIX_DEFAULT "" +#endif #define LG_PROF_SAMPLE_DEFAULT 19 #define LG_PROF_INTERVAL_DEFAULT -1 @@ -129,6 +133,7 @@ struct prof_ctx_s { * limbo due to one of: * - Initializing per thread counters associated with this ctx. * - Preparing to destroy this ctx. + * - Dumping a heap profile that includes this ctx. * nlimbo must be 1 (single destroyer) in order to safely destroy the * ctx. */ @@ -145,7 +150,11 @@ struct prof_ctx_s { * this context. */ ql_head(prof_thr_cnt_t) cnts_ql; + + /* Linkage for list of contexts to be dumped. */ + ql_elm(prof_ctx_t) dump_link; }; +typedef ql_head(prof_ctx_t) prof_ctx_list_t; struct prof_tdata_s { /* @@ -195,7 +204,12 @@ extern bool opt_prof_gdump; /* High-water memory dumping. */ extern bool opt_prof_final; /* Final profile dumping. */ extern bool opt_prof_leak; /* Dump leak summary at exit. */ extern bool opt_prof_accum; /* Report cumulative bytes. */ -extern char opt_prof_prefix[PATH_MAX + 1]; +extern char opt_prof_prefix[ + /* Minimize memory bloat for non-prof builds. */ +#ifdef JEMALLOC_PROF + PATH_MAX + +#endif + 1]; /* * Profile dump interval, measured in bytes allocated. Each arena triggers a @@ -215,6 +229,11 @@ extern bool prof_promote; void bt_init(prof_bt_t *bt, void **vec); void prof_backtrace(prof_bt_t *bt, unsigned nignore); prof_thr_cnt_t *prof_lookup(prof_bt_t *bt); +#ifdef JEMALLOC_JET +size_t prof_bt_count(void); +typedef int (prof_dump_open_t)(bool, const char *); +extern prof_dump_open_t *prof_dump_open; +#endif void prof_idump(void); bool prof_mdump(const char *filename); void prof_gdump(void); @@ -289,11 +308,11 @@ malloc_tsd_protos(JEMALLOC_ATTR(unused), prof_tdata, prof_tdata_t *) prof_tdata_t *prof_tdata_get(bool create); void prof_sample_threshold_update(prof_tdata_t *prof_tdata); prof_ctx_t *prof_ctx_get(const void *ptr); -void prof_ctx_set(const void *ptr, prof_ctx_t *ctx); +void prof_ctx_set(const void *ptr, size_t usize, prof_ctx_t *ctx); bool prof_sample_accum_update(size_t size); -void prof_malloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt); -void prof_realloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt, - size_t old_size, prof_ctx_t *old_ctx); +void prof_malloc(const void *ptr, size_t usize, prof_thr_cnt_t *cnt); +void prof_realloc(const void *ptr, size_t usize, prof_thr_cnt_t *cnt, + size_t old_usize, prof_ctx_t *old_ctx); void prof_free(const void *ptr, size_t size); #endif @@ -320,6 +339,20 @@ prof_tdata_get(bool create) JEMALLOC_INLINE void prof_sample_threshold_update(prof_tdata_t *prof_tdata) { + /* + * The body of this function is compiled out unless heap profiling is + * enabled, so that it is possible to compile jemalloc with floating + * point support completely disabled. Avoiding floating point code is + * important on memory-constrained systems, but it also enables a + * workaround for versions of glibc that don't properly save/restore + * floating point registers during dynamic lazy symbol loading (which + * internally calls into whatever malloc implementation happens to be + * integrated into the application). Note that some compilers (e.g. + * gcc 4.8) may use floating point registers for fast memory moves, so + * jemalloc must be compiled with such optimizations disabled (e.g. + * -mno-sse) in order for the workaround to be complete. + */ +#ifdef JEMALLOC_PROF uint64_t r; double u; @@ -341,7 +374,7 @@ prof_sample_threshold_update(prof_tdata_t *prof_tdata) * Luc Devroye * Springer-Verlag, New York, 1986 * pp 500 - * (http://cg.scs.carleton.ca/~luc/rnbookindex.html) + * (http://luc.devroye.org/rnbookindex.html) */ prng64(r, 53, prof_tdata->prng_state, UINT64_C(6364136223846793005), UINT64_C(1442695040888963407)); @@ -349,6 +382,7 @@ prof_sample_threshold_update(prof_tdata_t *prof_tdata) prof_tdata->threshold = (uint64_t)(log(u) / log(1.0 - (1.0 / (double)((uint64_t)1U << opt_lg_prof_sample)))) + (uint64_t)1U; +#endif } JEMALLOC_INLINE prof_ctx_t * @@ -371,7 +405,7 @@ prof_ctx_get(const void *ptr) } JEMALLOC_INLINE void -prof_ctx_set(const void *ptr, prof_ctx_t *ctx) +prof_ctx_set(const void *ptr, size_t usize, prof_ctx_t *ctx) { arena_chunk_t *chunk; @@ -381,7 +415,7 @@ prof_ctx_set(const void *ptr, prof_ctx_t *ctx) chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); if (chunk != ptr) { /* Region. */ - arena_prof_ctx_set(ptr, ctx); + arena_prof_ctx_set(ptr, usize, ctx); } else huge_prof_ctx_set(ptr, ctx); } @@ -416,20 +450,20 @@ prof_sample_accum_update(size_t size) } JEMALLOC_INLINE void -prof_malloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt) +prof_malloc(const void *ptr, size_t usize, prof_thr_cnt_t *cnt) { cassert(config_prof); assert(ptr != NULL); - assert(size == isalloc(ptr, true)); + assert(usize == isalloc(ptr, true)); if (opt_lg_prof_sample != 0) { - if (prof_sample_accum_update(size)) { + if (prof_sample_accum_update(usize)) { /* * Don't sample. For malloc()-like allocation, it is * always possible to tell in advance how large an * object's usable size will be, so there should never - * be a difference between the size passed to + * be a difference between the usize passed to * PROF_ALLOC_PREP() and prof_malloc(). */ assert((uintptr_t)cnt == (uintptr_t)1U); @@ -437,17 +471,17 @@ prof_malloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt) } if ((uintptr_t)cnt > (uintptr_t)1U) { - prof_ctx_set(ptr, cnt->ctx); + prof_ctx_set(ptr, usize, cnt->ctx); cnt->epoch++; /*********/ mb_write(); /*********/ cnt->cnts.curobjs++; - cnt->cnts.curbytes += size; + cnt->cnts.curbytes += usize; if (opt_prof_accum) { cnt->cnts.accumobjs++; - cnt->cnts.accumbytes += size; + cnt->cnts.accumbytes += usize; } /*********/ mb_write(); @@ -457,12 +491,12 @@ prof_malloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt) mb_write(); /*********/ } else - prof_ctx_set(ptr, (prof_ctx_t *)(uintptr_t)1U); + prof_ctx_set(ptr, usize, (prof_ctx_t *)(uintptr_t)1U); } JEMALLOC_INLINE void -prof_realloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt, - size_t old_size, prof_ctx_t *old_ctx) +prof_realloc(const void *ptr, size_t usize, prof_thr_cnt_t *cnt, + size_t old_usize, prof_ctx_t *old_ctx) { prof_thr_cnt_t *told_cnt; @@ -470,15 +504,15 @@ prof_realloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt, assert(ptr != NULL || (uintptr_t)cnt <= (uintptr_t)1U); if (ptr != NULL) { - assert(size == isalloc(ptr, true)); + assert(usize == isalloc(ptr, true)); if (opt_lg_prof_sample != 0) { - if (prof_sample_accum_update(size)) { + if (prof_sample_accum_update(usize)) { /* - * Don't sample. The size passed to + * Don't sample. The usize passed to * PROF_ALLOC_PREP() was larger than what * actually got allocated, so a backtrace was * captured for this allocation, even though - * its actual size was insufficient to cross + * its actual usize was insufficient to cross * the sample threshold. */ cnt = (prof_thr_cnt_t *)(uintptr_t)1U; @@ -495,7 +529,7 @@ prof_realloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt, */ malloc_mutex_lock(old_ctx->lock); old_ctx->cnt_merged.curobjs--; - old_ctx->cnt_merged.curbytes -= old_size; + old_ctx->cnt_merged.curbytes -= old_usize; malloc_mutex_unlock(old_ctx->lock); told_cnt = (prof_thr_cnt_t *)(uintptr_t)1U; } @@ -505,23 +539,23 @@ prof_realloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt, if ((uintptr_t)told_cnt > (uintptr_t)1U) told_cnt->epoch++; if ((uintptr_t)cnt > (uintptr_t)1U) { - prof_ctx_set(ptr, cnt->ctx); + prof_ctx_set(ptr, usize, cnt->ctx); cnt->epoch++; } else if (ptr != NULL) - prof_ctx_set(ptr, (prof_ctx_t *)(uintptr_t)1U); + prof_ctx_set(ptr, usize, (prof_ctx_t *)(uintptr_t)1U); /*********/ mb_write(); /*********/ if ((uintptr_t)told_cnt > (uintptr_t)1U) { told_cnt->cnts.curobjs--; - told_cnt->cnts.curbytes -= old_size; + told_cnt->cnts.curbytes -= old_usize; } if ((uintptr_t)cnt > (uintptr_t)1U) { cnt->cnts.curobjs++; - cnt->cnts.curbytes += size; + cnt->cnts.curbytes += usize; if (opt_prof_accum) { cnt->cnts.accumobjs++; - cnt->cnts.accumbytes += size; + cnt->cnts.accumbytes += usize; } } /*********/ diff --git a/dep/jemalloc/include/jemalloc/internal/ql.h b/dep/jemalloc/include/jemalloc/internal/ql.h index a9ed2393f0c..f70c5f6f391 100644 --- a/dep/jemalloc/include/jemalloc/internal/ql.h +++ b/dep/jemalloc/include/jemalloc/internal/ql.h @@ -1,61 +1,61 @@ /* * List definitions. */ -#define ql_head(a_type) \ +#define ql_head(a_type) \ struct { \ a_type *qlh_first; \ } -#define ql_head_initializer(a_head) {NULL} +#define ql_head_initializer(a_head) {NULL} -#define ql_elm(a_type) qr(a_type) +#define ql_elm(a_type) qr(a_type) /* List functions. */ -#define ql_new(a_head) do { \ +#define ql_new(a_head) do { \ (a_head)->qlh_first = NULL; \ } while (0) -#define ql_elm_new(a_elm, a_field) qr_new((a_elm), a_field) +#define ql_elm_new(a_elm, a_field) qr_new((a_elm), a_field) -#define ql_first(a_head) ((a_head)->qlh_first) +#define ql_first(a_head) ((a_head)->qlh_first) -#define ql_last(a_head, a_field) \ +#define ql_last(a_head, a_field) \ ((ql_first(a_head) != NULL) \ ? qr_prev(ql_first(a_head), a_field) : NULL) -#define ql_next(a_head, a_elm, a_field) \ +#define ql_next(a_head, a_elm, a_field) \ ((ql_last(a_head, a_field) != (a_elm)) \ ? qr_next((a_elm), a_field) : NULL) -#define ql_prev(a_head, a_elm, a_field) \ +#define ql_prev(a_head, a_elm, a_field) \ ((ql_first(a_head) != (a_elm)) ? qr_prev((a_elm), a_field) \ : NULL) -#define ql_before_insert(a_head, a_qlelm, a_elm, a_field) do { \ +#define ql_before_insert(a_head, a_qlelm, a_elm, a_field) do { \ qr_before_insert((a_qlelm), (a_elm), a_field); \ if (ql_first(a_head) == (a_qlelm)) { \ ql_first(a_head) = (a_elm); \ } \ } while (0) -#define ql_after_insert(a_qlelm, a_elm, a_field) \ +#define ql_after_insert(a_qlelm, a_elm, a_field) \ qr_after_insert((a_qlelm), (a_elm), a_field) -#define ql_head_insert(a_head, a_elm, a_field) do { \ +#define ql_head_insert(a_head, a_elm, a_field) do { \ if (ql_first(a_head) != NULL) { \ qr_before_insert(ql_first(a_head), (a_elm), a_field); \ } \ ql_first(a_head) = (a_elm); \ } while (0) -#define ql_tail_insert(a_head, a_elm, a_field) do { \ +#define ql_tail_insert(a_head, a_elm, a_field) do { \ if (ql_first(a_head) != NULL) { \ qr_before_insert(ql_first(a_head), (a_elm), a_field); \ } \ ql_first(a_head) = qr_next((a_elm), a_field); \ } while (0) -#define ql_remove(a_head, a_elm, a_field) do { \ +#define ql_remove(a_head, a_elm, a_field) do { \ if (ql_first(a_head) == (a_elm)) { \ ql_first(a_head) = qr_next(ql_first(a_head), a_field); \ } \ @@ -66,18 +66,18 @@ struct { \ } \ } while (0) -#define ql_head_remove(a_head, a_type, a_field) do { \ +#define ql_head_remove(a_head, a_type, a_field) do { \ a_type *t = ql_first(a_head); \ ql_remove((a_head), t, a_field); \ } while (0) -#define ql_tail_remove(a_head, a_type, a_field) do { \ +#define ql_tail_remove(a_head, a_type, a_field) do { \ a_type *t = ql_last(a_head, a_field); \ ql_remove((a_head), t, a_field); \ } while (0) -#define ql_foreach(a_var, a_head, a_field) \ +#define ql_foreach(a_var, a_head, a_field) \ qr_foreach((a_var), ql_first(a_head), a_field) -#define ql_reverse_foreach(a_var, a_head, a_field) \ +#define ql_reverse_foreach(a_var, a_head, a_field) \ qr_reverse_foreach((a_var), ql_first(a_head), a_field) diff --git a/dep/jemalloc/include/jemalloc/internal/qr.h b/dep/jemalloc/include/jemalloc/internal/qr.h index fe22352fedd..602944b9b4f 100644 --- a/dep/jemalloc/include/jemalloc/internal/qr.h +++ b/dep/jemalloc/include/jemalloc/internal/qr.h @@ -1,28 +1,28 @@ /* Ring definitions. */ -#define qr(a_type) \ +#define qr(a_type) \ struct { \ a_type *qre_next; \ a_type *qre_prev; \ } /* Ring functions. */ -#define qr_new(a_qr, a_field) do { \ +#define qr_new(a_qr, a_field) do { \ (a_qr)->a_field.qre_next = (a_qr); \ (a_qr)->a_field.qre_prev = (a_qr); \ } while (0) -#define qr_next(a_qr, a_field) ((a_qr)->a_field.qre_next) +#define qr_next(a_qr, a_field) ((a_qr)->a_field.qre_next) -#define qr_prev(a_qr, a_field) ((a_qr)->a_field.qre_prev) +#define qr_prev(a_qr, a_field) ((a_qr)->a_field.qre_prev) -#define qr_before_insert(a_qrelm, a_qr, a_field) do { \ +#define qr_before_insert(a_qrelm, a_qr, a_field) do { \ (a_qr)->a_field.qre_prev = (a_qrelm)->a_field.qre_prev; \ (a_qr)->a_field.qre_next = (a_qrelm); \ (a_qr)->a_field.qre_prev->a_field.qre_next = (a_qr); \ (a_qrelm)->a_field.qre_prev = (a_qr); \ } while (0) -#define qr_after_insert(a_qrelm, a_qr, a_field) \ +#define qr_after_insert(a_qrelm, a_qr, a_field) \ do \ { \ (a_qr)->a_field.qre_next = (a_qrelm)->a_field.qre_next; \ @@ -31,7 +31,7 @@ struct { \ (a_qrelm)->a_field.qre_next = (a_qr); \ } while (0) -#define qr_meld(a_qr_a, a_qr_b, a_field) do { \ +#define qr_meld(a_qr_a, a_qr_b, a_field) do { \ void *t; \ (a_qr_a)->a_field.qre_prev->a_field.qre_next = (a_qr_b); \ (a_qr_b)->a_field.qre_prev->a_field.qre_next = (a_qr_a); \ @@ -42,10 +42,10 @@ struct { \ /* qr_meld() and qr_split() are functionally equivalent, so there's no need to * have two copies of the code. */ -#define qr_split(a_qr_a, a_qr_b, a_field) \ +#define qr_split(a_qr_a, a_qr_b, a_field) \ qr_meld((a_qr_a), (a_qr_b), a_field) -#define qr_remove(a_qr, a_field) do { \ +#define qr_remove(a_qr, a_field) do { \ (a_qr)->a_field.qre_prev->a_field.qre_next \ = (a_qr)->a_field.qre_next; \ (a_qr)->a_field.qre_next->a_field.qre_prev \ @@ -54,13 +54,13 @@ struct { \ (a_qr)->a_field.qre_prev = (a_qr); \ } while (0) -#define qr_foreach(var, a_qr, a_field) \ +#define qr_foreach(var, a_qr, a_field) \ for ((var) = (a_qr); \ (var) != NULL; \ (var) = (((var)->a_field.qre_next != (a_qr)) \ ? (var)->a_field.qre_next : NULL)) -#define qr_reverse_foreach(var, a_qr, a_field) \ +#define qr_reverse_foreach(var, a_qr, a_field) \ for ((var) = ((a_qr) != NULL) ? qr_prev(a_qr, a_field) : NULL; \ (var) != NULL; \ (var) = (((var) != (a_qr)) \ diff --git a/dep/jemalloc/include/jemalloc/internal/rb.h b/dep/jemalloc/include/jemalloc/internal/rb.h index 7b675f09051..423802eb2dc 100644 --- a/dep/jemalloc/include/jemalloc/internal/rb.h +++ b/dep/jemalloc/include/jemalloc/internal/rb.h @@ -22,10 +22,6 @@ #ifndef RB_H_ #define RB_H_ -#if 0 -__FBSDID("$FreeBSD: head/lib/libc/stdlib/rb.h 204493 2010-02-28 22:57:13Z jasone $"); -#endif - #ifdef RB_COMPACT /* Node structure. */ #define rb_node(a_type) \ diff --git a/dep/jemalloc/include/jemalloc/internal/rtree.h b/dep/jemalloc/include/jemalloc/internal/rtree.h index 9bd98548cfe..bc74769f50e 100644 --- a/dep/jemalloc/include/jemalloc/internal/rtree.h +++ b/dep/jemalloc/include/jemalloc/internal/rtree.h @@ -14,17 +14,18 @@ typedef struct rtree_s rtree_t; * Size of each radix tree node (must be a power of 2). This impacts tree * depth. */ -#if (LG_SIZEOF_PTR == 2) -# define RTREE_NODESIZE (1U << 14) -#else -# define RTREE_NODESIZE CACHELINE -#endif +#define RTREE_NODESIZE (1U << 16) + +typedef void *(rtree_alloc_t)(size_t); +typedef void (rtree_dalloc_t)(void *); #endif /* JEMALLOC_H_TYPES */ /******************************************************************************/ #ifdef JEMALLOC_H_STRUCTS struct rtree_s { + rtree_alloc_t *alloc; + rtree_dalloc_t *dalloc; malloc_mutex_t mutex; void **root; unsigned height; @@ -35,7 +36,8 @@ struct rtree_s { /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS -rtree_t *rtree_new(unsigned bits); +rtree_t *rtree_new(unsigned bits, rtree_alloc_t *alloc, rtree_dalloc_t *dalloc); +void rtree_delete(rtree_t *rtree); void rtree_prefork(rtree_t *rtree); void rtree_postfork_parent(rtree_t *rtree); void rtree_postfork_child(rtree_t *rtree); @@ -45,20 +47,20 @@ void rtree_postfork_child(rtree_t *rtree); #ifdef JEMALLOC_H_INLINES #ifndef JEMALLOC_ENABLE_INLINE -#ifndef JEMALLOC_DEBUG -void *rtree_get_locked(rtree_t *rtree, uintptr_t key); +#ifdef JEMALLOC_DEBUG +uint8_t rtree_get_locked(rtree_t *rtree, uintptr_t key); #endif -void *rtree_get(rtree_t *rtree, uintptr_t key); -bool rtree_set(rtree_t *rtree, uintptr_t key, void *val); +uint8_t rtree_get(rtree_t *rtree, uintptr_t key); +bool rtree_set(rtree_t *rtree, uintptr_t key, uint8_t val); #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_RTREE_C_)) #define RTREE_GET_GENERATE(f) \ /* The least significant bits of the key are ignored. */ \ -JEMALLOC_INLINE void * \ +JEMALLOC_INLINE uint8_t \ f(rtree_t *rtree, uintptr_t key) \ { \ - void *ret; \ + uint8_t ret; \ uintptr_t subkey; \ unsigned i, lshift, height, bits; \ void **node, **child; \ @@ -68,12 +70,12 @@ f(rtree_t *rtree, uintptr_t key) \ i < height - 1; \ i++, lshift += bits, node = child) { \ bits = rtree->level2bits[i]; \ - subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR + \ + subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR + \ 3)) - bits); \ child = (void**)node[subkey]; \ if (child == NULL) { \ RTREE_UNLOCK(&rtree->mutex); \ - return (NULL); \ + return (0); \ } \ } \ \ @@ -84,7 +86,10 @@ f(rtree_t *rtree, uintptr_t key) \ bits = rtree->level2bits[i]; \ subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) - \ bits); \ - ret = node[subkey]; \ + { \ + uint8_t *leaf = (uint8_t *)node; \ + ret = leaf[subkey]; \ + } \ RTREE_UNLOCK(&rtree->mutex); \ \ RTREE_GET_VALIDATE \ @@ -123,7 +128,7 @@ RTREE_GET_GENERATE(rtree_get) #undef RTREE_GET_VALIDATE JEMALLOC_INLINE bool -rtree_set(rtree_t *rtree, uintptr_t key, void *val) +rtree_set(rtree_t *rtree, uintptr_t key, uint8_t val) { uintptr_t subkey; unsigned i, lshift, height, bits; @@ -138,14 +143,14 @@ rtree_set(rtree_t *rtree, uintptr_t key, void *val) bits); child = (void**)node[subkey]; if (child == NULL) { - child = (void**)base_alloc(sizeof(void *) << - rtree->level2bits[i+1]); + size_t size = ((i + 1 < height - 1) ? sizeof(void *) + : (sizeof(uint8_t))) << rtree->level2bits[i+1]; + child = (void**)rtree->alloc(size); if (child == NULL) { malloc_mutex_unlock(&rtree->mutex); return (true); } - memset(child, 0, sizeof(void *) << - rtree->level2bits[i+1]); + memset(child, 0, size); node[subkey] = child; } } @@ -153,7 +158,10 @@ rtree_set(rtree_t *rtree, uintptr_t key, void *val) /* node is a leaf, so it contains values rather than node pointers. */ bits = rtree->level2bits[i]; subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) - bits); - node[subkey] = val; + { + uint8_t *leaf = (uint8_t *)node; + leaf[subkey] = val; + } malloc_mutex_unlock(&rtree->mutex); return (false); diff --git a/dep/jemalloc/include/jemalloc/internal/tcache.h b/dep/jemalloc/include/jemalloc/internal/tcache.h index ba36204ff21..c3d4b58d4dc 100644 --- a/dep/jemalloc/include/jemalloc/internal/tcache.h +++ b/dep/jemalloc/include/jemalloc/internal/tcache.h @@ -297,6 +297,7 @@ tcache_alloc_small(tcache_t *tcache, size_t size, bool zero) binind = SMALL_SIZE2BIN(size); assert(binind < NBINS); tbin = &tcache->tbins[binind]; + size = arena_bin_info[binind].reg_size; ret = tcache_alloc_easy(tbin); if (ret == NULL) { ret = tcache_alloc_small_hard(tcache, tbin, binind); @@ -313,6 +314,7 @@ tcache_alloc_small(tcache_t *tcache, size_t size, bool zero) } else if (opt_zero) memset(ret, 0, size); } + VALGRIND_MAKE_MEM_UNDEFINED(ret, size); } else { if (config_fill && opt_junk) { arena_alloc_junk_small(ret, &arena_bin_info[binind], @@ -321,7 +323,6 @@ tcache_alloc_small(tcache_t *tcache, size_t size, bool zero) VALGRIND_MAKE_MEM_UNDEFINED(ret, size); memset(ret, 0, size); } - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); if (config_stats) tbin->tstats.nrequests++; @@ -368,11 +369,11 @@ tcache_alloc_large(tcache_t *tcache, size_t size, bool zero) else if (opt_zero) memset(ret, 0, size); } + VALGRIND_MAKE_MEM_UNDEFINED(ret, size); } else { VALGRIND_MAKE_MEM_UNDEFINED(ret, size); memset(ret, 0, size); } - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); if (config_stats) tbin->tstats.nrequests++; diff --git a/dep/jemalloc/include/jemalloc/internal/tsd.h b/dep/jemalloc/include/jemalloc/internal/tsd.h index 0037cf35e70..9fb4a23ec6b 100644 --- a/dep/jemalloc/include/jemalloc/internal/tsd.h +++ b/dep/jemalloc/include/jemalloc/internal/tsd.h @@ -6,6 +6,12 @@ typedef bool (*malloc_tsd_cleanup_t)(void); +#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \ + !defined(_WIN32)) +typedef struct tsd_init_block_s tsd_init_block_t; +typedef struct tsd_init_head_s tsd_init_head_t; +#endif + /* * TLS/TSD-agnostic macro-based implementation of thread-specific data. There * are four macros that support (at least) three use cases: file-private, @@ -75,12 +81,13 @@ extern __thread a_type a_name##_tls; \ extern pthread_key_t a_name##_tsd; \ extern bool a_name##_booted; #elif (defined(_WIN32)) -#define malloc_tsd_externs(a_name, a_type) \ +#define malloc_tsd_externs(a_name, a_type) \ extern DWORD a_name##_tsd; \ extern bool a_name##_booted; #else #define malloc_tsd_externs(a_name, a_type) \ extern pthread_key_t a_name##_tsd; \ +extern tsd_init_head_t a_name##_tsd_init_head; \ extern bool a_name##_booted; #endif @@ -105,6 +112,10 @@ a_attr bool a_name##_booted = false; #else #define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ a_attr pthread_key_t a_name##_tsd; \ +a_attr tsd_init_head_t a_name##_tsd_init_head = { \ + ql_head_initializer(blocks), \ + MALLOC_MUTEX_INITIALIZER \ +}; \ a_attr bool a_name##_booted = false; #endif @@ -333,8 +344,14 @@ a_name##_tsd_get_wrapper(void) \ pthread_getspecific(a_name##_tsd); \ \ if (wrapper == NULL) { \ + tsd_init_block_t block; \ + wrapper = tsd_init_check_recursion( \ + &a_name##_tsd_init_head, &block); \ + if (wrapper) \ + return (wrapper); \ wrapper = (a_name##_tsd_wrapper_t *) \ malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t)); \ + block.data = wrapper; \ if (wrapper == NULL) { \ malloc_write("<jemalloc>: Error allocating" \ " TSD for "#a_name"\n"); \ @@ -350,6 +367,7 @@ a_name##_tsd_get_wrapper(void) \ " TSD for "#a_name"\n"); \ abort(); \ } \ + tsd_init_finish(&a_name##_tsd_init_head, &block); \ } \ return (wrapper); \ } \ @@ -379,6 +397,19 @@ a_name##_tsd_set(a_type *val) \ /******************************************************************************/ #ifdef JEMALLOC_H_STRUCTS +#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \ + !defined(_WIN32)) +struct tsd_init_block_s { + ql_elm(tsd_init_block_t) link; + pthread_t thread; + void *data; +}; +struct tsd_init_head_s { + ql_head(tsd_init_block_t) blocks; + malloc_mutex_t lock; +}; +#endif + #endif /* JEMALLOC_H_STRUCTS */ /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS @@ -388,6 +419,12 @@ void malloc_tsd_dalloc(void *wrapper); void malloc_tsd_no_cleanup(void *); void malloc_tsd_cleanup_register(bool (*f)(void)); void malloc_tsd_boot(void); +#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \ + !defined(_WIN32)) +void *tsd_init_check_recursion(tsd_init_head_t *head, + tsd_init_block_t *block); +void tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block); +#endif #endif /* JEMALLOC_H_EXTERNS */ /******************************************************************************/ diff --git a/dep/jemalloc/include/jemalloc/internal/util.h b/dep/jemalloc/include/jemalloc/internal/util.h index 8479693631a..6b938f74688 100644 --- a/dep/jemalloc/include/jemalloc/internal/util.h +++ b/dep/jemalloc/include/jemalloc/internal/util.h @@ -14,7 +14,7 @@ * Wrap a cpp argument that contains commas such that it isn't broken up into * multiple arguments. */ -#define JEMALLOC_CONCAT(...) __VA_ARGS__ +#define JEMALLOC_ARG_CONCAT(...) __VA_ARGS__ /* * Silence compiler warnings due to uninitialized values. This is used @@ -42,12 +42,6 @@ } while (0) #endif -/* Use to assert a particular configuration, e.g., cassert(config_debug). */ -#define cassert(c) do { \ - if ((c) == false) \ - assert(false); \ -} while (0) - #ifndef not_reached #define not_reached() do { \ if (config_debug) { \ @@ -69,10 +63,18 @@ } while (0) #endif +#ifndef assert_not_implemented #define assert_not_implemented(e) do { \ if (config_debug && !(e)) \ not_implemented(); \ } while (0) +#endif + +/* Use to assert a particular configuration, e.g., cassert(config_debug). */ +#define cassert(c) do { \ + if ((c) == false) \ + not_reached(); \ +} while (0) #endif /* JEMALLOC_H_TYPES */ /******************************************************************************/ @@ -82,8 +84,9 @@ /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS -int buferror(char *buf, size_t buflen); -uintmax_t malloc_strtoumax(const char *nptr, char **endptr, int base); +int buferror(int err, char *buf, size_t buflen); +uintmax_t malloc_strtoumax(const char *restrict nptr, + char **restrict endptr, int base); void malloc_write(const char *s); /* @@ -107,7 +110,6 @@ void malloc_printf(const char *format, ...) #ifndef JEMALLOC_ENABLE_INLINE size_t pow2_ceil(size_t x); -void malloc_write(const char *s); void set_errno(int errnum); int get_errno(void); #endif diff --git a/dep/jemalloc/include/jemalloc/jemalloc.h b/dep/jemalloc/include/jemalloc/jemalloc.h index 946c73b75e5..b8ea851e525 100644 --- a/dep/jemalloc/include/jemalloc/jemalloc.h +++ b/dep/jemalloc/include/jemalloc/jemalloc.h @@ -7,36 +7,45 @@ extern "C" { #include <limits.h> #include <strings.h> -#define JEMALLOC_VERSION "3.3.1-0-g9ef9d9e8c271cdf14f664b871a8f98c827714784" +#define JEMALLOC_VERSION "3.6.0-0-g46c0af68bd248b04df75e4f92d5fb804c3d75340" #define JEMALLOC_VERSION_MAJOR 3 -#define JEMALLOC_VERSION_MINOR 3 -#define JEMALLOC_VERSION_BUGFIX 1 +#define JEMALLOC_VERSION_MINOR 6 +#define JEMALLOC_VERSION_BUGFIX 0 #define JEMALLOC_VERSION_NREV 0 -#define JEMALLOC_VERSION_GID "9ef9d9e8c271cdf14f664b871a8f98c827714784" +#define JEMALLOC_VERSION_GID "46c0af68bd248b04df75e4f92d5fb804c3d75340" -#include "jemalloc_defs.h" +# define MALLOCX_LG_ALIGN(la) (la) +# if LG_SIZEOF_PTR == 2 +# define MALLOCX_ALIGN(a) (ffs(a)-1) +# else +# define MALLOCX_ALIGN(a) \ + ((a < (size_t)INT_MAX) ? ffs(a)-1 : ffs(a>>32)+31) +# endif +# define MALLOCX_ZERO ((int)0x40) +/* Bias arena index bits so that 0 encodes "MALLOCX_ARENA() unspecified". */ +# define MALLOCX_ARENA(a) ((int)(((a)+1) << 8)) #ifdef JEMALLOC_EXPERIMENTAL -#define ALLOCM_LG_ALIGN(la) (la) -#if LG_SIZEOF_PTR == 2 -#define ALLOCM_ALIGN(a) (ffs(a)-1) -#else -#define ALLOCM_ALIGN(a) ((a < (size_t)INT_MAX) ? ffs(a)-1 : ffs(a>>32)+31) -#endif -#define ALLOCM_ZERO ((int)0x40) -#define ALLOCM_NO_MOVE ((int)0x80) +# define ALLOCM_LG_ALIGN(la) (la) +# if LG_SIZEOF_PTR == 2 +# define ALLOCM_ALIGN(a) (ffs(a)-1) +# else +# define ALLOCM_ALIGN(a) \ + ((a < (size_t)INT_MAX) ? ffs(a)-1 : ffs(a>>32)+31) +# endif +# define ALLOCM_ZERO ((int)0x40) +# define ALLOCM_NO_MOVE ((int)0x80) /* Bias arena index bits so that 0 encodes "ALLOCM_ARENA() unspecified". */ -#define ALLOCM_ARENA(a) ((int)(((a)+1) << 8)) - -#define ALLOCM_SUCCESS 0 -#define ALLOCM_ERR_OOM 1 -#define ALLOCM_ERR_NOT_MOVED 2 +# define ALLOCM_ARENA(a) ((int)(((a)+1) << 8)) +# define ALLOCM_SUCCESS 0 +# define ALLOCM_ERR_OOM 1 +# define ALLOCM_ERR_NOT_MOVED 2 #endif /* - * The je_ prefix on the following public symbol declarations is an artifact of - * namespace management, and should be omitted in application code unless - * JEMALLOC_NO_DEMANGLE is defined (see below). + * The je_ prefix on the following public symbol declarations is an artifact + * of namespace management, and should be omitted in application code unless + * JEMALLOC_NO_DEMANGLE is defined (see jemalloc_mangle.h). */ extern JEMALLOC_EXPORT const char *je_malloc_conf; extern JEMALLOC_EXPORT void (*je_malloc_message)(void *cbopaque, @@ -52,6 +61,25 @@ JEMALLOC_EXPORT void *je_aligned_alloc(size_t alignment, size_t size) JEMALLOC_EXPORT void *je_realloc(void *ptr, size_t size); JEMALLOC_EXPORT void je_free(void *ptr); +JEMALLOC_EXPORT void *je_mallocx(size_t size, int flags); +JEMALLOC_EXPORT void *je_rallocx(void *ptr, size_t size, int flags); +JEMALLOC_EXPORT size_t je_xallocx(void *ptr, size_t size, size_t extra, + int flags); +JEMALLOC_EXPORT size_t je_sallocx(const void *ptr, int flags); +JEMALLOC_EXPORT void je_dallocx(void *ptr, int flags); +JEMALLOC_EXPORT size_t je_nallocx(size_t size, int flags); + +JEMALLOC_EXPORT int je_mallctl(const char *name, void *oldp, + size_t *oldlenp, void *newp, size_t newlen); +JEMALLOC_EXPORT int je_mallctlnametomib(const char *name, size_t *mibp, + size_t *miblenp); +JEMALLOC_EXPORT int je_mallctlbymib(const size_t *mib, size_t miblen, + void *oldp, size_t *oldlenp, void *newp, size_t newlen); +JEMALLOC_EXPORT void je_malloc_stats_print(void (*write_cb)(void *, + const char *), void *je_cbopaque, const char *opts); +JEMALLOC_EXPORT size_t je_malloc_usable_size( + JEMALLOC_USABLE_SIZE_CONST void *ptr); + #ifdef JEMALLOC_OVERRIDE_MEMALIGN JEMALLOC_EXPORT void * je_memalign(size_t alignment, size_t size) JEMALLOC_ATTR(malloc); @@ -61,17 +89,6 @@ JEMALLOC_EXPORT void * je_memalign(size_t alignment, size_t size) JEMALLOC_EXPORT void * je_valloc(size_t size) JEMALLOC_ATTR(malloc); #endif -JEMALLOC_EXPORT size_t je_malloc_usable_size( - JEMALLOC_USABLE_SIZE_CONST void *ptr); -JEMALLOC_EXPORT void je_malloc_stats_print(void (*write_cb)(void *, - const char *), void *je_cbopaque, const char *opts); -JEMALLOC_EXPORT int je_mallctl(const char *name, void *oldp, - size_t *oldlenp, void *newp, size_t newlen); -JEMALLOC_EXPORT int je_mallctlnametomib(const char *name, size_t *mibp, - size_t *miblenp); -JEMALLOC_EXPORT int je_mallctlbymib(const size_t *mib, size_t miblen, - void *oldp, size_t *oldlenp, void *newp, size_t newlen); - #ifdef JEMALLOC_EXPERIMENTAL JEMALLOC_EXPORT int je_allocm(void **ptr, size_t *rsize, size_t size, int flags) JEMALLOC_ATTR(nonnull(1)); @@ -92,63 +109,71 @@ JEMALLOC_EXPORT int je_nallocm(size_t *rsize, size_t size, int flags); * --with-mangling and/or --with-jemalloc-prefix configuration settings. */ #ifdef JEMALLOC_MANGLE -#ifndef JEMALLOC_NO_DEMANGLE -#define JEMALLOC_NO_DEMANGLE -#endif -#define malloc_conf je_malloc_conf -#define malloc_message je_malloc_message -#define malloc je_malloc -#define calloc je_calloc -#define posix_memalign je_posix_memalign -#define aligned_alloc je_aligned_alloc -#define realloc je_realloc -#define free je_free -#define malloc_usable_size je_malloc_usable_size -#define malloc_stats_print je_malloc_stats_print -#define mallctl je_mallctl -#define mallctlnametomib je_mallctlnametomib -#define mallctlbymib je_mallctlbymib -#define memalign je_memalign -#define valloc je_valloc -#ifdef JEMALLOC_EXPERIMENTAL -#define allocm je_allocm -#define rallocm je_rallocm -#define sallocm je_sallocm -#define dallocm je_dallocm -#define nallocm je_nallocm -#endif +# ifndef JEMALLOC_NO_DEMANGLE +# define JEMALLOC_NO_DEMANGLE +# endif +# define malloc_conf je_malloc_conf +# define malloc_message je_malloc_message +# define malloc je_malloc +# define calloc je_calloc +# define posix_memalign je_posix_memalign +# define aligned_alloc je_aligned_alloc +# define realloc je_realloc +# define free je_free +# define mallocx je_mallocx +# define rallocx je_rallocx +# define xallocx je_xallocx +# define sallocx je_sallocx +# define dallocx je_dallocx +# define nallocx je_nallocx +# define mallctl je_mallctl +# define mallctlnametomib je_mallctlnametomib +# define mallctlbymib je_mallctlbymib +# define malloc_stats_print je_malloc_stats_print +# define malloc_usable_size je_malloc_usable_size +# define memalign je_memalign +# define valloc je_valloc +# define allocm je_allocm +# define dallocm je_dallocm +# define nallocm je_nallocm +# define rallocm je_rallocm +# define sallocm je_sallocm #endif /* - * The je_* macros can be used as stable alternative names for the public - * jemalloc API if JEMALLOC_NO_DEMANGLE is defined. This is primarily meant - * for use in jemalloc itself, but it can be used by application code to + * The je_* macros can be used as stable alternative names for the + * public jemalloc API if JEMALLOC_NO_DEMANGLE is defined. This is primarily + * meant for use in jemalloc itself, but it can be used by application code to * provide isolation from the name mangling specified via --with-mangling * and/or --with-jemalloc-prefix. */ #ifndef JEMALLOC_NO_DEMANGLE -#undef je_malloc_conf -#undef je_malloc_message -#undef je_malloc -#undef je_calloc -#undef je_posix_memalign -#undef je_aligned_alloc -#undef je_realloc -#undef je_free -#undef je_malloc_usable_size -#undef je_malloc_stats_print -#undef je_mallctl -#undef je_mallctlnametomib -#undef je_mallctlbymib -#undef je_memalign -#undef je_valloc -#ifdef JEMALLOC_EXPERIMENTAL -#undef je_allocm -#undef je_rallocm -#undef je_sallocm -#undef je_dallocm -#undef je_nallocm -#endif +# undef je_malloc_conf +# undef je_malloc_message +# undef je_malloc +# undef je_calloc +# undef je_posix_memalign +# undef je_aligned_alloc +# undef je_realloc +# undef je_free +# undef je_mallocx +# undef je_rallocx +# undef je_xallocx +# undef je_sallocx +# undef je_dallocx +# undef je_nallocx +# undef je_mallctl +# undef je_mallctlnametomib +# undef je_mallctlbymib +# undef je_malloc_stats_print +# undef je_malloc_usable_size +# undef je_memalign +# undef je_valloc +# undef je_allocm +# undef je_dallocm +# undef je_nallocm +# undef je_rallocm +# undef je_sallocm #endif #ifdef __cplusplus diff --git a/dep/jemalloc/jemalloc_defs.h.in.cmake b/dep/jemalloc/jemalloc_defs.h.in.cmake index 9fdf53546e3..89e496f4acb 100644 --- a/dep/jemalloc/jemalloc_defs.h.in.cmake +++ b/dep/jemalloc/jemalloc_defs.h.in.cmake @@ -266,3 +266,9 @@ /* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */ #define LG_SIZEOF_INTMAX_T 3 + +/* C99 restrict keyword supported. */ +#define JEMALLOC_HAS_RESTRICT + +/* JEMALLOC_CODE_COVERAGE enables test code coverage analysis. */ +#undef JEMALLOC_CODE_COVERAGE diff --git a/dep/jemalloc/src/arena.c b/dep/jemalloc/src/arena.c index 05a787f89d9..dad707b63d0 100644 --- a/dep/jemalloc/src/arena.c +++ b/dep/jemalloc/src/arena.c @@ -38,52 +38,18 @@ const uint8_t small_size2bin[] = { }; /******************************************************************************/ -/* Function prototypes for non-inline static functions. */ - -static void arena_avail_insert(arena_t *arena, arena_chunk_t *chunk, - size_t pageind, size_t npages, bool maybe_adjac_pred, - bool maybe_adjac_succ); -static void arena_avail_remove(arena_t *arena, arena_chunk_t *chunk, - size_t pageind, size_t npages, bool maybe_adjac_pred, - bool maybe_adjac_succ); -static void arena_run_split(arena_t *arena, arena_run_t *run, size_t size, - bool large, size_t binind, bool zero); -static arena_chunk_t *arena_chunk_alloc(arena_t *arena); -static void arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk); -static arena_run_t *arena_run_alloc_helper(arena_t *arena, size_t size, - bool large, size_t binind, bool zero); -static arena_run_t *arena_run_alloc(arena_t *arena, size_t size, bool large, - size_t binind, bool zero); -static arena_chunk_t *chunks_dirty_iter_cb(arena_chunk_tree_t *tree, - arena_chunk_t *chunk, void *arg); +/* + * Function prototypes for static functions that are referenced prior to + * definition. + */ + static void arena_purge(arena_t *arena, bool all); static void arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned); -static void arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, - arena_run_t *run, size_t oldsize, size_t newsize); -static void arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, - arena_run_t *run, size_t oldsize, size_t newsize, bool dirty); -static arena_run_t *arena_bin_runs_first(arena_bin_t *bin); -static void arena_bin_runs_insert(arena_bin_t *bin, arena_run_t *run); -static void arena_bin_runs_remove(arena_bin_t *bin, arena_run_t *run); -static arena_run_t *arena_bin_nonfull_run_tryget(arena_bin_t *bin); -static arena_run_t *arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin); -static void *arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin); -static void arena_dissociate_bin_run(arena_chunk_t *chunk, arena_run_t *run, - arena_bin_t *bin); static void arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, arena_bin_t *bin); static void arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, arena_bin_t *bin); -static void arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, - void *ptr, size_t oldsize, size_t size); -static bool arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, - void *ptr, size_t oldsize, size_t size, size_t extra, bool zero); -static bool arena_ralloc_large(void *ptr, size_t oldsize, size_t size, - size_t extra, bool zero); -static size_t bin_info_run_size_calc(arena_bin_info_t *bin_info, - size_t min_run_size); -static void bin_info_init(void); /******************************************************************************/ @@ -369,62 +335,63 @@ arena_run_zero(arena_chunk_t *chunk, size_t run_ind, size_t npages) } static inline void +arena_run_page_mark_zeroed(arena_chunk_t *chunk, size_t run_ind) +{ + + VALGRIND_MAKE_MEM_DEFINED((void *)((uintptr_t)chunk + (run_ind << + LG_PAGE)), PAGE); +} + +static inline void arena_run_page_validate_zeroed(arena_chunk_t *chunk, size_t run_ind) { size_t i; UNUSED size_t *p = (size_t *)((uintptr_t)chunk + (run_ind << LG_PAGE)); - VALGRIND_MAKE_MEM_DEFINED((void *)((uintptr_t)chunk + (run_ind << - LG_PAGE)), PAGE); + arena_run_page_mark_zeroed(chunk, run_ind); for (i = 0; i < PAGE / sizeof(size_t); i++) assert(p[i] == 0); } static void -arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large, - size_t binind, bool zero) +arena_cactive_update(arena_t *arena, size_t add_pages, size_t sub_pages) { - arena_chunk_t *chunk; - size_t run_ind, total_pages, need_pages, rem_pages, i; - size_t flag_dirty; - assert((large && binind == BININD_INVALID) || (large == false && binind - != BININD_INVALID)); + if (config_stats) { + ssize_t cactive_diff = CHUNK_CEILING((arena->nactive + + add_pages) << LG_PAGE) - CHUNK_CEILING((arena->nactive - + sub_pages) << LG_PAGE); + if (cactive_diff != 0) + stats_cactive_add(cactive_diff); + } +} + +static void +arena_run_split_remove(arena_t *arena, arena_chunk_t *chunk, size_t run_ind, + size_t flag_dirty, size_t need_pages) +{ + size_t total_pages, rem_pages; - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); - run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); - flag_dirty = arena_mapbits_dirty_get(chunk, run_ind); total_pages = arena_mapbits_unallocated_size_get(chunk, run_ind) >> LG_PAGE; assert(arena_mapbits_dirty_get(chunk, run_ind+total_pages-1) == flag_dirty); - need_pages = (size >> LG_PAGE); - assert(need_pages > 0); assert(need_pages <= total_pages); rem_pages = total_pages - need_pages; arena_avail_remove(arena, chunk, run_ind, total_pages, true, true); - if (config_stats) { - /* - * Update stats_cactive if nactive is crossing a chunk - * multiple. - */ - size_t cactive_diff = CHUNK_CEILING((arena->nactive + - need_pages) << LG_PAGE) - CHUNK_CEILING(arena->nactive << - LG_PAGE); - if (cactive_diff != 0) - stats_cactive_add(cactive_diff); - } + arena_cactive_update(arena, need_pages, 0); arena->nactive += need_pages; /* Keep track of trailing unused pages for later use. */ if (rem_pages > 0) { if (flag_dirty != 0) { - arena_mapbits_unallocated_set(chunk, run_ind+need_pages, - (rem_pages << LG_PAGE), CHUNK_MAP_DIRTY); + arena_mapbits_unallocated_set(chunk, + run_ind+need_pages, (rem_pages << LG_PAGE), + flag_dirty); arena_mapbits_unallocated_set(chunk, run_ind+total_pages-1, (rem_pages << LG_PAGE), - CHUNK_MAP_DIRTY); + flag_dirty); } else { arena_mapbits_unallocated_set(chunk, run_ind+need_pages, (rem_pages << LG_PAGE), @@ -438,152 +405,219 @@ arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large, arena_avail_insert(arena, chunk, run_ind+need_pages, rem_pages, false, true); } +} - /* - * Update the page map separately for large vs. small runs, since it is - * possible to avoid iteration for large mallocs. - */ - if (large) { - if (zero) { - if (flag_dirty == 0) { - /* - * The run is clean, so some pages may be - * zeroed (i.e. never before touched). - */ - for (i = 0; i < need_pages; i++) { - if (arena_mapbits_unzeroed_get(chunk, - run_ind+i) != 0) { - arena_run_zero(chunk, run_ind+i, - 1); - } else if (config_debug) { - arena_run_page_validate_zeroed( - chunk, run_ind+i); - } +static void +arena_run_split_large_helper(arena_t *arena, arena_run_t *run, size_t size, + bool remove, bool zero) +{ + arena_chunk_t *chunk; + size_t flag_dirty, run_ind, need_pages, i; + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); + run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); + flag_dirty = arena_mapbits_dirty_get(chunk, run_ind); + need_pages = (size >> LG_PAGE); + assert(need_pages > 0); + + if (remove) { + arena_run_split_remove(arena, chunk, run_ind, flag_dirty, + need_pages); + } + + if (zero) { + if (flag_dirty == 0) { + /* + * The run is clean, so some pages may be zeroed (i.e. + * never before touched). + */ + for (i = 0; i < need_pages; i++) { + if (arena_mapbits_unzeroed_get(chunk, run_ind+i) + != 0) + arena_run_zero(chunk, run_ind+i, 1); + else if (config_debug) { + arena_run_page_validate_zeroed(chunk, + run_ind+i); + } else { + arena_run_page_mark_zeroed(chunk, + run_ind+i); } - } else { - /* - * The run is dirty, so all pages must be - * zeroed. - */ - arena_run_zero(chunk, run_ind, need_pages); } + } else { + /* The run is dirty, so all pages must be zeroed. */ + arena_run_zero(chunk, run_ind, need_pages); } - - /* - * Set the last element first, in case the run only contains one - * page (i.e. both statements set the same element). - */ - arena_mapbits_large_set(chunk, run_ind+need_pages-1, 0, - flag_dirty); - arena_mapbits_large_set(chunk, run_ind, size, flag_dirty); } else { - assert(zero == false); - /* - * Propagate the dirty and unzeroed flags to the allocated - * small run, so that arena_dalloc_bin_run() has the ability to - * conditionally trim clean pages. - */ - arena_mapbits_small_set(chunk, run_ind, 0, binind, flag_dirty); - /* - * The first page will always be dirtied during small run - * initialization, so a validation failure here would not - * actually cause an observable failure. - */ - if (config_debug && flag_dirty == 0 && - arena_mapbits_unzeroed_get(chunk, run_ind) == 0) - arena_run_page_validate_zeroed(chunk, run_ind); - for (i = 1; i < need_pages - 1; i++) { - arena_mapbits_small_set(chunk, run_ind+i, i, binind, 0); - if (config_debug && flag_dirty == 0 && - arena_mapbits_unzeroed_get(chunk, run_ind+i) == 0) { - arena_run_page_validate_zeroed(chunk, - run_ind+i); - } - } - arena_mapbits_small_set(chunk, run_ind+need_pages-1, - need_pages-1, binind, flag_dirty); - if (config_debug && flag_dirty == 0 && - arena_mapbits_unzeroed_get(chunk, run_ind+need_pages-1) == - 0) { - arena_run_page_validate_zeroed(chunk, - run_ind+need_pages-1); - } + VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + + (run_ind << LG_PAGE)), (need_pages << LG_PAGE)); } - VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + (run_ind << - LG_PAGE)), (need_pages << LG_PAGE)); + + /* + * Set the last element first, in case the run only contains one page + * (i.e. both statements set the same element). + */ + arena_mapbits_large_set(chunk, run_ind+need_pages-1, 0, flag_dirty); + arena_mapbits_large_set(chunk, run_ind, size, flag_dirty); +} + +static void +arena_run_split_large(arena_t *arena, arena_run_t *run, size_t size, bool zero) +{ + + arena_run_split_large_helper(arena, run, size, true, zero); +} + +static void +arena_run_init_large(arena_t *arena, arena_run_t *run, size_t size, bool zero) +{ + + arena_run_split_large_helper(arena, run, size, false, zero); +} + +static void +arena_run_split_small(arena_t *arena, arena_run_t *run, size_t size, + size_t binind) +{ + arena_chunk_t *chunk; + size_t flag_dirty, run_ind, need_pages, i; + + assert(binind != BININD_INVALID); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); + run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); + flag_dirty = arena_mapbits_dirty_get(chunk, run_ind); + need_pages = (size >> LG_PAGE); + assert(need_pages > 0); + + arena_run_split_remove(arena, chunk, run_ind, flag_dirty, need_pages); + + /* + * Propagate the dirty and unzeroed flags to the allocated small run, + * so that arena_dalloc_bin_run() has the ability to conditionally trim + * clean pages. + */ + arena_mapbits_small_set(chunk, run_ind, 0, binind, flag_dirty); + /* + * The first page will always be dirtied during small run + * initialization, so a validation failure here would not actually + * cause an observable failure. + */ + if (config_debug && flag_dirty == 0 && arena_mapbits_unzeroed_get(chunk, + run_ind) == 0) + arena_run_page_validate_zeroed(chunk, run_ind); + for (i = 1; i < need_pages - 1; i++) { + arena_mapbits_small_set(chunk, run_ind+i, i, binind, 0); + if (config_debug && flag_dirty == 0 && + arena_mapbits_unzeroed_get(chunk, run_ind+i) == 0) + arena_run_page_validate_zeroed(chunk, run_ind+i); + } + arena_mapbits_small_set(chunk, run_ind+need_pages-1, need_pages-1, + binind, flag_dirty); + if (config_debug && flag_dirty == 0 && arena_mapbits_unzeroed_get(chunk, + run_ind+need_pages-1) == 0) + arena_run_page_validate_zeroed(chunk, run_ind+need_pages-1); + VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + + (run_ind << LG_PAGE)), (need_pages << LG_PAGE)); } static arena_chunk_t * -arena_chunk_alloc(arena_t *arena) +arena_chunk_init_spare(arena_t *arena) { arena_chunk_t *chunk; - size_t i; - if (arena->spare != NULL) { - chunk = arena->spare; - arena->spare = NULL; + assert(arena->spare != NULL); - assert(arena_mapbits_allocated_get(chunk, map_bias) == 0); - assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0); - assert(arena_mapbits_unallocated_size_get(chunk, map_bias) == - arena_maxclass); - assert(arena_mapbits_unallocated_size_get(chunk, - chunk_npages-1) == arena_maxclass); - assert(arena_mapbits_dirty_get(chunk, map_bias) == - arena_mapbits_dirty_get(chunk, chunk_npages-1)); - } else { - bool zero; - size_t unzeroed; + chunk = arena->spare; + arena->spare = NULL; - zero = false; - malloc_mutex_unlock(&arena->lock); - chunk = (arena_chunk_t *)chunk_alloc(chunksize, chunksize, - false, &zero, arena->dss_prec); - malloc_mutex_lock(&arena->lock); - if (chunk == NULL) - return (NULL); - if (config_stats) - arena->stats.mapped += chunksize; + assert(arena_mapbits_allocated_get(chunk, map_bias) == 0); + assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0); + assert(arena_mapbits_unallocated_size_get(chunk, map_bias) == + arena_maxclass); + assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) == + arena_maxclass); + assert(arena_mapbits_dirty_get(chunk, map_bias) == + arena_mapbits_dirty_get(chunk, chunk_npages-1)); - chunk->arena = arena; + return (chunk); +} - /* - * Claim that no pages are in use, since the header is merely - * overhead. - */ - chunk->ndirty = 0; +static arena_chunk_t * +arena_chunk_init_hard(arena_t *arena) +{ + arena_chunk_t *chunk; + bool zero; + size_t unzeroed, i; - chunk->nruns_avail = 0; - chunk->nruns_adjac = 0; + assert(arena->spare == NULL); - /* - * Initialize the map to contain one maximal free untouched run. - * Mark the pages as zeroed iff chunk_alloc() returned a zeroed - * chunk. - */ - unzeroed = zero ? 0 : CHUNK_MAP_UNZEROED; - arena_mapbits_unallocated_set(chunk, map_bias, arena_maxclass, - unzeroed); - /* - * There is no need to initialize the internal page map entries - * unless the chunk is not zeroed. - */ - if (zero == false) { - for (i = map_bias+1; i < chunk_npages-1; i++) - arena_mapbits_unzeroed_set(chunk, i, unzeroed); - } else if (config_debug) { - VALGRIND_MAKE_MEM_DEFINED( - (void *)arena_mapp_get(chunk, map_bias+1), - (void *)((uintptr_t) - arena_mapp_get(chunk, chunk_npages-1) - - (uintptr_t)arena_mapp_get(chunk, map_bias+1))); + zero = false; + malloc_mutex_unlock(&arena->lock); + chunk = (arena_chunk_t *)chunk_alloc(chunksize, chunksize, false, + &zero, arena->dss_prec); + malloc_mutex_lock(&arena->lock); + if (chunk == NULL) + return (NULL); + if (config_stats) + arena->stats.mapped += chunksize; + + chunk->arena = arena; + + /* + * Claim that no pages are in use, since the header is merely overhead. + */ + chunk->ndirty = 0; + + chunk->nruns_avail = 0; + chunk->nruns_adjac = 0; + + /* + * Initialize the map to contain one maximal free untouched run. Mark + * the pages as zeroed iff chunk_alloc() returned a zeroed chunk. + */ + unzeroed = zero ? 0 : CHUNK_MAP_UNZEROED; + arena_mapbits_unallocated_set(chunk, map_bias, arena_maxclass, + unzeroed); + /* + * There is no need to initialize the internal page map entries unless + * the chunk is not zeroed. + */ + if (zero == false) { + VALGRIND_MAKE_MEM_UNDEFINED((void *)arena_mapp_get(chunk, + map_bias+1), (size_t)((uintptr_t) arena_mapp_get(chunk, + chunk_npages-1) - (uintptr_t)arena_mapp_get(chunk, + map_bias+1))); + for (i = map_bias+1; i < chunk_npages-1; i++) + arena_mapbits_unzeroed_set(chunk, i, unzeroed); + } else { + VALGRIND_MAKE_MEM_DEFINED((void *)arena_mapp_get(chunk, + map_bias+1), (size_t)((uintptr_t) arena_mapp_get(chunk, + chunk_npages-1) - (uintptr_t)arena_mapp_get(chunk, + map_bias+1))); + if (config_debug) { for (i = map_bias+1; i < chunk_npages-1; i++) { assert(arena_mapbits_unzeroed_get(chunk, i) == unzeroed); } } - arena_mapbits_unallocated_set(chunk, chunk_npages-1, - arena_maxclass, unzeroed); + } + arena_mapbits_unallocated_set(chunk, chunk_npages-1, arena_maxclass, + unzeroed); + + return (chunk); +} + +static arena_chunk_t * +arena_chunk_alloc(arena_t *arena) +{ + arena_chunk_t *chunk; + + if (arena->spare != NULL) + chunk = arena_chunk_init_spare(arena); + else { + chunk = arena_chunk_init_hard(arena); + if (chunk == NULL) + return (NULL); } /* Insert the run into the runs_avail tree. */ @@ -626,8 +660,7 @@ arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) } static arena_run_t * -arena_run_alloc_helper(arena_t *arena, size_t size, bool large, size_t binind, - bool zero) +arena_run_alloc_large_helper(arena_t *arena, size_t size, bool zero) { arena_run_t *run; arena_chunk_map_t *mapelm, key; @@ -642,7 +675,7 @@ arena_run_alloc_helper(arena_t *arena, size_t size, bool large, size_t binind, run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << LG_PAGE)); - arena_run_split(arena, run, size, large, binind, zero); + arena_run_split_large(arena, run, size, zero); return (run); } @@ -650,19 +683,16 @@ arena_run_alloc_helper(arena_t *arena, size_t size, bool large, size_t binind, } static arena_run_t * -arena_run_alloc(arena_t *arena, size_t size, bool large, size_t binind, - bool zero) +arena_run_alloc_large(arena_t *arena, size_t size, bool zero) { arena_chunk_t *chunk; arena_run_t *run; assert(size <= arena_maxclass); assert((size & PAGE_MASK) == 0); - assert((large && binind == BININD_INVALID) || (large == false && binind - != BININD_INVALID)); /* Search the arena's chunks for the lowest best fit. */ - run = arena_run_alloc_helper(arena, size, large, binind, zero); + run = arena_run_alloc_large_helper(arena, size, zero); if (run != NULL) return (run); @@ -672,7 +702,7 @@ arena_run_alloc(arena_t *arena, size_t size, bool large, size_t binind, chunk = arena_chunk_alloc(arena); if (chunk != NULL) { run = (arena_run_t *)((uintptr_t)chunk + (map_bias << LG_PAGE)); - arena_run_split(arena, run, size, large, binind, zero); + arena_run_split_large(arena, run, size, zero); return (run); } @@ -681,7 +711,63 @@ arena_run_alloc(arena_t *arena, size_t size, bool large, size_t binind, * sufficient memory available while this one dropped arena->lock in * arena_chunk_alloc(), so search one more time. */ - return (arena_run_alloc_helper(arena, size, large, binind, zero)); + return (arena_run_alloc_large_helper(arena, size, zero)); +} + +static arena_run_t * +arena_run_alloc_small_helper(arena_t *arena, size_t size, size_t binind) +{ + arena_run_t *run; + arena_chunk_map_t *mapelm, key; + + key.bits = size | CHUNK_MAP_KEY; + mapelm = arena_avail_tree_nsearch(&arena->runs_avail, &key); + if (mapelm != NULL) { + arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); + size_t pageind = (((uintptr_t)mapelm - + (uintptr_t)run_chunk->map) / sizeof(arena_chunk_map_t)) + + map_bias; + + run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << + LG_PAGE)); + arena_run_split_small(arena, run, size, binind); + return (run); + } + + return (NULL); +} + +static arena_run_t * +arena_run_alloc_small(arena_t *arena, size_t size, size_t binind) +{ + arena_chunk_t *chunk; + arena_run_t *run; + + assert(size <= arena_maxclass); + assert((size & PAGE_MASK) == 0); + assert(binind != BININD_INVALID); + + /* Search the arena's chunks for the lowest best fit. */ + run = arena_run_alloc_small_helper(arena, size, binind); + if (run != NULL) + return (run); + + /* + * No usable runs. Create a new chunk from which to allocate the run. + */ + chunk = arena_chunk_alloc(arena); + if (chunk != NULL) { + run = (arena_run_t *)((uintptr_t)chunk + (map_bias << LG_PAGE)); + arena_run_split_small(arena, run, size, binind); + return (run); + } + + /* + * arena_chunk_alloc() failed, but another thread may have made + * sufficient memory available while this one dropped arena->lock in + * arena_chunk_alloc(), so search one more time. + */ + return (arena_run_alloc_small_helper(arena, size, binind)); } static inline void @@ -707,48 +793,42 @@ arena_maybe_purge(arena_t *arena) arena_purge(arena, false); } -static inline size_t -arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk, bool all) +static arena_chunk_t * +chunks_dirty_iter_cb(arena_chunk_tree_t *tree, arena_chunk_t *chunk, void *arg) { - size_t npurged; - ql_head(arena_chunk_map_t) mapelms; - arena_chunk_map_t *mapelm; - size_t pageind, npages; - size_t nmadvise; + size_t *ndirty = (size_t *)arg; - ql_new(&mapelms); + assert(chunk->ndirty != 0); + *ndirty += chunk->ndirty; + return (NULL); +} + +static size_t +arena_compute_npurgatory(arena_t *arena, bool all) +{ + size_t npurgatory, npurgeable; /* - * If chunk is the spare, temporarily re-allocate it, 1) so that its - * run is reinserted into runs_avail, and 2) so that it cannot be - * completely discarded by another thread while arena->lock is dropped - * by this thread. Note that the arena_run_dalloc() call will - * implicitly deallocate the chunk, so no explicit action is required - * in this function to deallocate the chunk. - * - * Note that once a chunk contains dirty pages, it cannot again contain - * a single run unless 1) it is a dirty run, or 2) this function purges - * dirty pages and causes the transition to a single clean run. Thus - * (chunk == arena->spare) is possible, but it is not possible for - * this function to be called on the spare unless it contains a dirty - * run. + * Compute the minimum number of pages that this thread should try to + * purge. */ - if (chunk == arena->spare) { - assert(arena_mapbits_dirty_get(chunk, map_bias) != 0); - assert(arena_mapbits_dirty_get(chunk, chunk_npages-1) != 0); + npurgeable = arena->ndirty - arena->npurgatory; - arena_chunk_alloc(arena); - } + if (all == false) { + size_t threshold = (arena->nactive >> opt_lg_dirty_mult); - if (config_stats) - arena->stats.purged += chunk->ndirty; + npurgatory = npurgeable - threshold; + } else + npurgatory = npurgeable; - /* - * Operate on all dirty runs if there is no clean/dirty run - * fragmentation. - */ - if (chunk->nruns_adjac == 0) - all = true; + return (npurgatory); +} + +static void +arena_chunk_stash_dirty(arena_t *arena, arena_chunk_t *chunk, bool all, + arena_chunk_mapelms_t *mapelms) +{ + size_t pageind, npages; /* * Temporarily allocate free dirty runs within chunk. If all is false, @@ -756,7 +836,7 @@ arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk, bool all) * all dirty runs. */ for (pageind = map_bias; pageind < chunk_npages; pageind += npages) { - mapelm = arena_mapp_get(chunk, pageind); + arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); if (arena_mapbits_allocated_get(chunk, pageind) == 0) { size_t run_size = arena_mapbits_unallocated_size_get(chunk, pageind); @@ -772,11 +852,11 @@ arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk, bool all) arena_run_t *run = (arena_run_t *)((uintptr_t) chunk + (uintptr_t)(pageind << LG_PAGE)); - arena_run_split(arena, run, run_size, true, - BININD_INVALID, false); + arena_run_split_large(arena, run, run_size, + false); /* Append to list for later processing. */ ql_elm_new(mapelm, u.ql_link); - ql_tail_insert(&mapelms, mapelm, u.ql_link); + ql_tail_insert(mapelms, mapelm, u.ql_link); } } else { /* Skip run. */ @@ -800,12 +880,20 @@ arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk, bool all) assert(pageind == chunk_npages); assert(chunk->ndirty == 0 || all == false); assert(chunk->nruns_adjac == 0); +} + +static size_t +arena_chunk_purge_stashed(arena_t *arena, arena_chunk_t *chunk, + arena_chunk_mapelms_t *mapelms) +{ + size_t npurged, pageind, npages, nmadvise; + arena_chunk_map_t *mapelm; malloc_mutex_unlock(&arena->lock); if (config_stats) nmadvise = 0; npurged = 0; - ql_foreach(mapelm, &mapelms, u.ql_link) { + ql_foreach(mapelm, mapelms, u.ql_link) { bool unzeroed; size_t flag_unzeroed, i; @@ -839,30 +927,75 @@ arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk, bool all) if (config_stats) arena->stats.nmadvise += nmadvise; + return (npurged); +} + +static void +arena_chunk_unstash_purged(arena_t *arena, arena_chunk_t *chunk, + arena_chunk_mapelms_t *mapelms) +{ + arena_chunk_map_t *mapelm; + size_t pageind; + /* Deallocate runs. */ - for (mapelm = ql_first(&mapelms); mapelm != NULL; - mapelm = ql_first(&mapelms)) { + for (mapelm = ql_first(mapelms); mapelm != NULL; + mapelm = ql_first(mapelms)) { arena_run_t *run; pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) / sizeof(arena_chunk_map_t)) + map_bias; run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)(pageind << LG_PAGE)); - ql_remove(&mapelms, mapelm, u.ql_link); + ql_remove(mapelms, mapelm, u.ql_link); arena_run_dalloc(arena, run, false, true); } - - return (npurged); } -static arena_chunk_t * -chunks_dirty_iter_cb(arena_chunk_tree_t *tree, arena_chunk_t *chunk, void *arg) +static inline size_t +arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk, bool all) { - size_t *ndirty = (size_t *)arg; + size_t npurged; + arena_chunk_mapelms_t mapelms; - assert(chunk->ndirty != 0); - *ndirty += chunk->ndirty; - return (NULL); + ql_new(&mapelms); + + /* + * If chunk is the spare, temporarily re-allocate it, 1) so that its + * run is reinserted into runs_avail, and 2) so that it cannot be + * completely discarded by another thread while arena->lock is dropped + * by this thread. Note that the arena_run_dalloc() call will + * implicitly deallocate the chunk, so no explicit action is required + * in this function to deallocate the chunk. + * + * Note that once a chunk contains dirty pages, it cannot again contain + * a single run unless 1) it is a dirty run, or 2) this function purges + * dirty pages and causes the transition to a single clean run. Thus + * (chunk == arena->spare) is possible, but it is not possible for + * this function to be called on the spare unless it contains a dirty + * run. + */ + if (chunk == arena->spare) { + assert(arena_mapbits_dirty_get(chunk, map_bias) != 0); + assert(arena_mapbits_dirty_get(chunk, chunk_npages-1) != 0); + + arena_chunk_alloc(arena); + } + + if (config_stats) + arena->stats.purged += chunk->ndirty; + + /* + * Operate on all dirty runs if there is no clean/dirty run + * fragmentation. + */ + if (chunk->nruns_adjac == 0) + all = true; + + arena_chunk_stash_dirty(arena, chunk, all, &mapelms); + npurged = arena_chunk_purge_stashed(arena, chunk, &mapelms); + arena_chunk_unstash_purged(arena, chunk, &mapelms); + + return (npurged); } static void @@ -885,21 +1018,11 @@ arena_purge(arena_t *arena, bool all) arena->stats.npurge++; /* - * Compute the minimum number of pages that this thread should try to - * purge, and add the result to arena->npurgatory. This will keep - * multiple threads from racing to reduce ndirty below the threshold. + * Add the minimum number of pages this thread should try to purge to + * arena->npurgatory. This will keep multiple threads from racing to + * reduce ndirty below the threshold. */ - { - size_t npurgeable = arena->ndirty - arena->npurgatory; - - if (all == false) { - size_t threshold = (arena->nactive >> - opt_lg_dirty_mult); - - npurgatory = npurgeable - threshold; - } else - npurgatory = npurgeable; - } + npurgatory = arena_compute_npurgatory(arena, all); arena->npurgatory += npurgatory; while (npurgatory > 0) { @@ -966,61 +1089,12 @@ arena_purge_all(arena_t *arena) } static void -arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned) +arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size, + size_t *p_run_ind, size_t *p_run_pages, size_t flag_dirty) { - arena_chunk_t *chunk; - size_t size, run_ind, run_pages, flag_dirty; - - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); - run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); - assert(run_ind >= map_bias); - assert(run_ind < chunk_npages); - if (arena_mapbits_large_get(chunk, run_ind) != 0) { - size = arena_mapbits_large_size_get(chunk, run_ind); - assert(size == PAGE || - arena_mapbits_large_size_get(chunk, - run_ind+(size>>LG_PAGE)-1) == 0); - } else { - size_t binind = arena_bin_index(arena, run->bin); - arena_bin_info_t *bin_info = &arena_bin_info[binind]; - size = bin_info->run_size; - } - run_pages = (size >> LG_PAGE); - if (config_stats) { - /* - * Update stats_cactive if nactive is crossing a chunk - * multiple. - */ - size_t cactive_diff = CHUNK_CEILING(arena->nactive << LG_PAGE) - - CHUNK_CEILING((arena->nactive - run_pages) << LG_PAGE); - if (cactive_diff != 0) - stats_cactive_sub(cactive_diff); - } - arena->nactive -= run_pages; - - /* - * The run is dirty if the caller claims to have dirtied it, as well as - * if it was already dirty before being allocated and the caller - * doesn't claim to have cleaned it. - */ - assert(arena_mapbits_dirty_get(chunk, run_ind) == - arena_mapbits_dirty_get(chunk, run_ind+run_pages-1)); - if (cleaned == false && arena_mapbits_dirty_get(chunk, run_ind) != 0) - dirty = true; - flag_dirty = dirty ? CHUNK_MAP_DIRTY : 0; - - /* Mark pages as unallocated in the chunk map. */ - if (dirty) { - arena_mapbits_unallocated_set(chunk, run_ind, size, - CHUNK_MAP_DIRTY); - arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size, - CHUNK_MAP_DIRTY); - } else { - arena_mapbits_unallocated_set(chunk, run_ind, size, - arena_mapbits_unzeroed_get(chunk, run_ind)); - arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size, - arena_mapbits_unzeroed_get(chunk, run_ind+run_pages-1)); - } + size_t size = *p_size; + size_t run_ind = *p_run_ind; + size_t run_pages = *p_run_pages; /* Try to coalesce forward. */ if (run_ind + run_pages < chunk_npages && @@ -1050,8 +1124,9 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned) } /* Try to coalesce backward. */ - if (run_ind > map_bias && arena_mapbits_allocated_get(chunk, run_ind-1) - == 0 && arena_mapbits_dirty_get(chunk, run_ind-1) == flag_dirty) { + if (run_ind > map_bias && arena_mapbits_allocated_get(chunk, + run_ind-1) == 0 && arena_mapbits_dirty_get(chunk, run_ind-1) == + flag_dirty) { size_t prun_size = arena_mapbits_unallocated_size_get(chunk, run_ind-1); size_t prun_pages = prun_size >> LG_PAGE; @@ -1076,6 +1151,62 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned) size); } + *p_size = size; + *p_run_ind = run_ind; + *p_run_pages = run_pages; +} + +static void +arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned) +{ + arena_chunk_t *chunk; + size_t size, run_ind, run_pages, flag_dirty; + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); + run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); + assert(run_ind >= map_bias); + assert(run_ind < chunk_npages); + if (arena_mapbits_large_get(chunk, run_ind) != 0) { + size = arena_mapbits_large_size_get(chunk, run_ind); + assert(size == PAGE || + arena_mapbits_large_size_get(chunk, + run_ind+(size>>LG_PAGE)-1) == 0); + } else { + size_t binind = arena_bin_index(arena, run->bin); + arena_bin_info_t *bin_info = &arena_bin_info[binind]; + size = bin_info->run_size; + } + run_pages = (size >> LG_PAGE); + arena_cactive_update(arena, 0, run_pages); + arena->nactive -= run_pages; + + /* + * The run is dirty if the caller claims to have dirtied it, as well as + * if it was already dirty before being allocated and the caller + * doesn't claim to have cleaned it. + */ + assert(arena_mapbits_dirty_get(chunk, run_ind) == + arena_mapbits_dirty_get(chunk, run_ind+run_pages-1)); + if (cleaned == false && arena_mapbits_dirty_get(chunk, run_ind) != 0) + dirty = true; + flag_dirty = dirty ? CHUNK_MAP_DIRTY : 0; + + /* Mark pages as unallocated in the chunk map. */ + if (dirty) { + arena_mapbits_unallocated_set(chunk, run_ind, size, + CHUNK_MAP_DIRTY); + arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size, + CHUNK_MAP_DIRTY); + } else { + arena_mapbits_unallocated_set(chunk, run_ind, size, + arena_mapbits_unzeroed_get(chunk, run_ind)); + arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size, + arena_mapbits_unzeroed_get(chunk, run_ind+run_pages-1)); + } + + arena_run_coalesce(arena, chunk, &size, &run_ind, &run_pages, + flag_dirty); + /* Insert into runs_avail, now that coalescing is complete. */ assert(arena_mapbits_unallocated_size_get(chunk, run_ind) == arena_mapbits_unallocated_size_get(chunk, run_ind+run_pages-1)); @@ -1243,7 +1374,7 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) malloc_mutex_unlock(&bin->lock); /******************************/ malloc_mutex_lock(&arena->lock); - run = arena_run_alloc(arena, bin_info->run_size, false, binind, false); + run = arena_run_alloc_small(arena, bin_info->run_size, binind); if (run != NULL) { bitmap_t *bitmap = (bitmap_t *)((uintptr_t)run + (uintptr_t)bin_info->bitmap_offset); @@ -1266,7 +1397,7 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) } /* - * arena_run_alloc() failed, but another thread may have made + * arena_run_alloc_small() failed, but another thread may have made * sufficient memory available while this one dropped bin->lock above, * so search one more time. */ @@ -1301,12 +1432,12 @@ arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin) arena_chunk_t *chunk; /* - * arena_run_alloc() may have allocated run, or it may - * have pulled run from the bin's run tree. Therefore - * it is unsafe to make any assumptions about how run - * has previously been used, and arena_bin_lower_run() - * must be called, as if a region were just deallocated - * from the run. + * arena_run_alloc_small() may have allocated run, or + * it may have pulled run from the bin's run tree. + * Therefore it is unsafe to make any assumptions about + * how run has previously been used, and + * arena_bin_lower_run() must be called, as if a region + * were just deallocated from the run. */ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); if (run->nfree == bin_info->nregs) @@ -1384,8 +1515,28 @@ arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info, bool zero) } } -void -arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info) +#ifdef JEMALLOC_JET +#undef arena_redzone_corruption +#define arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption_impl) +#endif +static void +arena_redzone_corruption(void *ptr, size_t usize, bool after, + size_t offset, uint8_t byte) +{ + + malloc_printf("<jemalloc>: Corrupt redzone %zu byte%s %s %p " + "(size %zu), byte=%#x\n", offset, (offset == 1) ? "" : "s", + after ? "after" : "before", ptr, usize, byte); +} +#ifdef JEMALLOC_JET +#undef arena_redzone_corruption +#define arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption) +arena_redzone_corruption_t *arena_redzone_corruption = + JEMALLOC_N(arena_redzone_corruption_impl); +#endif + +static void +arena_redzones_validate(void *ptr, arena_bin_info_t *bin_info, bool reset) { size_t size = bin_info->reg_size; size_t redzone_size = bin_info->redzone_size; @@ -1393,29 +1544,61 @@ arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info) bool error = false; for (i = 1; i <= redzone_size; i++) { - unsigned byte; - if ((byte = *(uint8_t *)((uintptr_t)ptr - i)) != 0xa5) { + uint8_t *byte = (uint8_t *)((uintptr_t)ptr - i); + if (*byte != 0xa5) { error = true; - malloc_printf("<jemalloc>: Corrupt redzone " - "%zu byte%s before %p (size %zu), byte=%#x\n", i, - (i == 1) ? "" : "s", ptr, size, byte); + arena_redzone_corruption(ptr, size, false, i, *byte); + if (reset) + *byte = 0xa5; } } for (i = 0; i < redzone_size; i++) { - unsigned byte; - if ((byte = *(uint8_t *)((uintptr_t)ptr + size + i)) != 0xa5) { + uint8_t *byte = (uint8_t *)((uintptr_t)ptr + size + i); + if (*byte != 0xa5) { error = true; - malloc_printf("<jemalloc>: Corrupt redzone " - "%zu byte%s after end of %p (size %zu), byte=%#x\n", - i, (i == 1) ? "" : "s", ptr, size, byte); + arena_redzone_corruption(ptr, size, true, i, *byte); + if (reset) + *byte = 0xa5; } } if (opt_abort && error) abort(); +} +#ifdef JEMALLOC_JET +#undef arena_dalloc_junk_small +#define arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small_impl) +#endif +void +arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info) +{ + size_t redzone_size = bin_info->redzone_size; + + arena_redzones_validate(ptr, bin_info, false); memset((void *)((uintptr_t)ptr - redzone_size), 0x5a, bin_info->reg_interval); } +#ifdef JEMALLOC_JET +#undef arena_dalloc_junk_small +#define arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small) +arena_dalloc_junk_small_t *arena_dalloc_junk_small = + JEMALLOC_N(arena_dalloc_junk_small_impl); +#endif + +void +arena_quarantine_junk_small(void *ptr, size_t usize) +{ + size_t binind; + arena_bin_info_t *bin_info; + cassert(config_fill); + assert(opt_junk); + assert(opt_quarantine); + assert(usize <= SMALL_MAXCLASS); + + binind = SMALL_SIZE2BIN(usize); + bin_info = &arena_bin_info[binind]; + arena_redzones_validate(ptr, bin_info, true); +} void * arena_malloc_small(arena_t *arena, size_t size, bool zero) @@ -1458,6 +1641,7 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero) } else if (opt_zero) memset(ret, 0, size); } + VALGRIND_MAKE_MEM_UNDEFINED(ret, size); } else { if (config_fill && opt_junk) { arena_alloc_junk_small(ret, &arena_bin_info[binind], @@ -1466,7 +1650,6 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero) VALGRIND_MAKE_MEM_UNDEFINED(ret, size); memset(ret, 0, size); } - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); return (ret); } @@ -1480,7 +1663,7 @@ arena_malloc_large(arena_t *arena, size_t size, bool zero) /* Large allocation. */ size = PAGE_CEILING(size); malloc_mutex_lock(&arena->lock); - ret = (void *)arena_run_alloc(arena, size, true, BININD_INVALID, zero); + ret = (void *)arena_run_alloc_large(arena, size, zero); if (ret == NULL) { malloc_mutex_unlock(&arena->lock); return (NULL); @@ -1526,7 +1709,7 @@ arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero) alloc_size = size + alignment - PAGE; malloc_mutex_lock(&arena->lock); - run = arena_run_alloc(arena, alloc_size, true, BININD_INVALID, zero); + run = arena_run_alloc_large(arena, alloc_size, false); if (run == NULL) { malloc_mutex_unlock(&arena->lock); return (NULL); @@ -1546,6 +1729,7 @@ arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero) arena_run_trim_tail(arena, chunk, ret, size + trailsize, size, false); } + arena_run_init_large(arena, (arena_run_t *)ret, size, zero); if (config_stats) { arena->stats.nmalloc_large++; @@ -1749,21 +1933,38 @@ arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, arena_dalloc_bin(arena, chunk, ptr, pageind, mapelm); } +#ifdef JEMALLOC_JET +#undef arena_dalloc_junk_large +#define arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large_impl) +#endif +static void +arena_dalloc_junk_large(void *ptr, size_t usize) +{ + + if (config_fill && opt_junk) + memset(ptr, 0x5a, usize); +} +#ifdef JEMALLOC_JET +#undef arena_dalloc_junk_large +#define arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large) +arena_dalloc_junk_large_t *arena_dalloc_junk_large = + JEMALLOC_N(arena_dalloc_junk_large_impl); +#endif + void arena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr) { if (config_fill || config_stats) { size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - size_t size = arena_mapbits_large_size_get(chunk, pageind); + size_t usize = arena_mapbits_large_size_get(chunk, pageind); - if (config_fill && config_stats && opt_junk) - memset(ptr, 0x5a, size); + arena_dalloc_junk_large(ptr, usize); if (config_stats) { arena->stats.ndalloc_large++; - arena->stats.allocated_large -= size; - arena->stats.lstats[(size >> LG_PAGE) - 1].ndalloc++; - arena->stats.lstats[(size >> LG_PAGE) - 1].curruns--; + arena->stats.allocated_large -= usize; + arena->stats.lstats[(usize >> LG_PAGE) - 1].ndalloc++; + arena->stats.lstats[(usize >> LG_PAGE) - 1].curruns--; } } @@ -1834,9 +2035,8 @@ arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, size_t flag_dirty; size_t splitsize = (oldsize + followsize <= size + extra) ? followsize : size + extra - oldsize; - arena_run_split(arena, (arena_run_t *)((uintptr_t)chunk + - ((pageind+npages) << LG_PAGE)), splitsize, true, - BININD_INVALID, zero); + arena_run_split_large(arena, (arena_run_t *)((uintptr_t)chunk + + ((pageind+npages) << LG_PAGE)), splitsize, zero); size = oldsize + splitsize; npages = size >> LG_PAGE; @@ -1875,6 +2075,26 @@ arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, return (true); } +#ifdef JEMALLOC_JET +#undef arena_ralloc_junk_large +#define arena_ralloc_junk_large JEMALLOC_N(arena_ralloc_junk_large_impl) +#endif +static void +arena_ralloc_junk_large(void *ptr, size_t old_usize, size_t usize) +{ + + if (config_fill && opt_junk) { + memset((void *)((uintptr_t)ptr + usize), 0x5a, + old_usize - usize); + } +} +#ifdef JEMALLOC_JET +#undef arena_ralloc_junk_large +#define arena_ralloc_junk_large JEMALLOC_N(arena_ralloc_junk_large) +arena_ralloc_junk_large_t *arena_ralloc_junk_large = + JEMALLOC_N(arena_ralloc_junk_large_impl); +#endif + /* * Try to resize a large allocation, in order to avoid copying. This will * always fail if growing an object, and the following run is already in use. @@ -1888,10 +2108,6 @@ arena_ralloc_large(void *ptr, size_t oldsize, size_t size, size_t extra, psize = PAGE_CEILING(size + extra); if (psize == oldsize) { /* Same size class. */ - if (config_fill && opt_junk && size < oldsize) { - memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - - size); - } return (false); } else { arena_chunk_t *chunk; @@ -1902,10 +2118,7 @@ arena_ralloc_large(void *ptr, size_t oldsize, size_t size, size_t extra, if (psize < oldsize) { /* Fill before shrinking in order avoid a race. */ - if (config_fill && opt_junk) { - memset((void *)((uintptr_t)ptr + size), 0x5a, - oldsize - size); - } + arena_ralloc_junk_large(ptr, oldsize, psize); arena_ralloc_large_shrink(arena, chunk, ptr, oldsize, psize); return (false); @@ -1913,17 +2126,23 @@ arena_ralloc_large(void *ptr, size_t oldsize, size_t size, size_t extra, bool ret = arena_ralloc_large_grow(arena, chunk, ptr, oldsize, PAGE_CEILING(size), psize - PAGE_CEILING(size), zero); - if (config_fill && ret == false && zero == false && - opt_zero) { - memset((void *)((uintptr_t)ptr + oldsize), 0, - size - oldsize); + if (config_fill && ret == false && zero == false) { + if (opt_junk) { + memset((void *)((uintptr_t)ptr + + oldsize), 0xa5, isalloc(ptr, + config_prof) - oldsize); + } else if (opt_zero) { + memset((void *)((uintptr_t)ptr + + oldsize), 0, isalloc(ptr, + config_prof) - oldsize); + } } return (ret); } } } -void * +bool arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra, bool zero) { @@ -1938,25 +2157,20 @@ arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra, if ((size + extra <= SMALL_MAXCLASS && SMALL_SIZE2BIN(size + extra) == SMALL_SIZE2BIN(oldsize)) || (size <= oldsize && - size + extra >= oldsize)) { - if (config_fill && opt_junk && size < oldsize) { - memset((void *)((uintptr_t)ptr + size), - 0x5a, oldsize - size); - } - return (ptr); - } + size + extra >= oldsize)) + return (false); } else { assert(size <= arena_maxclass); if (size + extra > SMALL_MAXCLASS) { if (arena_ralloc_large(ptr, oldsize, size, extra, zero) == false) - return (ptr); + return (false); } } } /* Reallocation would require a move. */ - return (NULL); + return (true); } void * @@ -1968,9 +2182,8 @@ arena_ralloc(arena_t *arena, void *ptr, size_t oldsize, size_t size, size_t copysize; /* Try to avoid moving the allocation. */ - ret = arena_ralloc_no_move(ptr, oldsize, size, extra, zero); - if (ret != NULL) - return (ret); + if (arena_ralloc_no_move(ptr, oldsize, size, extra, zero) == false) + return (ptr); /* * size and oldsize are different enough that we need to move the @@ -1981,7 +2194,7 @@ arena_ralloc(arena_t *arena, void *ptr, size_t oldsize, size_t size, size_t usize = sa2u(size + extra, alignment); if (usize == 0) return (NULL); - ret = ipallocx(usize, alignment, zero, try_tcache_alloc, arena); + ret = ipalloct(usize, alignment, zero, try_tcache_alloc, arena); } else ret = arena_malloc(arena, size + extra, zero, try_tcache_alloc); @@ -1993,7 +2206,7 @@ arena_ralloc(arena_t *arena, void *ptr, size_t oldsize, size_t size, size_t usize = sa2u(size, alignment); if (usize == 0) return (NULL); - ret = ipallocx(usize, alignment, zero, try_tcache_alloc, + ret = ipalloct(usize, alignment, zero, try_tcache_alloc, arena); } else ret = arena_malloc(arena, size, zero, try_tcache_alloc); @@ -2011,7 +2224,7 @@ arena_ralloc(arena_t *arena, void *ptr, size_t oldsize, size_t size, copysize = (size < oldsize) ? size : oldsize; VALGRIND_MAKE_MEM_UNDEFINED(ret, copysize); memcpy(ret, ptr, copysize); - iqallocx(ptr, try_tcache_dalloc); + iqalloct(ptr, try_tcache_dalloc); return (ret); } @@ -2266,7 +2479,6 @@ bin_info_run_size_calc(arena_bin_info_t *bin_info, size_t min_run_size) bin_info->reg_interval) - pad_size; } while (try_hdr_size > try_redzone0_offset); } while (try_run_size <= arena_maxclass - && try_run_size <= arena_maxclass && RUN_MAX_OVRHD * (bin_info->reg_interval << 3) > RUN_MAX_OVRHD_RELAX && (try_redzone0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size diff --git a/dep/jemalloc/src/bitmap.c b/dep/jemalloc/src/bitmap.c index b47e2629093..e2bd907d558 100644 --- a/dep/jemalloc/src/bitmap.c +++ b/dep/jemalloc/src/bitmap.c @@ -1,4 +1,4 @@ -#define JEMALLOC_BITMAP_C_ +#define JEMALLOC_BITMAP_C_ #include "jemalloc/internal/jemalloc_internal.h" /******************************************************************************/ diff --git a/dep/jemalloc/src/chunk.c b/dep/jemalloc/src/chunk.c index 044f76be96c..90ab116ae5f 100644 --- a/dep/jemalloc/src/chunk.c +++ b/dep/jemalloc/src/chunk.c @@ -180,7 +180,7 @@ chunk_alloc(size_t size, size_t alignment, bool base, bool *zero, label_return: if (ret != NULL) { if (config_ivsalloc && base == false) { - if (rtree_set(chunks_rtree, (uintptr_t)ret, ret)) { + if (rtree_set(chunks_rtree, (uintptr_t)ret, 1)) { chunk_dealloc(ret, size, true); return (NULL); } @@ -214,7 +214,7 @@ chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk, size_t size) { bool unzeroed; - extent_node_t *xnode, *node, *prev, key; + extent_node_t *xnode, *node, *prev, *xprev, key; unzeroed = pages_purge(chunk, size); VALGRIND_MAKE_MEM_NOACCESS(chunk, size); @@ -226,6 +226,8 @@ chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk, * held. */ xnode = base_node_alloc(); + /* Use xprev to implement conditional deferred deallocation of prev. */ + xprev = NULL; malloc_mutex_lock(&chunks_mtx); key.addr = (void *)((uintptr_t)chunk + size); @@ -242,8 +244,6 @@ chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk, node->size += size; node->zeroed = (node->zeroed && (unzeroed == false)); extent_tree_szad_insert(chunks_szad, node); - if (xnode != NULL) - base_node_dealloc(xnode); } else { /* Coalescing forward failed, so insert a new node. */ if (xnode == NULL) { @@ -253,10 +253,10 @@ chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk, * already been purged, so this is only a virtual * memory leak. */ - malloc_mutex_unlock(&chunks_mtx); - return; + goto label_return; } node = xnode; + xnode = NULL; /* Prevent deallocation below. */ node->addr = chunk; node->size = size; node->zeroed = (unzeroed == false); @@ -282,9 +282,19 @@ chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk, node->zeroed = (node->zeroed && prev->zeroed); extent_tree_szad_insert(chunks_szad, node); - base_node_dealloc(prev); + xprev = prev; } + +label_return: malloc_mutex_unlock(&chunks_mtx); + /* + * Deallocate xnode and/or xprev after unlocking chunks_mtx in order to + * avoid potential deadlock. + */ + if (xnode != NULL) + base_node_dealloc(xnode); + if (xprev != NULL) + base_node_dealloc(xprev); } void @@ -311,7 +321,7 @@ chunk_dealloc(void *chunk, size_t size, bool unmap) assert((size & chunksize_mask) == 0); if (config_ivsalloc) - rtree_set(chunks_rtree, (uintptr_t)chunk, NULL); + rtree_set(chunks_rtree, (uintptr_t)chunk, 0); if (config_stats || config_prof) { malloc_mutex_lock(&chunks_mtx); assert(stats_chunks.curchunks >= (size / chunksize)); @@ -346,7 +356,7 @@ chunk_boot(void) extent_tree_ad_new(&chunks_ad_dss); if (config_ivsalloc) { chunks_rtree = rtree_new((ZU(1) << (LG_SIZEOF_PTR+3)) - - opt_lg_chunk); + opt_lg_chunk, base_alloc, NULL); if (chunks_rtree == NULL) return (true); } @@ -358,7 +368,7 @@ void chunk_prefork(void) { - malloc_mutex_lock(&chunks_mtx); + malloc_mutex_prefork(&chunks_mtx); if (config_ivsalloc) rtree_prefork(chunks_rtree); chunk_dss_prefork(); diff --git a/dep/jemalloc/src/chunk_dss.c b/dep/jemalloc/src/chunk_dss.c index 24781cc52dc..510bb8bee85 100644 --- a/dep/jemalloc/src/chunk_dss.c +++ b/dep/jemalloc/src/chunk_dss.c @@ -28,16 +28,17 @@ static void *dss_max; /******************************************************************************/ -#ifndef JEMALLOC_HAVE_SBRK static void * -sbrk(intptr_t increment) +chunk_dss_sbrk(intptr_t increment) { +#ifdef JEMALLOC_HAVE_SBRK + return (sbrk(increment)); +#else not_implemented(); - return (NULL); -} #endif +} dss_prec_t chunk_dss_prec_get(void) @@ -93,7 +94,7 @@ chunk_alloc_dss(size_t size, size_t alignment, bool *zero) */ do { /* Get the current end of the DSS. */ - dss_max = sbrk(0); + dss_max = chunk_dss_sbrk(0); /* * Calculate how much padding is necessary to * chunk-align the end of the DSS. @@ -117,7 +118,7 @@ chunk_alloc_dss(size_t size, size_t alignment, bool *zero) return (NULL); } incr = gap_size + cpad_size + size; - dss_prev = sbrk(incr); + dss_prev = chunk_dss_sbrk(incr); if (dss_prev == dss_max) { /* Success. */ dss_max = dss_next; @@ -163,7 +164,7 @@ chunk_dss_boot(void) if (malloc_mutex_init(&dss_mtx)) return (true); - dss_base = sbrk(0); + dss_base = chunk_dss_sbrk(0); dss_prev = dss_base; dss_max = dss_base; diff --git a/dep/jemalloc/src/chunk_mmap.c b/dep/jemalloc/src/chunk_mmap.c index 8a42e75915f..2056d793f05 100644 --- a/dep/jemalloc/src/chunk_mmap.c +++ b/dep/jemalloc/src/chunk_mmap.c @@ -43,7 +43,7 @@ pages_map(void *addr, size_t size) if (munmap(ret, size) == -1) { char buf[BUFERROR_BUF]; - buferror(buf, sizeof(buf)); + buferror(get_errno(), buf, sizeof(buf)); malloc_printf("<jemalloc: Error in munmap(): %s\n", buf); if (opt_abort) @@ -69,7 +69,7 @@ pages_unmap(void *addr, size_t size) { char buf[BUFERROR_BUF]; - buferror(buf, sizeof(buf)); + buferror(get_errno(), buf, sizeof(buf)); malloc_printf("<jemalloc>: Error in " #ifdef _WIN32 "VirtualFree" diff --git a/dep/jemalloc/src/ckh.c b/dep/jemalloc/src/ckh.c index 2f38348bb85..04c52966193 100644 --- a/dep/jemalloc/src/ckh.c +++ b/dep/jemalloc/src/ckh.c @@ -49,7 +49,7 @@ static void ckh_shrink(ckh_t *ckh); * Search bucket for key and return the cell number if found; SIZE_T_MAX * otherwise. */ -JEMALLOC_INLINE size_t +JEMALLOC_INLINE_C size_t ckh_bucket_search(ckh_t *ckh, size_t bucket, const void *key) { ckhc_t *cell; @@ -67,7 +67,7 @@ ckh_bucket_search(ckh_t *ckh, size_t bucket, const void *key) /* * Search table for key and return cell number if found; SIZE_T_MAX otherwise. */ -JEMALLOC_INLINE size_t +JEMALLOC_INLINE_C size_t ckh_isearch(ckh_t *ckh, const void *key) { size_t hashes[2], bucket, cell; @@ -88,7 +88,7 @@ ckh_isearch(ckh_t *ckh, const void *key) return (cell); } -JEMALLOC_INLINE bool +JEMALLOC_INLINE_C bool ckh_try_bucket_insert(ckh_t *ckh, size_t bucket, const void *key, const void *data) { @@ -120,7 +120,7 @@ ckh_try_bucket_insert(ckh_t *ckh, size_t bucket, const void *key, * eviction/relocation procedure until either success or detection of an * eviction/relocation bucket cycle. */ -JEMALLOC_INLINE bool +JEMALLOC_INLINE_C bool ckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey, void const **argdata) { @@ -190,7 +190,7 @@ ckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey, } } -JEMALLOC_INLINE bool +JEMALLOC_INLINE_C bool ckh_try_insert(ckh_t *ckh, void const**argkey, void const**argdata) { size_t hashes[2], bucket; @@ -219,7 +219,7 @@ ckh_try_insert(ckh_t *ckh, void const**argkey, void const**argdata) * Try to rebuild the hash table from scratch by inserting all items from the * old table into the new. */ -JEMALLOC_INLINE bool +JEMALLOC_INLINE_C bool ckh_rebuild(ckh_t *ckh, ckhc_t *aTab) { size_t count, i, nins; diff --git a/dep/jemalloc/src/ctl.c b/dep/jemalloc/src/ctl.c index f2ef4e60611..cc2c5aef570 100644 --- a/dep/jemalloc/src/ctl.c +++ b/dep/jemalloc/src/ctl.c @@ -546,43 +546,30 @@ ctl_arena_refresh(arena_t *arena, unsigned i) static bool ctl_grow(void) { - size_t astats_size; ctl_arena_stats_t *astats; arena_t **tarenas; - /* Extend arena stats and arenas arrays. */ - astats_size = (ctl_stats.narenas + 2) * sizeof(ctl_arena_stats_t); - if (ctl_stats.narenas == narenas_auto) { - /* ctl_stats.arenas and arenas came from base_alloc(). */ - astats = (ctl_arena_stats_t *)imalloc(astats_size); - if (astats == NULL) - return (true); - memcpy(astats, ctl_stats.arenas, (ctl_stats.narenas + 1) * - sizeof(ctl_arena_stats_t)); - - tarenas = (arena_t **)imalloc((ctl_stats.narenas + 1) * - sizeof(arena_t *)); - if (tarenas == NULL) { - idalloc(astats); - return (true); - } - memcpy(tarenas, arenas, ctl_stats.narenas * sizeof(arena_t *)); - } else { - astats = (ctl_arena_stats_t *)iralloc(ctl_stats.arenas, - astats_size, 0, 0, false, false); - if (astats == NULL) - return (true); - - tarenas = (arena_t **)iralloc(arenas, (ctl_stats.narenas + 1) * - sizeof(arena_t *), 0, 0, false, false); - if (tarenas == NULL) - return (true); + /* Allocate extended arena stats and arenas arrays. */ + astats = (ctl_arena_stats_t *)imalloc((ctl_stats.narenas + 2) * + sizeof(ctl_arena_stats_t)); + if (astats == NULL) + return (true); + tarenas = (arena_t **)imalloc((ctl_stats.narenas + 1) * + sizeof(arena_t *)); + if (tarenas == NULL) { + idalloc(astats); + return (true); } - /* Initialize the new astats and arenas elements. */ + + /* Initialize the new astats element. */ + memcpy(astats, ctl_stats.arenas, (ctl_stats.narenas + 1) * + sizeof(ctl_arena_stats_t)); memset(&astats[ctl_stats.narenas + 1], 0, sizeof(ctl_arena_stats_t)); - if (ctl_arena_init(&astats[ctl_stats.narenas + 1])) + if (ctl_arena_init(&astats[ctl_stats.narenas + 1])) { + idalloc(tarenas); + idalloc(astats); return (true); - tarenas[ctl_stats.narenas] = NULL; + } /* Swap merged stats to their new location. */ { ctl_arena_stats_t tstats; @@ -593,13 +580,34 @@ ctl_grow(void) memcpy(&astats[ctl_stats.narenas + 1], &tstats, sizeof(ctl_arena_stats_t)); } + /* Initialize the new arenas element. */ + tarenas[ctl_stats.narenas] = NULL; + { + arena_t **arenas_old = arenas; + /* + * Swap extended arenas array into place. Although ctl_mtx + * protects this function from other threads extending the + * array, it does not protect from other threads mutating it + * (i.e. initializing arenas and setting array elements to + * point to them). Therefore, array copying must happen under + * the protection of arenas_lock. + */ + malloc_mutex_lock(&arenas_lock); + arenas = tarenas; + memcpy(arenas, arenas_old, ctl_stats.narenas * + sizeof(arena_t *)); + narenas_total++; + arenas_extend(narenas_total - 1); + malloc_mutex_unlock(&arenas_lock); + /* + * Deallocate arenas_old only if it came from imalloc() (not + * base_alloc()). + */ + if (ctl_stats.narenas != narenas_auto) + idalloc(arenas_old); + } ctl_stats.arenas = astats; ctl_stats.narenas++; - malloc_mutex_lock(&arenas_lock); - arenas = tarenas; - narenas_total++; - arenas_extend(narenas_total - 1); - malloc_mutex_unlock(&arenas_lock); return (false); } @@ -921,7 +929,7 @@ void ctl_prefork(void) { - malloc_mutex_lock(&ctl_mtx); + malloc_mutex_prefork(&ctl_mtx); } void @@ -1102,6 +1110,8 @@ label_return: \ return (ret); \ } +/******************************************************************************/ + CTL_RO_NL_GEN(version, JEMALLOC_VERSION, const char *) static int @@ -1109,7 +1119,7 @@ epoch_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { int ret; - uint64_t newval; + UNUSED uint64_t newval; malloc_mutex_lock(&ctl_mtx); WRITE(newval, uint64_t); @@ -1123,49 +1133,52 @@ label_return: return (ret); } -static int -thread_tcache_enabled_ctl(const size_t *mib, size_t miblen, void *oldp, - size_t *oldlenp, void *newp, size_t newlen) -{ - int ret; - bool oldval; - - if (config_tcache == false) - return (ENOENT); - - oldval = tcache_enabled_get(); - if (newp != NULL) { - if (newlen != sizeof(bool)) { - ret = EINVAL; - goto label_return; - } - tcache_enabled_set(*(bool *)newp); - } - READ(oldval, bool); - - ret = 0; -label_return: - return (ret); -} - -static int -thread_tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp, - size_t *oldlenp, void *newp, size_t newlen) -{ - int ret; +/******************************************************************************/ - if (config_tcache == false) - return (ENOENT); +CTL_RO_BOOL_CONFIG_GEN(config_debug) +CTL_RO_BOOL_CONFIG_GEN(config_dss) +CTL_RO_BOOL_CONFIG_GEN(config_fill) +CTL_RO_BOOL_CONFIG_GEN(config_lazy_lock) +CTL_RO_BOOL_CONFIG_GEN(config_mremap) +CTL_RO_BOOL_CONFIG_GEN(config_munmap) +CTL_RO_BOOL_CONFIG_GEN(config_prof) +CTL_RO_BOOL_CONFIG_GEN(config_prof_libgcc) +CTL_RO_BOOL_CONFIG_GEN(config_prof_libunwind) +CTL_RO_BOOL_CONFIG_GEN(config_stats) +CTL_RO_BOOL_CONFIG_GEN(config_tcache) +CTL_RO_BOOL_CONFIG_GEN(config_tls) +CTL_RO_BOOL_CONFIG_GEN(config_utrace) +CTL_RO_BOOL_CONFIG_GEN(config_valgrind) +CTL_RO_BOOL_CONFIG_GEN(config_xmalloc) - READONLY(); - WRITEONLY(); +/******************************************************************************/ - tcache_flush(); +CTL_RO_NL_GEN(opt_abort, opt_abort, bool) +CTL_RO_NL_GEN(opt_dss, opt_dss, const char *) +CTL_RO_NL_GEN(opt_lg_chunk, opt_lg_chunk, size_t) +CTL_RO_NL_GEN(opt_narenas, opt_narenas, size_t) +CTL_RO_NL_GEN(opt_lg_dirty_mult, opt_lg_dirty_mult, ssize_t) +CTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool) +CTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, bool) +CTL_RO_NL_CGEN(config_fill, opt_quarantine, opt_quarantine, size_t) +CTL_RO_NL_CGEN(config_fill, opt_redzone, opt_redzone, bool) +CTL_RO_NL_CGEN(config_fill, opt_zero, opt_zero, bool) +CTL_RO_NL_CGEN(config_utrace, opt_utrace, opt_utrace, bool) +CTL_RO_NL_CGEN(config_valgrind, opt_valgrind, opt_valgrind, bool) +CTL_RO_NL_CGEN(config_xmalloc, opt_xmalloc, opt_xmalloc, bool) +CTL_RO_NL_CGEN(config_tcache, opt_tcache, opt_tcache, bool) +CTL_RO_NL_CGEN(config_tcache, opt_lg_tcache_max, opt_lg_tcache_max, ssize_t) +CTL_RO_NL_CGEN(config_prof, opt_prof, opt_prof, bool) +CTL_RO_NL_CGEN(config_prof, opt_prof_prefix, opt_prof_prefix, const char *) +CTL_RO_CGEN(config_prof, opt_prof_active, opt_prof_active, bool) /* Mutable. */ +CTL_RO_NL_CGEN(config_prof, opt_lg_prof_sample, opt_lg_prof_sample, size_t) +CTL_RO_NL_CGEN(config_prof, opt_prof_accum, opt_prof_accum, bool) +CTL_RO_NL_CGEN(config_prof, opt_lg_prof_interval, opt_lg_prof_interval, ssize_t) +CTL_RO_NL_CGEN(config_prof, opt_prof_gdump, opt_prof_gdump, bool) +CTL_RO_NL_CGEN(config_prof, opt_prof_final, opt_prof_final, bool) +CTL_RO_NL_CGEN(config_prof, opt_prof_leak, opt_prof_leak, bool) - ret = 0; -label_return: - return (ret); -} +/******************************************************************************/ static int thread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, @@ -1227,50 +1240,49 @@ CTL_RO_NL_CGEN(config_stats, thread_deallocated, CTL_RO_NL_CGEN(config_stats, thread_deallocatedp, &thread_allocated_tsd_get()->deallocated, uint64_t *) -/******************************************************************************/ +static int +thread_tcache_enabled_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + bool oldval; -CTL_RO_BOOL_CONFIG_GEN(config_debug) -CTL_RO_BOOL_CONFIG_GEN(config_dss) -CTL_RO_BOOL_CONFIG_GEN(config_fill) -CTL_RO_BOOL_CONFIG_GEN(config_lazy_lock) -CTL_RO_BOOL_CONFIG_GEN(config_mremap) -CTL_RO_BOOL_CONFIG_GEN(config_munmap) -CTL_RO_BOOL_CONFIG_GEN(config_prof) -CTL_RO_BOOL_CONFIG_GEN(config_prof_libgcc) -CTL_RO_BOOL_CONFIG_GEN(config_prof_libunwind) -CTL_RO_BOOL_CONFIG_GEN(config_stats) -CTL_RO_BOOL_CONFIG_GEN(config_tcache) -CTL_RO_BOOL_CONFIG_GEN(config_tls) -CTL_RO_BOOL_CONFIG_GEN(config_utrace) -CTL_RO_BOOL_CONFIG_GEN(config_valgrind) -CTL_RO_BOOL_CONFIG_GEN(config_xmalloc) + if (config_tcache == false) + return (ENOENT); -/******************************************************************************/ + oldval = tcache_enabled_get(); + if (newp != NULL) { + if (newlen != sizeof(bool)) { + ret = EINVAL; + goto label_return; + } + tcache_enabled_set(*(bool *)newp); + } + READ(oldval, bool); -CTL_RO_NL_GEN(opt_abort, opt_abort, bool) -CTL_RO_NL_GEN(opt_dss, opt_dss, const char *) -CTL_RO_NL_GEN(opt_lg_chunk, opt_lg_chunk, size_t) -CTL_RO_NL_GEN(opt_narenas, opt_narenas, size_t) -CTL_RO_NL_GEN(opt_lg_dirty_mult, opt_lg_dirty_mult, ssize_t) -CTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool) -CTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, bool) -CTL_RO_NL_CGEN(config_fill, opt_zero, opt_zero, bool) -CTL_RO_NL_CGEN(config_fill, opt_quarantine, opt_quarantine, size_t) -CTL_RO_NL_CGEN(config_fill, opt_redzone, opt_redzone, bool) -CTL_RO_NL_CGEN(config_utrace, opt_utrace, opt_utrace, bool) -CTL_RO_NL_CGEN(config_valgrind, opt_valgrind, opt_valgrind, bool) -CTL_RO_NL_CGEN(config_xmalloc, opt_xmalloc, opt_xmalloc, bool) -CTL_RO_NL_CGEN(config_tcache, opt_tcache, opt_tcache, bool) -CTL_RO_NL_CGEN(config_tcache, opt_lg_tcache_max, opt_lg_tcache_max, ssize_t) -CTL_RO_NL_CGEN(config_prof, opt_prof, opt_prof, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_prefix, opt_prof_prefix, const char *) -CTL_RO_CGEN(config_prof, opt_prof_active, opt_prof_active, bool) /* Mutable. */ -CTL_RO_NL_CGEN(config_prof, opt_lg_prof_sample, opt_lg_prof_sample, size_t) -CTL_RO_NL_CGEN(config_prof, opt_lg_prof_interval, opt_lg_prof_interval, ssize_t) -CTL_RO_NL_CGEN(config_prof, opt_prof_gdump, opt_prof_gdump, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_final, opt_prof_final, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_leak, opt_prof_leak, bool) -CTL_RO_NL_CGEN(config_prof, opt_prof_accum, opt_prof_accum, bool) + ret = 0; +label_return: + return (ret); +} + +static int +thread_tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + + if (config_tcache == false) + return (ENOENT); + + READONLY(); + WRITEONLY(); + + tcache_flush(); + + ret = 0; +label_return: + return (ret); +} /******************************************************************************/ @@ -1382,31 +1394,8 @@ label_return: return (ret); } - /******************************************************************************/ -CTL_RO_NL_GEN(arenas_bin_i_size, arena_bin_info[mib[2]].reg_size, size_t) -CTL_RO_NL_GEN(arenas_bin_i_nregs, arena_bin_info[mib[2]].nregs, uint32_t) -CTL_RO_NL_GEN(arenas_bin_i_run_size, arena_bin_info[mib[2]].run_size, size_t) -static const ctl_named_node_t * -arenas_bin_i_index(const size_t *mib, size_t miblen, size_t i) -{ - - if (i > NBINS) - return (NULL); - return (super_arenas_bin_i_node); -} - -CTL_RO_NL_GEN(arenas_lrun_i_size, ((mib[2]+1) << LG_PAGE), size_t) -static const ctl_named_node_t * -arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i) -{ - - if (i > nlclasses) - return (NULL); - return (super_arenas_lrun_i_node); -} - static int arenas_narenas_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) @@ -1460,7 +1449,28 @@ CTL_RO_NL_GEN(arenas_page, PAGE, size_t) CTL_RO_NL_CGEN(config_tcache, arenas_tcache_max, tcache_maxclass, size_t) CTL_RO_NL_GEN(arenas_nbins, NBINS, unsigned) CTL_RO_NL_CGEN(config_tcache, arenas_nhbins, nhbins, unsigned) +CTL_RO_NL_GEN(arenas_bin_i_size, arena_bin_info[mib[2]].reg_size, size_t) +CTL_RO_NL_GEN(arenas_bin_i_nregs, arena_bin_info[mib[2]].nregs, uint32_t) +CTL_RO_NL_GEN(arenas_bin_i_run_size, arena_bin_info[mib[2]].run_size, size_t) +static const ctl_named_node_t * +arenas_bin_i_index(const size_t *mib, size_t miblen, size_t i) +{ + + if (i > NBINS) + return (NULL); + return (super_arenas_bin_i_node); +} + CTL_RO_NL_GEN(arenas_nlruns, nlclasses, size_t) +CTL_RO_NL_GEN(arenas_lrun_i_size, ((mib[2]+1) << LG_PAGE), size_t) +static const ctl_named_node_t * +arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i) +{ + + if (i > nlclasses) + return (NULL); + return (super_arenas_lrun_i_node); +} static int arenas_purge_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, @@ -1567,6 +1577,11 @@ CTL_RO_NL_CGEN(config_prof, prof_interval, prof_interval, uint64_t) /******************************************************************************/ +CTL_RO_CGEN(config_stats, stats_cactive, &stats_cactive, size_t *) +CTL_RO_CGEN(config_stats, stats_allocated, ctl_stats.allocated, size_t) +CTL_RO_CGEN(config_stats, stats_active, ctl_stats.active, size_t) +CTL_RO_CGEN(config_stats, stats_mapped, ctl_stats.mapped, size_t) + CTL_RO_CGEN(config_stats, stats_chunks_current, ctl_stats.chunks.current, size_t) CTL_RO_CGEN(config_stats, stats_chunks_total, ctl_stats.chunks.total, uint64_t) @@ -1574,6 +1589,20 @@ CTL_RO_CGEN(config_stats, stats_chunks_high, ctl_stats.chunks.high, size_t) CTL_RO_CGEN(config_stats, stats_huge_allocated, huge_allocated, size_t) CTL_RO_CGEN(config_stats, stats_huge_nmalloc, huge_nmalloc, uint64_t) CTL_RO_CGEN(config_stats, stats_huge_ndalloc, huge_ndalloc, uint64_t) + +CTL_RO_GEN(stats_arenas_i_dss, ctl_stats.arenas[mib[2]].dss, const char *) +CTL_RO_GEN(stats_arenas_i_nthreads, ctl_stats.arenas[mib[2]].nthreads, unsigned) +CTL_RO_GEN(stats_arenas_i_pactive, ctl_stats.arenas[mib[2]].pactive, size_t) +CTL_RO_GEN(stats_arenas_i_pdirty, ctl_stats.arenas[mib[2]].pdirty, size_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_mapped, + ctl_stats.arenas[mib[2]].astats.mapped, size_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_npurge, + ctl_stats.arenas[mib[2]].astats.npurge, uint64_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_nmadvise, + ctl_stats.arenas[mib[2]].astats.nmadvise, uint64_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_purged, + ctl_stats.arenas[mib[2]].astats.purged, uint64_t) + CTL_RO_CGEN(config_stats, stats_arenas_i_small_allocated, ctl_stats.arenas[mib[2]].allocated_small, size_t) CTL_RO_CGEN(config_stats, stats_arenas_i_small_nmalloc, @@ -1637,19 +1666,6 @@ stats_arenas_i_lruns_j_index(const size_t *mib, size_t miblen, size_t j) return (super_stats_arenas_i_lruns_j_node); } -CTL_RO_GEN(stats_arenas_i_nthreads, ctl_stats.arenas[mib[2]].nthreads, unsigned) -CTL_RO_GEN(stats_arenas_i_dss, ctl_stats.arenas[mib[2]].dss, const char *) -CTL_RO_GEN(stats_arenas_i_pactive, ctl_stats.arenas[mib[2]].pactive, size_t) -CTL_RO_GEN(stats_arenas_i_pdirty, ctl_stats.arenas[mib[2]].pdirty, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_mapped, - ctl_stats.arenas[mib[2]].astats.mapped, size_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_npurge, - ctl_stats.arenas[mib[2]].astats.npurge, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_nmadvise, - ctl_stats.arenas[mib[2]].astats.nmadvise, uint64_t) -CTL_RO_CGEN(config_stats, stats_arenas_i_purged, - ctl_stats.arenas[mib[2]].astats.purged, uint64_t) - static const ctl_named_node_t * stats_arenas_i_index(const size_t *mib, size_t miblen, size_t i) { @@ -1666,8 +1682,3 @@ label_return: malloc_mutex_unlock(&ctl_mtx); return (ret); } - -CTL_RO_CGEN(config_stats, stats_cactive, &stats_cactive, size_t *) -CTL_RO_CGEN(config_stats, stats_allocated, ctl_stats.allocated, size_t) -CTL_RO_CGEN(config_stats, stats_active, ctl_stats.active, size_t) -CTL_RO_CGEN(config_stats, stats_mapped, ctl_stats.mapped, size_t) diff --git a/dep/jemalloc/src/huge.c b/dep/jemalloc/src/huge.c index aa08d43d362..d72f2135702 100644 --- a/dep/jemalloc/src/huge.c +++ b/dep/jemalloc/src/huge.c @@ -16,14 +16,14 @@ malloc_mutex_t huge_mtx; static extent_tree_t huge; void * -huge_malloc(size_t size, bool zero) +huge_malloc(size_t size, bool zero, dss_prec_t dss_prec) { - return (huge_palloc(size, chunksize, zero)); + return (huge_palloc(size, chunksize, zero, dss_prec)); } void * -huge_palloc(size_t size, size_t alignment, bool zero) +huge_palloc(size_t size, size_t alignment, bool zero, dss_prec_t dss_prec) { void *ret; size_t csize; @@ -48,8 +48,7 @@ huge_palloc(size_t size, size_t alignment, bool zero) * it is possible to make correct junk/zero fill decisions below. */ is_zeroed = zero; - ret = chunk_alloc(csize, alignment, false, &is_zeroed, - chunk_dss_prec_get()); + ret = chunk_alloc(csize, alignment, false, &is_zeroed, dss_prec); if (ret == NULL) { base_node_dealloc(node); return (NULL); @@ -78,7 +77,7 @@ huge_palloc(size_t size, size_t alignment, bool zero) return (ret); } -void * +bool huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra) { @@ -89,28 +88,23 @@ huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra) && CHUNK_CEILING(oldsize) >= CHUNK_CEILING(size) && CHUNK_CEILING(oldsize) <= CHUNK_CEILING(size+extra)) { assert(CHUNK_CEILING(oldsize) == oldsize); - if (config_fill && opt_junk && size < oldsize) { - memset((void *)((uintptr_t)ptr + size), 0x5a, - oldsize - size); - } - return (ptr); + return (false); } /* Reallocation would require a move. */ - return (NULL); + return (true); } void * huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra, - size_t alignment, bool zero, bool try_tcache_dalloc) + size_t alignment, bool zero, bool try_tcache_dalloc, dss_prec_t dss_prec) { void *ret; size_t copysize; /* Try to avoid moving the allocation. */ - ret = huge_ralloc_no_move(ptr, oldsize, size, extra); - if (ret != NULL) - return (ret); + if (huge_ralloc_no_move(ptr, oldsize, size, extra) == false) + return (ptr); /* * size and oldsize are different enough that we need to use a @@ -118,18 +112,18 @@ huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra, * space and copying. */ if (alignment > chunksize) - ret = huge_palloc(size + extra, alignment, zero); + ret = huge_palloc(size + extra, alignment, zero, dss_prec); else - ret = huge_malloc(size + extra, zero); + ret = huge_malloc(size + extra, zero, dss_prec); if (ret == NULL) { if (extra == 0) return (NULL); /* Try again, this time without extra. */ if (alignment > chunksize) - ret = huge_palloc(size, alignment, zero); + ret = huge_palloc(size, alignment, zero, dss_prec); else - ret = huge_malloc(size, zero); + ret = huge_malloc(size, zero, dss_prec); if (ret == NULL) return (NULL); @@ -169,23 +163,56 @@ huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra, */ char buf[BUFERROR_BUF]; - buferror(buf, sizeof(buf)); + buferror(get_errno(), buf, sizeof(buf)); malloc_printf("<jemalloc>: Error in mremap(): %s\n", buf); if (opt_abort) abort(); memcpy(ret, ptr, copysize); chunk_dealloc_mmap(ptr, oldsize); + } else if (config_fill && zero == false && opt_junk && oldsize + < newsize) { + /* + * mremap(2) clobbers the original mapping, so + * junk/zero filling is not preserved. There is no + * need to zero fill here, since any trailing + * uninititialized memory is demand-zeroed by the + * kernel, but junk filling must be redone. + */ + memset(ret + oldsize, 0xa5, newsize - oldsize); } } else #endif { memcpy(ret, ptr, copysize); - iqallocx(ptr, try_tcache_dalloc); + iqalloct(ptr, try_tcache_dalloc); } return (ret); } +#ifdef JEMALLOC_JET +#undef huge_dalloc_junk +#define huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk_impl) +#endif +static void +huge_dalloc_junk(void *ptr, size_t usize) +{ + + if (config_fill && config_dss && opt_junk) { + /* + * Only bother junk filling if the chunk isn't about to be + * unmapped. + */ + if (config_munmap == false || (config_dss && chunk_in_dss(ptr))) + memset(ptr, 0x5a, usize); + } +} +#ifdef JEMALLOC_JET +#undef huge_dalloc_junk +#define huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk) +huge_dalloc_junk_t *huge_dalloc_junk = JEMALLOC_N(huge_dalloc_junk_impl); +#endif + void huge_dalloc(void *ptr, bool unmap) { @@ -208,8 +235,8 @@ huge_dalloc(void *ptr, bool unmap) malloc_mutex_unlock(&huge_mtx); - if (unmap && config_fill && config_dss && opt_junk) - memset(node->addr, 0x5a, node->size); + if (unmap) + huge_dalloc_junk(node->addr, node->size); chunk_dealloc(node->addr, node->size, unmap); @@ -236,6 +263,13 @@ huge_salloc(const void *ptr) return (ret); } +dss_prec_t +huge_dss_prec_get(arena_t *arena) +{ + + return (arena_dss_prec_get(choose_arena(arena))); +} + prof_ctx_t * huge_prof_ctx_get(const void *ptr) { diff --git a/dep/jemalloc/src/jemalloc.c b/dep/jemalloc/src/jemalloc.c index bc350ed953b..204778bc89d 100644 --- a/dep/jemalloc/src/jemalloc.c +++ b/dep/jemalloc/src/jemalloc.c @@ -100,18 +100,12 @@ typedef struct { #endif /******************************************************************************/ -/* Function prototypes for non-inline static functions. */ - -static void stats_print_atexit(void); -static unsigned malloc_ncpus(void); -static bool malloc_conf_next(char const **opts_p, char const **k_p, - size_t *klen_p, char const **v_p, size_t *vlen_p); -static void malloc_conf_error(const char *msg, const char *k, size_t klen, - const char *v, size_t vlen); -static void malloc_conf_init(void); +/* + * Function prototypes for static functions that are referenced prior to + * definition. + */ + static bool malloc_init_hard(void); -static int imemalign(void **memptr, size_t alignment, size_t size, - size_t min_alignment); /******************************************************************************/ /* @@ -252,7 +246,6 @@ stats_print_atexit(void) static unsigned malloc_ncpus(void) { - unsigned ret; long result; #ifdef _WIN32 @@ -262,14 +255,7 @@ malloc_ncpus(void) #else result = sysconf(_SC_NPROCESSORS_ONLN); #endif - if (result == -1) { - /* Error. */ - ret = 1; - } else { - ret = (unsigned)result; - } - - return (ret); + return ((result == -1) ? 1 : (unsigned)result); } void @@ -282,7 +268,7 @@ arenas_cleanup(void *arg) malloc_mutex_unlock(&arenas_lock); } -static JEMALLOC_ATTR(always_inline) void +JEMALLOC_ALWAYS_INLINE_C void malloc_thread_init(void) { @@ -299,7 +285,7 @@ malloc_thread_init(void) quarantine_alloc_hook(); } -static JEMALLOC_ATTR(always_inline) bool +JEMALLOC_ALWAYS_INLINE_C bool malloc_init(void) { @@ -436,8 +422,9 @@ malloc_conf_init(void) } break; case 1: { + int linklen = 0; #ifndef _WIN32 - int linklen; + int saved_errno = errno; const char *linkname = # ifdef JEMALLOC_PREFIX "/etc/"JEMALLOC_PREFIX"malloc.conf" @@ -446,21 +433,20 @@ malloc_conf_init(void) # endif ; - if ((linklen = readlink(linkname, buf, - sizeof(buf) - 1)) != -1) { - /* - * Use the contents of the "/etc/malloc.conf" - * symbolic link's name. - */ - buf[linklen] = '\0'; - opts = buf; - } else -#endif - { + /* + * Try to use the contents of the "/etc/malloc.conf" + * symbolic link's name. + */ + linklen = readlink(linkname, buf, sizeof(buf) - 1); + if (linklen == -1) { /* No configuration specified. */ - buf[0] = '\0'; - opts = buf; + linklen = 0; + /* restore errno */ + set_errno(saved_errno); } +#endif + buf[linklen] = '\0'; + opts = buf; break; } case 2: { const char *envname = @@ -484,8 +470,7 @@ malloc_conf_init(void) } break; } default: - /* NOTREACHED */ - assert(false); + not_reached(); buf[0] = '\0'; opts = buf; } @@ -522,14 +507,15 @@ malloc_conf_init(void) "Invalid conf value", \ k, klen, v, vlen); \ } else if (clip) { \ - if (um < min) \ + if (min != 0 && um < min) \ o = min; \ else if (um > max) \ o = max; \ else \ o = um; \ } else { \ - if (um < min || um > max) { \ + if ((min != 0 && um < min) || \ + um > max) { \ malloc_conf_error( \ "Out-of-range " \ "conf value", \ @@ -695,17 +681,6 @@ malloc_init_hard(void) malloc_conf_init(); -#if (!defined(JEMALLOC_MUTEX_INIT_CB) && !defined(JEMALLOC_ZONE) \ - && !defined(_WIN32)) - /* Register fork handlers. */ - if (pthread_atfork(jemalloc_prefork, jemalloc_postfork_parent, - jemalloc_postfork_child) != 0) { - malloc_write("<jemalloc>: Error in pthread_atfork()\n"); - if (opt_abort) - abort(); - } -#endif - if (opt_stats_print) { /* Print statistics at exit. */ if (atexit(stats_print_atexit) != 0) { @@ -745,8 +720,10 @@ malloc_init_hard(void) return (true); } - if (malloc_mutex_init(&arenas_lock)) + if (malloc_mutex_init(&arenas_lock)) { + malloc_mutex_unlock(&init_lock); return (true); + } /* * Create enough scaffolding to allow recursive allocation in @@ -792,9 +769,25 @@ malloc_init_hard(void) return (true); } - /* Get number of CPUs. */ malloc_mutex_unlock(&init_lock); + /**********************************************************************/ + /* Recursive allocation may follow. */ + ncpus = malloc_ncpus(); + +#if (!defined(JEMALLOC_MUTEX_INIT_CB) && !defined(JEMALLOC_ZONE) \ + && !defined(_WIN32)) + /* LinuxThreads's pthread_atfork() allocates. */ + if (pthread_atfork(jemalloc_prefork, jemalloc_postfork_parent, + jemalloc_postfork_child) != 0) { + malloc_write("<jemalloc>: Error in pthread_atfork()\n"); + if (opt_abort) + abort(); + } +#endif + + /* Done recursively allocating. */ + /**********************************************************************/ malloc_mutex_lock(&init_lock); if (mutex_boot()) { @@ -841,6 +834,7 @@ malloc_init_hard(void) malloc_initialized = true; malloc_mutex_unlock(&init_lock); + return (false); } @@ -852,42 +846,88 @@ malloc_init_hard(void) * Begin malloc(3)-compatible functions. */ +static void * +imalloc_prof_sample(size_t usize, prof_thr_cnt_t *cnt) +{ + void *p; + + if (cnt == NULL) + return (NULL); + if (prof_promote && usize <= SMALL_MAXCLASS) { + p = imalloc(SMALL_MAXCLASS+1); + if (p == NULL) + return (NULL); + arena_prof_promoted(p, usize); + } else + p = imalloc(usize); + + return (p); +} + +JEMALLOC_ALWAYS_INLINE_C void * +imalloc_prof(size_t usize, prof_thr_cnt_t *cnt) +{ + void *p; + + if ((uintptr_t)cnt != (uintptr_t)1U) + p = imalloc_prof_sample(usize, cnt); + else + p = imalloc(usize); + if (p == NULL) + return (NULL); + prof_malloc(p, usize, cnt); + + return (p); +} + +/* + * MALLOC_BODY() is a macro rather than a function because its contents are in + * the fast path, but inlining would cause reliability issues when determining + * how many frames to discard from heap profiling backtraces. + */ +#define MALLOC_BODY(ret, size, usize) do { \ + if (malloc_init()) \ + ret = NULL; \ + else { \ + if (config_prof && opt_prof) { \ + prof_thr_cnt_t *cnt; \ + \ + usize = s2u(size); \ + /* \ + * Call PROF_ALLOC_PREP() here rather than in \ + * imalloc_prof() so that imalloc_prof() can be \ + * inlined without introducing uncertainty \ + * about the number of backtrace frames to \ + * ignore. imalloc_prof() is in the fast path \ + * when heap profiling is enabled, so inlining \ + * is critical to performance. (For \ + * consistency all callers of PROF_ALLOC_PREP() \ + * are structured similarly, even though e.g. \ + * realloc() isn't called enough for inlining \ + * to be critical.) \ + */ \ + PROF_ALLOC_PREP(1, usize, cnt); \ + ret = imalloc_prof(usize, cnt); \ + } else { \ + if (config_stats || (config_valgrind && \ + opt_valgrind)) \ + usize = s2u(size); \ + ret = imalloc(size); \ + } \ + } \ +} while (0) + void * je_malloc(size_t size) { void *ret; size_t usize JEMALLOC_CC_SILENCE_INIT(0); - prof_thr_cnt_t *cnt JEMALLOC_CC_SILENCE_INIT(NULL); - - if (malloc_init()) { - ret = NULL; - goto label_oom; - } if (size == 0) size = 1; - if (config_prof && opt_prof) { - usize = s2u(size); - PROF_ALLOC_PREP(1, usize, cnt); - if (cnt == NULL) { - ret = NULL; - goto label_oom; - } - if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && usize <= - SMALL_MAXCLASS) { - ret = imalloc(SMALL_MAXCLASS+1); - if (ret != NULL) - arena_prof_promoted(ret, usize); - } else - ret = imalloc(size); - } else { - if (config_stats || (config_valgrind && opt_valgrind)) - usize = s2u(size); - ret = imalloc(size); - } + MALLOC_BODY(ret, size, usize); -label_oom: if (ret == NULL) { if (config_xmalloc && opt_xmalloc) { malloc_write("<jemalloc>: Error in malloc(): " @@ -896,8 +936,6 @@ label_oom: } set_errno(ENOMEM); } - if (config_prof && opt_prof && ret != NULL) - prof_malloc(ret, usize, cnt); if (config_stats && ret != NULL) { assert(usize == isalloc(ret, config_prof)); thread_allocated_tsd_get()->allocated += usize; @@ -907,6 +945,42 @@ label_oom: return (ret); } +static void * +imemalign_prof_sample(size_t alignment, size_t usize, prof_thr_cnt_t *cnt) +{ + void *p; + + if (cnt == NULL) + return (NULL); + if (prof_promote && usize <= SMALL_MAXCLASS) { + assert(sa2u(SMALL_MAXCLASS+1, alignment) != 0); + p = ipalloc(sa2u(SMALL_MAXCLASS+1, alignment), alignment, + false); + if (p == NULL) + return (NULL); + arena_prof_promoted(p, usize); + } else + p = ipalloc(usize, alignment, false); + + return (p); +} + +JEMALLOC_ALWAYS_INLINE_C void * +imemalign_prof(size_t alignment, size_t usize, prof_thr_cnt_t *cnt) +{ + void *p; + + if ((uintptr_t)cnt != (uintptr_t)1U) + p = imemalign_prof_sample(alignment, usize, cnt); + else + p = ipalloc(usize, alignment, false); + if (p == NULL) + return (NULL); + prof_malloc(p, usize, cnt); + + return (p); +} + JEMALLOC_ATTR(nonnull(1)) #ifdef JEMALLOC_PROF /* @@ -916,19 +990,18 @@ JEMALLOC_ATTR(nonnull(1)) JEMALLOC_NOINLINE #endif static int -imemalign(void **memptr, size_t alignment, size_t size, - size_t min_alignment) +imemalign(void **memptr, size_t alignment, size_t size, size_t min_alignment) { int ret; size_t usize; void *result; - prof_thr_cnt_t *cnt JEMALLOC_CC_SILENCE_INIT(NULL); assert(min_alignment != 0); - if (malloc_init()) + if (malloc_init()) { result = NULL; - else { + goto label_oom; + } else { if (size == 0) size = 1; @@ -948,57 +1021,38 @@ imemalign(void **memptr, size_t alignment, size_t size, usize = sa2u(size, alignment); if (usize == 0) { result = NULL; - ret = ENOMEM; - goto label_return; + goto label_oom; } if (config_prof && opt_prof) { + prof_thr_cnt_t *cnt; + PROF_ALLOC_PREP(2, usize, cnt); - if (cnt == NULL) { - result = NULL; - ret = EINVAL; - } else { - if (prof_promote && (uintptr_t)cnt != - (uintptr_t)1U && usize <= SMALL_MAXCLASS) { - assert(sa2u(SMALL_MAXCLASS+1, - alignment) != 0); - result = ipalloc(sa2u(SMALL_MAXCLASS+1, - alignment), alignment, false); - if (result != NULL) { - arena_prof_promoted(result, - usize); - } - } else { - result = ipalloc(usize, alignment, - false); - } - } + result = imemalign_prof(alignment, usize, cnt); } else result = ipalloc(usize, alignment, false); - } - - if (result == NULL) { - if (config_xmalloc && opt_xmalloc) { - malloc_write("<jemalloc>: Error allocating aligned " - "memory: out of memory\n"); - abort(); - } - ret = ENOMEM; - goto label_return; + if (result == NULL) + goto label_oom; } *memptr = result; ret = 0; - label_return: if (config_stats && result != NULL) { assert(usize == isalloc(result, config_prof)); thread_allocated_tsd_get()->allocated += usize; } - if (config_prof && opt_prof && result != NULL) - prof_malloc(result, usize, cnt); UTRACE(0, size, result); return (ret); +label_oom: + assert(result == NULL); + if (config_xmalloc && opt_xmalloc) { + malloc_write("<jemalloc>: Error allocating aligned memory: " + "out of memory\n"); + abort(); + } + ret = ENOMEM; + goto label_return; } int @@ -1025,13 +1079,46 @@ je_aligned_alloc(size_t alignment, size_t size) return (ret); } +static void * +icalloc_prof_sample(size_t usize, prof_thr_cnt_t *cnt) +{ + void *p; + + if (cnt == NULL) + return (NULL); + if (prof_promote && usize <= SMALL_MAXCLASS) { + p = icalloc(SMALL_MAXCLASS+1); + if (p == NULL) + return (NULL); + arena_prof_promoted(p, usize); + } else + p = icalloc(usize); + + return (p); +} + +JEMALLOC_ALWAYS_INLINE_C void * +icalloc_prof(size_t usize, prof_thr_cnt_t *cnt) +{ + void *p; + + if ((uintptr_t)cnt != (uintptr_t)1U) + p = icalloc_prof_sample(usize, cnt); + else + p = icalloc(usize); + if (p == NULL) + return (NULL); + prof_malloc(p, usize, cnt); + + return (p); +} + void * je_calloc(size_t num, size_t size) { void *ret; size_t num_size; size_t usize JEMALLOC_CC_SILENCE_INIT(0); - prof_thr_cnt_t *cnt JEMALLOC_CC_SILENCE_INIT(NULL); if (malloc_init()) { num_size = 0; @@ -1060,19 +1147,11 @@ je_calloc(size_t num, size_t size) } if (config_prof && opt_prof) { + prof_thr_cnt_t *cnt; + usize = s2u(num_size); PROF_ALLOC_PREP(1, usize, cnt); - if (cnt == NULL) { - ret = NULL; - goto label_return; - } - if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && usize - <= SMALL_MAXCLASS) { - ret = icalloc(SMALL_MAXCLASS+1); - if (ret != NULL) - arena_prof_promoted(ret, usize); - } else - ret = icalloc(num_size); + ret = icalloc_prof(usize, cnt); } else { if (config_stats || (config_valgrind && opt_valgrind)) usize = s2u(num_size); @@ -1088,9 +1167,6 @@ label_return: } set_errno(ENOMEM); } - - if (config_prof && opt_prof && ret != NULL) - prof_malloc(ret, usize, cnt); if (config_stats && ret != NULL) { assert(usize == isalloc(ret, config_prof)); thread_allocated_tsd_get()->allocated += usize; @@ -1100,152 +1176,126 @@ label_return: return (ret); } +static void * +irealloc_prof_sample(void *oldptr, size_t usize, prof_thr_cnt_t *cnt) +{ + void *p; + + if (cnt == NULL) + return (NULL); + if (prof_promote && usize <= SMALL_MAXCLASS) { + p = iralloc(oldptr, SMALL_MAXCLASS+1, 0, 0, false); + if (p == NULL) + return (NULL); + arena_prof_promoted(p, usize); + } else + p = iralloc(oldptr, usize, 0, 0, false); + + return (p); +} + +JEMALLOC_ALWAYS_INLINE_C void * +irealloc_prof(void *oldptr, size_t old_usize, size_t usize, prof_thr_cnt_t *cnt) +{ + void *p; + prof_ctx_t *old_ctx; + + old_ctx = prof_ctx_get(oldptr); + if ((uintptr_t)cnt != (uintptr_t)1U) + p = irealloc_prof_sample(oldptr, usize, cnt); + else + p = iralloc(oldptr, usize, 0, 0, false); + if (p == NULL) + return (NULL); + prof_realloc(p, usize, cnt, old_usize, old_ctx); + + return (p); +} + +JEMALLOC_INLINE_C void +ifree(void *ptr) +{ + size_t usize; + UNUSED size_t rzsize JEMALLOC_CC_SILENCE_INIT(0); + + assert(ptr != NULL); + assert(malloc_initialized || IS_INITIALIZER); + + if (config_prof && opt_prof) { + usize = isalloc(ptr, config_prof); + prof_free(ptr, usize); + } else if (config_stats || config_valgrind) + usize = isalloc(ptr, config_prof); + if (config_stats) + thread_allocated_tsd_get()->deallocated += usize; + if (config_valgrind && opt_valgrind) + rzsize = p2rz(ptr); + iqalloc(ptr); + JEMALLOC_VALGRIND_FREE(ptr, rzsize); +} + void * je_realloc(void *ptr, size_t size) { void *ret; size_t usize JEMALLOC_CC_SILENCE_INIT(0); - size_t old_size = 0; - size_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0); - prof_thr_cnt_t *cnt JEMALLOC_CC_SILENCE_INIT(NULL); - prof_ctx_t *old_ctx JEMALLOC_CC_SILENCE_INIT(NULL); + size_t old_usize = 0; + UNUSED size_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0); if (size == 0) { if (ptr != NULL) { - /* realloc(ptr, 0) is equivalent to free(p). */ - assert(malloc_initialized || IS_INITIALIZER); - if (config_prof) { - old_size = isalloc(ptr, true); - if (config_valgrind && opt_valgrind) - old_rzsize = p2rz(ptr); - } else if (config_stats) { - old_size = isalloc(ptr, false); - if (config_valgrind && opt_valgrind) - old_rzsize = u2rz(old_size); - } else if (config_valgrind && opt_valgrind) { - old_size = isalloc(ptr, false); - old_rzsize = u2rz(old_size); - } - if (config_prof && opt_prof) { - old_ctx = prof_ctx_get(ptr); - cnt = NULL; - } - iqalloc(ptr); - ret = NULL; - goto label_return; - } else - size = 1; + /* realloc(ptr, 0) is equivalent to free(ptr). */ + UTRACE(ptr, 0, 0); + ifree(ptr); + return (NULL); + } + size = 1; } if (ptr != NULL) { assert(malloc_initialized || IS_INITIALIZER); malloc_thread_init(); - if (config_prof) { - old_size = isalloc(ptr, true); - if (config_valgrind && opt_valgrind) - old_rzsize = p2rz(ptr); - } else if (config_stats) { - old_size = isalloc(ptr, false); - if (config_valgrind && opt_valgrind) - old_rzsize = u2rz(old_size); - } else if (config_valgrind && opt_valgrind) { - old_size = isalloc(ptr, false); - old_rzsize = u2rz(old_size); - } + if ((config_prof && opt_prof) || config_stats || + (config_valgrind && opt_valgrind)) + old_usize = isalloc(ptr, config_prof); + if (config_valgrind && opt_valgrind) + old_rzsize = config_prof ? p2rz(ptr) : u2rz(old_usize); + if (config_prof && opt_prof) { + prof_thr_cnt_t *cnt; + usize = s2u(size); - old_ctx = prof_ctx_get(ptr); PROF_ALLOC_PREP(1, usize, cnt); - if (cnt == NULL) { - old_ctx = NULL; - ret = NULL; - goto label_oom; - } - if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && - usize <= SMALL_MAXCLASS) { - ret = iralloc(ptr, SMALL_MAXCLASS+1, 0, 0, - false, false); - if (ret != NULL) - arena_prof_promoted(ret, usize); - else - old_ctx = NULL; - } else { - ret = iralloc(ptr, size, 0, 0, false, false); - if (ret == NULL) - old_ctx = NULL; - } + ret = irealloc_prof(ptr, old_usize, usize, cnt); } else { if (config_stats || (config_valgrind && opt_valgrind)) usize = s2u(size); - ret = iralloc(ptr, size, 0, 0, false, false); - } - -label_oom: - if (ret == NULL) { - if (config_xmalloc && opt_xmalloc) { - malloc_write("<jemalloc>: Error in realloc(): " - "out of memory\n"); - abort(); - } - set_errno(ENOMEM); + ret = iralloc(ptr, size, 0, 0, false); } } else { /* realloc(NULL, size) is equivalent to malloc(size). */ - if (config_prof && opt_prof) - old_ctx = NULL; - if (malloc_init()) { - if (config_prof && opt_prof) - cnt = NULL; - ret = NULL; - } else { - if (config_prof && opt_prof) { - usize = s2u(size); - PROF_ALLOC_PREP(1, usize, cnt); - if (cnt == NULL) - ret = NULL; - else { - if (prof_promote && (uintptr_t)cnt != - (uintptr_t)1U && usize <= - SMALL_MAXCLASS) { - ret = imalloc(SMALL_MAXCLASS+1); - if (ret != NULL) { - arena_prof_promoted(ret, - usize); - } - } else - ret = imalloc(size); - } - } else { - if (config_stats || (config_valgrind && - opt_valgrind)) - usize = s2u(size); - ret = imalloc(size); - } - } + MALLOC_BODY(ret, size, usize); + } - if (ret == NULL) { - if (config_xmalloc && opt_xmalloc) { - malloc_write("<jemalloc>: Error in realloc(): " - "out of memory\n"); - abort(); - } - set_errno(ENOMEM); + if (ret == NULL) { + if (config_xmalloc && opt_xmalloc) { + malloc_write("<jemalloc>: Error in realloc(): " + "out of memory\n"); + abort(); } + set_errno(ENOMEM); } - -label_return: - if (config_prof && opt_prof) - prof_realloc(ret, usize, cnt, old_size, old_ctx); if (config_stats && ret != NULL) { thread_allocated_t *ta; assert(usize == isalloc(ret, config_prof)); ta = thread_allocated_tsd_get(); ta->allocated += usize; - ta->deallocated += old_size; + ta->deallocated += old_usize; } UTRACE(ptr, size, ret); - JEMALLOC_VALGRIND_REALLOC(ret, usize, ptr, old_size, old_rzsize, false); + JEMALLOC_VALGRIND_REALLOC(ret, usize, ptr, old_usize, old_rzsize, + false); return (ret); } @@ -1254,24 +1304,8 @@ je_free(void *ptr) { UTRACE(ptr, 0, 0); - if (ptr != NULL) { - size_t usize; - size_t rzsize JEMALLOC_CC_SILENCE_INIT(0); - - assert(malloc_initialized || IS_INITIALIZER); - - if (config_prof && opt_prof) { - usize = isalloc(ptr, config_prof); - prof_free(ptr, usize); - } else if (config_stats || config_valgrind) - usize = isalloc(ptr, config_prof); - if (config_stats) - thread_allocated_tsd_get()->deallocated += usize; - if (config_valgrind && opt_valgrind) - rzsize = p2rz(ptr); - iqalloc(ptr); - JEMALLOC_VALGRIND_FREE(ptr, rzsize); - } + if (ptr != NULL) + ifree(ptr); } /* @@ -1337,208 +1371,344 @@ JEMALLOC_EXPORT void *(* __memalign_hook)(size_t alignment, size_t size) = * Begin non-standard functions. */ -size_t -je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) +JEMALLOC_ALWAYS_INLINE_C void * +imallocx(size_t usize, size_t alignment, bool zero, bool try_tcache, + arena_t *arena) { - size_t ret; - assert(malloc_initialized || IS_INITIALIZER); - malloc_thread_init(); + assert(usize == ((alignment == 0) ? s2u(usize) : sa2u(usize, + alignment))); - if (config_ivsalloc) - ret = ivsalloc(ptr, config_prof); + if (alignment != 0) + return (ipalloct(usize, alignment, zero, try_tcache, arena)); + else if (zero) + return (icalloct(usize, try_tcache, arena)); else - ret = (ptr != NULL) ? isalloc(ptr, config_prof) : 0; - - return (ret); + return (imalloct(usize, try_tcache, arena)); } -void -je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque, - const char *opts) +static void * +imallocx_prof_sample(size_t usize, size_t alignment, bool zero, bool try_tcache, + arena_t *arena, prof_thr_cnt_t *cnt) { + void *p; - stats_print(write_cb, cbopaque, opts); + if (cnt == NULL) + return (NULL); + if (prof_promote && usize <= SMALL_MAXCLASS) { + size_t usize_promoted = (alignment == 0) ? + s2u(SMALL_MAXCLASS+1) : sa2u(SMALL_MAXCLASS+1, alignment); + assert(usize_promoted != 0); + p = imallocx(usize_promoted, alignment, zero, try_tcache, + arena); + if (p == NULL) + return (NULL); + arena_prof_promoted(p, usize); + } else + p = imallocx(usize, alignment, zero, try_tcache, arena); + + return (p); } -int -je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, - size_t newlen) +JEMALLOC_ALWAYS_INLINE_C void * +imallocx_prof(size_t usize, size_t alignment, bool zero, bool try_tcache, + arena_t *arena, prof_thr_cnt_t *cnt) { + void *p; - if (malloc_init()) - return (EAGAIN); + if ((uintptr_t)cnt != (uintptr_t)1U) { + p = imallocx_prof_sample(usize, alignment, zero, try_tcache, + arena, cnt); + } else + p = imallocx(usize, alignment, zero, try_tcache, arena); + if (p == NULL) + return (NULL); + prof_malloc(p, usize, cnt); - return (ctl_byname(name, oldp, oldlenp, newp, newlen)); + return (p); } -int -je_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp) +void * +je_mallocx(size_t size, int flags) { + void *p; + size_t usize; + size_t alignment = (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK) + & (SIZE_T_MAX-1)); + bool zero = flags & MALLOCX_ZERO; + unsigned arena_ind = ((unsigned)(flags >> 8)) - 1; + arena_t *arena; + bool try_tcache; + + assert(size != 0); if (malloc_init()) - return (EAGAIN); + goto label_oom; - return (ctl_nametomib(name, mibp, miblenp)); + if (arena_ind != UINT_MAX) { + arena = arenas[arena_ind]; + try_tcache = false; + } else { + arena = NULL; + try_tcache = true; + } + + usize = (alignment == 0) ? s2u(size) : sa2u(size, alignment); + assert(usize != 0); + + if (config_prof && opt_prof) { + prof_thr_cnt_t *cnt; + + PROF_ALLOC_PREP(1, usize, cnt); + p = imallocx_prof(usize, alignment, zero, try_tcache, arena, + cnt); + } else + p = imallocx(usize, alignment, zero, try_tcache, arena); + if (p == NULL) + goto label_oom; + + if (config_stats) { + assert(usize == isalloc(p, config_prof)); + thread_allocated_tsd_get()->allocated += usize; + } + UTRACE(0, size, p); + JEMALLOC_VALGRIND_MALLOC(true, p, usize, zero); + return (p); +label_oom: + if (config_xmalloc && opt_xmalloc) { + malloc_write("<jemalloc>: Error in mallocx(): out of memory\n"); + abort(); + } + UTRACE(0, size, 0); + return (NULL); } -int -je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, - void *newp, size_t newlen) +static void * +irallocx_prof_sample(void *oldptr, size_t size, size_t alignment, size_t usize, + bool zero, bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena, + prof_thr_cnt_t *cnt) { + void *p; - if (malloc_init()) - return (EAGAIN); + if (cnt == NULL) + return (NULL); + if (prof_promote && usize <= SMALL_MAXCLASS) { + p = iralloct(oldptr, SMALL_MAXCLASS+1, (SMALL_MAXCLASS+1 >= + size) ? 0 : size - (SMALL_MAXCLASS+1), alignment, zero, + try_tcache_alloc, try_tcache_dalloc, arena); + if (p == NULL) + return (NULL); + arena_prof_promoted(p, usize); + } else { + p = iralloct(oldptr, size, 0, alignment, zero, + try_tcache_alloc, try_tcache_dalloc, arena); + } - return (ctl_bymib(mib, miblen, oldp, oldlenp, newp, newlen)); + return (p); } -/* - * End non-standard functions. - */ -/******************************************************************************/ -/* - * Begin experimental functions. - */ -#ifdef JEMALLOC_EXPERIMENTAL - -static JEMALLOC_ATTR(always_inline) void * -iallocm(size_t usize, size_t alignment, bool zero, bool try_tcache, - arena_t *arena) +JEMALLOC_ALWAYS_INLINE_C void * +irallocx_prof(void *oldptr, size_t old_usize, size_t size, size_t alignment, + size_t *usize, bool zero, bool try_tcache_alloc, bool try_tcache_dalloc, + arena_t *arena, prof_thr_cnt_t *cnt) { + void *p; + prof_ctx_t *old_ctx; - assert(usize == ((alignment == 0) ? s2u(usize) : sa2u(usize, - alignment))); + old_ctx = prof_ctx_get(oldptr); + if ((uintptr_t)cnt != (uintptr_t)1U) + p = irallocx_prof_sample(oldptr, size, alignment, *usize, zero, + try_tcache_alloc, try_tcache_dalloc, arena, cnt); + else { + p = iralloct(oldptr, size, 0, alignment, zero, + try_tcache_alloc, try_tcache_dalloc, arena); + } + if (p == NULL) + return (NULL); - if (alignment != 0) - return (ipallocx(usize, alignment, zero, try_tcache, arena)); - else if (zero) - return (icallocx(usize, try_tcache, arena)); - else - return (imallocx(usize, try_tcache, arena)); + if (p == oldptr && alignment != 0) { + /* + * The allocation did not move, so it is possible that the size + * class is smaller than would guarantee the requested + * alignment, and that the alignment constraint was + * serendipitously satisfied. Additionally, old_usize may not + * be the same as the current usize because of in-place large + * reallocation. Therefore, query the actual value of usize. + */ + *usize = isalloc(p, config_prof); + } + prof_realloc(p, *usize, cnt, old_usize, old_ctx); + + return (p); } -int -je_allocm(void **ptr, size_t *rsize, size_t size, int flags) +void * +je_rallocx(void *ptr, size_t size, int flags) { void *p; - size_t usize; - size_t alignment = (ZU(1) << (flags & ALLOCM_LG_ALIGN_MASK) + size_t usize, old_usize; + UNUSED size_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0); + size_t alignment = (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK) & (SIZE_T_MAX-1)); - bool zero = flags & ALLOCM_ZERO; + bool zero = flags & MALLOCX_ZERO; unsigned arena_ind = ((unsigned)(flags >> 8)) - 1; + bool try_tcache_alloc, try_tcache_dalloc; arena_t *arena; - bool try_tcache; assert(ptr != NULL); assert(size != 0); - - if (malloc_init()) - goto label_oom; + assert(malloc_initialized || IS_INITIALIZER); + malloc_thread_init(); if (arena_ind != UINT_MAX) { + arena_chunk_t *chunk; + try_tcache_alloc = false; + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + try_tcache_dalloc = (chunk == ptr || chunk->arena != + arenas[arena_ind]); arena = arenas[arena_ind]; - try_tcache = false; } else { + try_tcache_alloc = true; + try_tcache_dalloc = true; arena = NULL; - try_tcache = true; } - usize = (alignment == 0) ? s2u(size) : sa2u(size, alignment); - if (usize == 0) - goto label_oom; + if ((config_prof && opt_prof) || config_stats || + (config_valgrind && opt_valgrind)) + old_usize = isalloc(ptr, config_prof); + if (config_valgrind && opt_valgrind) + old_rzsize = u2rz(old_usize); if (config_prof && opt_prof) { prof_thr_cnt_t *cnt; + usize = (alignment == 0) ? s2u(size) : sa2u(size, alignment); + assert(usize != 0); PROF_ALLOC_PREP(1, usize, cnt); - if (cnt == NULL) + p = irallocx_prof(ptr, old_usize, size, alignment, &usize, zero, + try_tcache_alloc, try_tcache_dalloc, arena, cnt); + if (p == NULL) goto label_oom; - if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && usize <= - SMALL_MAXCLASS) { - size_t usize_promoted = (alignment == 0) ? - s2u(SMALL_MAXCLASS+1) : sa2u(SMALL_MAXCLASS+1, - alignment); - assert(usize_promoted != 0); - p = iallocm(usize_promoted, alignment, zero, - try_tcache, arena); - if (p == NULL) - goto label_oom; - arena_prof_promoted(p, usize); - } else { - p = iallocm(usize, alignment, zero, try_tcache, arena); - if (p == NULL) - goto label_oom; - } - prof_malloc(p, usize, cnt); } else { - p = iallocm(usize, alignment, zero, try_tcache, arena); + p = iralloct(ptr, size, 0, alignment, zero, try_tcache_alloc, + try_tcache_dalloc, arena); if (p == NULL) goto label_oom; + if (config_stats || (config_valgrind && opt_valgrind)) + usize = isalloc(p, config_prof); } - if (rsize != NULL) - *rsize = usize; - *ptr = p; if (config_stats) { - assert(usize == isalloc(p, config_prof)); - thread_allocated_tsd_get()->allocated += usize; + thread_allocated_t *ta; + ta = thread_allocated_tsd_get(); + ta->allocated += usize; + ta->deallocated += old_usize; } - UTRACE(0, size, p); - JEMALLOC_VALGRIND_MALLOC(true, p, usize, zero); - return (ALLOCM_SUCCESS); + UTRACE(ptr, size, p); + JEMALLOC_VALGRIND_REALLOC(p, usize, ptr, old_usize, old_rzsize, zero); + return (p); label_oom: if (config_xmalloc && opt_xmalloc) { - malloc_write("<jemalloc>: Error in allocm(): " - "out of memory\n"); + malloc_write("<jemalloc>: Error in rallocx(): out of memory\n"); abort(); } - *ptr = NULL; - UTRACE(0, size, 0); - return (ALLOCM_ERR_OOM); + UTRACE(ptr, size, 0); + return (NULL); } -int -je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags) +JEMALLOC_ALWAYS_INLINE_C size_t +ixallocx_helper(void *ptr, size_t old_usize, size_t size, size_t extra, + size_t alignment, bool zero, arena_t *arena) +{ + size_t usize; + + if (ixalloc(ptr, size, extra, alignment, zero)) + return (old_usize); + usize = isalloc(ptr, config_prof); + + return (usize); +} + +static size_t +ixallocx_prof_sample(void *ptr, size_t old_usize, size_t size, size_t extra, + size_t alignment, size_t max_usize, bool zero, arena_t *arena, + prof_thr_cnt_t *cnt) { - void *p, *q; size_t usize; - size_t old_size; - size_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0); - size_t alignment = (ZU(1) << (flags & ALLOCM_LG_ALIGN_MASK) + + if (cnt == NULL) + return (old_usize); + /* Use minimum usize to determine whether promotion may happen. */ + if (prof_promote && ((alignment == 0) ? s2u(size) : sa2u(size, + alignment)) <= SMALL_MAXCLASS) { + if (ixalloc(ptr, SMALL_MAXCLASS+1, (SMALL_MAXCLASS+1 >= + size+extra) ? 0 : size+extra - (SMALL_MAXCLASS+1), + alignment, zero)) + return (old_usize); + usize = isalloc(ptr, config_prof); + if (max_usize < PAGE) + arena_prof_promoted(ptr, usize); + } else { + usize = ixallocx_helper(ptr, old_usize, size, extra, alignment, + zero, arena); + } + + return (usize); +} + +JEMALLOC_ALWAYS_INLINE_C size_t +ixallocx_prof(void *ptr, size_t old_usize, size_t size, size_t extra, + size_t alignment, size_t max_usize, bool zero, arena_t *arena, + prof_thr_cnt_t *cnt) +{ + size_t usize; + prof_ctx_t *old_ctx; + + old_ctx = prof_ctx_get(ptr); + if ((uintptr_t)cnt != (uintptr_t)1U) { + usize = ixallocx_prof_sample(ptr, old_usize, size, extra, + alignment, zero, max_usize, arena, cnt); + } else { + usize = ixallocx_helper(ptr, old_usize, size, extra, alignment, + zero, arena); + } + if (usize == old_usize) + return (usize); + prof_realloc(ptr, usize, cnt, old_usize, old_ctx); + + return (usize); +} + +size_t +je_xallocx(void *ptr, size_t size, size_t extra, int flags) +{ + size_t usize, old_usize; + UNUSED size_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0); + size_t alignment = (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK) & (SIZE_T_MAX-1)); - bool zero = flags & ALLOCM_ZERO; - bool no_move = flags & ALLOCM_NO_MOVE; + bool zero = flags & MALLOCX_ZERO; unsigned arena_ind = ((unsigned)(flags >> 8)) - 1; - bool try_tcache_alloc, try_tcache_dalloc; arena_t *arena; assert(ptr != NULL); - assert(*ptr != NULL); assert(size != 0); assert(SIZE_T_MAX - size >= extra); assert(malloc_initialized || IS_INITIALIZER); malloc_thread_init(); - if (arena_ind != UINT_MAX) { - arena_chunk_t *chunk; - try_tcache_alloc = true; - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(*ptr); - try_tcache_dalloc = (chunk == *ptr || chunk->arena != - arenas[arena_ind]); + if (arena_ind != UINT_MAX) arena = arenas[arena_ind]; - } else { - try_tcache_alloc = true; - try_tcache_dalloc = true; + else arena = NULL; - } - p = *ptr; + old_usize = isalloc(ptr, config_prof); + if (config_valgrind && opt_valgrind) + old_rzsize = u2rz(old_usize); + if (config_prof && opt_prof) { prof_thr_cnt_t *cnt; - /* - * usize isn't knowable before iralloc() returns when extra is + * usize isn't knowable before ixalloc() returns when extra is * non-zero. Therefore, compute its maximum possible value and * use that in PROF_ALLOC_PREP() to decide whether to capture a * backtrace. prof_realloc() will use the actual usize to @@ -1546,112 +1716,51 @@ je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags) */ size_t max_usize = (alignment == 0) ? s2u(size+extra) : sa2u(size+extra, alignment); - prof_ctx_t *old_ctx = prof_ctx_get(p); - old_size = isalloc(p, true); - if (config_valgrind && opt_valgrind) - old_rzsize = p2rz(p); PROF_ALLOC_PREP(1, max_usize, cnt); - if (cnt == NULL) - goto label_oom; - /* - * Use minimum usize to determine whether promotion may happen. - */ - if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U - && ((alignment == 0) ? s2u(size) : sa2u(size, alignment)) - <= SMALL_MAXCLASS) { - q = irallocx(p, SMALL_MAXCLASS+1, (SMALL_MAXCLASS+1 >= - size+extra) ? 0 : size+extra - (SMALL_MAXCLASS+1), - alignment, zero, no_move, try_tcache_alloc, - try_tcache_dalloc, arena); - if (q == NULL) - goto label_err; - if (max_usize < PAGE) { - usize = max_usize; - arena_prof_promoted(q, usize); - } else - usize = isalloc(q, config_prof); - } else { - q = irallocx(p, size, extra, alignment, zero, no_move, - try_tcache_alloc, try_tcache_dalloc, arena); - if (q == NULL) - goto label_err; - usize = isalloc(q, config_prof); - } - prof_realloc(q, usize, cnt, old_size, old_ctx); - if (rsize != NULL) - *rsize = usize; + usize = ixallocx_prof(ptr, old_usize, size, extra, alignment, + max_usize, zero, arena, cnt); } else { - if (config_stats) { - old_size = isalloc(p, false); - if (config_valgrind && opt_valgrind) - old_rzsize = u2rz(old_size); - } else if (config_valgrind && opt_valgrind) { - old_size = isalloc(p, false); - old_rzsize = u2rz(old_size); - } - q = irallocx(p, size, extra, alignment, zero, no_move, - try_tcache_alloc, try_tcache_dalloc, arena); - if (q == NULL) - goto label_err; - if (config_stats) - usize = isalloc(q, config_prof); - if (rsize != NULL) { - if (config_stats == false) - usize = isalloc(q, config_prof); - *rsize = usize; - } + usize = ixallocx_helper(ptr, old_usize, size, extra, alignment, + zero, arena); } + if (usize == old_usize) + goto label_not_resized; - *ptr = q; if (config_stats) { thread_allocated_t *ta; ta = thread_allocated_tsd_get(); ta->allocated += usize; - ta->deallocated += old_size; + ta->deallocated += old_usize; } - UTRACE(p, size, q); - JEMALLOC_VALGRIND_REALLOC(q, usize, p, old_size, old_rzsize, zero); - return (ALLOCM_SUCCESS); -label_err: - if (no_move) { - UTRACE(p, size, q); - return (ALLOCM_ERR_NOT_MOVED); - } -label_oom: - if (config_xmalloc && opt_xmalloc) { - malloc_write("<jemalloc>: Error in rallocm(): " - "out of memory\n"); - abort(); - } - UTRACE(p, size, 0); - return (ALLOCM_ERR_OOM); + JEMALLOC_VALGRIND_REALLOC(ptr, usize, ptr, old_usize, old_rzsize, zero); +label_not_resized: + UTRACE(ptr, size, ptr); + return (usize); } -int -je_sallocm(const void *ptr, size_t *rsize, int flags) +size_t +je_sallocx(const void *ptr, int flags) { - size_t sz; + size_t usize; assert(malloc_initialized || IS_INITIALIZER); malloc_thread_init(); if (config_ivsalloc) - sz = ivsalloc(ptr, config_prof); + usize = ivsalloc(ptr, config_prof); else { assert(ptr != NULL); - sz = isalloc(ptr, config_prof); + usize = isalloc(ptr, config_prof); } - assert(rsize != NULL); - *rsize = sz; - return (ALLOCM_SUCCESS); + return (usize); } -int -je_dallocm(void *ptr, int flags) +void +je_dallocx(void *ptr, int flags) { size_t usize; - size_t rzsize JEMALLOC_CC_SILENCE_INIT(0); + UNUSED size_t rzsize JEMALLOC_CC_SILENCE_INIT(0); unsigned arena_ind = ((unsigned)(flags >> 8)) - 1; bool try_tcache; @@ -1677,28 +1786,162 @@ je_dallocm(void *ptr, int flags) thread_allocated_tsd_get()->deallocated += usize; if (config_valgrind && opt_valgrind) rzsize = p2rz(ptr); - iqallocx(ptr, try_tcache); + iqalloct(ptr, try_tcache); JEMALLOC_VALGRIND_FREE(ptr, rzsize); - - return (ALLOCM_SUCCESS); } -int -je_nallocm(size_t *rsize, size_t size, int flags) +size_t +je_nallocx(size_t size, int flags) { size_t usize; - size_t alignment = (ZU(1) << (flags & ALLOCM_LG_ALIGN_MASK) + size_t alignment = (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK) & (SIZE_T_MAX-1)); assert(size != 0); if (malloc_init()) - return (ALLOCM_ERR_OOM); + return (0); usize = (alignment == 0) ? s2u(size) : sa2u(size, alignment); - if (usize == 0) + assert(usize != 0); + return (usize); +} + +int +je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, + size_t newlen) +{ + + if (malloc_init()) + return (EAGAIN); + + return (ctl_byname(name, oldp, oldlenp, newp, newlen)); +} + +int +je_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp) +{ + + if (malloc_init()) + return (EAGAIN); + + return (ctl_nametomib(name, mibp, miblenp)); +} + +int +je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + + if (malloc_init()) + return (EAGAIN); + + return (ctl_bymib(mib, miblen, oldp, oldlenp, newp, newlen)); +} + +void +je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque, + const char *opts) +{ + + stats_print(write_cb, cbopaque, opts); +} + +size_t +je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) +{ + size_t ret; + + assert(malloc_initialized || IS_INITIALIZER); + malloc_thread_init(); + + if (config_ivsalloc) + ret = ivsalloc(ptr, config_prof); + else + ret = (ptr != NULL) ? isalloc(ptr, config_prof) : 0; + + return (ret); +} + +/* + * End non-standard functions. + */ +/******************************************************************************/ +/* + * Begin experimental functions. + */ +#ifdef JEMALLOC_EXPERIMENTAL + +int +je_allocm(void **ptr, size_t *rsize, size_t size, int flags) +{ + void *p; + + assert(ptr != NULL); + + p = je_mallocx(size, flags); + if (p == NULL) return (ALLOCM_ERR_OOM); + if (rsize != NULL) + *rsize = isalloc(p, config_prof); + *ptr = p; + return (ALLOCM_SUCCESS); +} +int +je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags) +{ + int ret; + bool no_move = flags & ALLOCM_NO_MOVE; + + assert(ptr != NULL); + assert(*ptr != NULL); + assert(size != 0); + assert(SIZE_T_MAX - size >= extra); + + if (no_move) { + size_t usize = je_xallocx(*ptr, size, extra, flags); + ret = (usize >= size) ? ALLOCM_SUCCESS : ALLOCM_ERR_NOT_MOVED; + if (rsize != NULL) + *rsize = usize; + } else { + void *p = je_rallocx(*ptr, size+extra, flags); + if (p != NULL) { + *ptr = p; + ret = ALLOCM_SUCCESS; + } else + ret = ALLOCM_ERR_OOM; + if (rsize != NULL) + *rsize = isalloc(*ptr, config_prof); + } + return (ret); +} + +int +je_sallocm(const void *ptr, size_t *rsize, int flags) +{ + + assert(rsize != NULL); + *rsize = je_sallocx(ptr, flags); + return (ALLOCM_SUCCESS); +} + +int +je_dallocm(void *ptr, int flags) +{ + + je_dallocx(ptr, flags); + return (ALLOCM_SUCCESS); +} + +int +je_nallocm(size_t *rsize, size_t size, int flags) +{ + size_t usize; + + usize = je_nallocx(size, flags); + if (usize == 0) + return (ALLOCM_ERR_OOM); if (rsize != NULL) *rsize = usize; return (ALLOCM_SUCCESS); @@ -1833,7 +2076,7 @@ a0alloc(size_t size, bool zero) if (size <= arena_maxclass) return (arena_malloc(arenas[0], size, zero, false)); else - return (huge_malloc(size, zero)); + return (huge_malloc(size, zero, huge_dss_prec_get(arenas[0]))); } void * diff --git a/dep/jemalloc/src/mutex.c b/dep/jemalloc/src/mutex.c index 55e18c23713..788eca38703 100644 --- a/dep/jemalloc/src/mutex.c +++ b/dep/jemalloc/src/mutex.c @@ -6,7 +6,7 @@ #endif #ifndef _CRT_SPINCOUNT -#define _CRT_SPINCOUNT 4000 +#define _CRT_SPINCOUNT 4000 #endif /******************************************************************************/ diff --git a/dep/jemalloc/src/prof.c b/dep/jemalloc/src/prof.c index c133b95c2c6..7722b7b4373 100644 --- a/dep/jemalloc/src/prof.c +++ b/dep/jemalloc/src/prof.c @@ -24,7 +24,12 @@ bool opt_prof_gdump = false; bool opt_prof_final = true; bool opt_prof_leak = false; bool opt_prof_accum = false; -char opt_prof_prefix[PATH_MAX + 1]; +char opt_prof_prefix[ + /* Minimize memory bloat for non-prof builds. */ +#ifdef JEMALLOC_PROF + PATH_MAX + +#endif + 1]; uint64_t prof_interval = 0; bool prof_promote; @@ -54,10 +59,17 @@ static uint64_t prof_dump_useq; /* * This buffer is rather large for stack allocation, so use a single buffer for - * all profile dumps. The buffer is implicitly protected by bt2ctx_mtx, since - * it must be locked anyway during dumping. + * all profile dumps. */ -static char prof_dump_buf[PROF_DUMP_BUFSIZE]; +static malloc_mutex_t prof_dump_mtx; +static char prof_dump_buf[ + /* Minimize memory bloat for non-prof builds. */ +#ifdef JEMALLOC_PROF + PROF_DUMP_BUFSIZE +#else + 1 +#endif +]; static unsigned prof_dump_buf_end; static int prof_dump_fd; @@ -65,36 +77,6 @@ static int prof_dump_fd; static bool prof_booted = false; /******************************************************************************/ -/* Function prototypes for non-inline static functions. */ - -static prof_bt_t *bt_dup(prof_bt_t *bt); -static void bt_destroy(prof_bt_t *bt); -#ifdef JEMALLOC_PROF_LIBGCC -static _Unwind_Reason_Code prof_unwind_init_callback( - struct _Unwind_Context *context, void *arg); -static _Unwind_Reason_Code prof_unwind_callback( - struct _Unwind_Context *context, void *arg); -#endif -static bool prof_flush(bool propagate_err); -static bool prof_write(bool propagate_err, const char *s); -static bool prof_printf(bool propagate_err, const char *format, ...) - JEMALLOC_ATTR(format(printf, 2, 3)); -static void prof_ctx_sum(prof_ctx_t *ctx, prof_cnt_t *cnt_all, - size_t *leak_nctx); -static void prof_ctx_destroy(prof_ctx_t *ctx); -static void prof_ctx_merge(prof_ctx_t *ctx, prof_thr_cnt_t *cnt); -static bool prof_dump_ctx(bool propagate_err, prof_ctx_t *ctx, - prof_bt_t *bt); -static bool prof_dump_maps(bool propagate_err); -static bool prof_dump(bool propagate_err, const char *filename, - bool leakcheck); -static void prof_dump_filename(char *filename, char v, int64_t vseq); -static void prof_fdump(void); -static void prof_bt_hash(const void *key, size_t r_hash[2]); -static bool prof_bt_keycomp(const void *k1, const void *k2); -static malloc_mutex_t *prof_ctx_mutex_choose(void); - -/******************************************************************************/ void bt_init(prof_bt_t *bt, void **vec) @@ -423,10 +405,169 @@ prof_backtrace(prof_bt_t *bt, unsigned nignore) { cassert(config_prof); - assert(false); + not_reached(); } #endif +static malloc_mutex_t * +prof_ctx_mutex_choose(void) +{ + unsigned nctxs = atomic_add_u(&cum_ctxs, 1); + + return (&ctx_locks[(nctxs - 1) % PROF_NCTX_LOCKS]); +} + +static void +prof_ctx_init(prof_ctx_t *ctx, prof_bt_t *bt) +{ + + ctx->bt = bt; + ctx->lock = prof_ctx_mutex_choose(); + /* + * Set nlimbo to 1, in order to avoid a race condition with + * prof_ctx_merge()/prof_ctx_destroy(). + */ + ctx->nlimbo = 1; + ql_elm_new(ctx, dump_link); + memset(&ctx->cnt_merged, 0, sizeof(prof_cnt_t)); + ql_new(&ctx->cnts_ql); +} + +static void +prof_ctx_destroy(prof_ctx_t *ctx) +{ + prof_tdata_t *prof_tdata; + + cassert(config_prof); + + /* + * Check that ctx is still unused by any thread cache before destroying + * it. prof_lookup() increments ctx->nlimbo in order to avoid a race + * condition with this function, as does prof_ctx_merge() in order to + * avoid a race between the main body of prof_ctx_merge() and entry + * into this function. + */ + prof_tdata = prof_tdata_get(false); + assert((uintptr_t)prof_tdata > (uintptr_t)PROF_TDATA_STATE_MAX); + prof_enter(prof_tdata); + malloc_mutex_lock(ctx->lock); + if (ql_first(&ctx->cnts_ql) == NULL && ctx->cnt_merged.curobjs == 0 && + ctx->nlimbo == 1) { + assert(ctx->cnt_merged.curbytes == 0); + assert(ctx->cnt_merged.accumobjs == 0); + assert(ctx->cnt_merged.accumbytes == 0); + /* Remove ctx from bt2ctx. */ + if (ckh_remove(&bt2ctx, ctx->bt, NULL, NULL)) + not_reached(); + prof_leave(prof_tdata); + /* Destroy ctx. */ + malloc_mutex_unlock(ctx->lock); + bt_destroy(ctx->bt); + idalloc(ctx); + } else { + /* + * Compensate for increment in prof_ctx_merge() or + * prof_lookup(). + */ + ctx->nlimbo--; + malloc_mutex_unlock(ctx->lock); + prof_leave(prof_tdata); + } +} + +static void +prof_ctx_merge(prof_ctx_t *ctx, prof_thr_cnt_t *cnt) +{ + bool destroy; + + cassert(config_prof); + + /* Merge cnt stats and detach from ctx. */ + malloc_mutex_lock(ctx->lock); + ctx->cnt_merged.curobjs += cnt->cnts.curobjs; + ctx->cnt_merged.curbytes += cnt->cnts.curbytes; + ctx->cnt_merged.accumobjs += cnt->cnts.accumobjs; + ctx->cnt_merged.accumbytes += cnt->cnts.accumbytes; + ql_remove(&ctx->cnts_ql, cnt, cnts_link); + if (opt_prof_accum == false && ql_first(&ctx->cnts_ql) == NULL && + ctx->cnt_merged.curobjs == 0 && ctx->nlimbo == 0) { + /* + * Increment ctx->nlimbo in order to keep another thread from + * winning the race to destroy ctx while this one has ctx->lock + * dropped. Without this, it would be possible for another + * thread to: + * + * 1) Sample an allocation associated with ctx. + * 2) Deallocate the sampled object. + * 3) Successfully prof_ctx_destroy(ctx). + * + * The result would be that ctx no longer exists by the time + * this thread accesses it in prof_ctx_destroy(). + */ + ctx->nlimbo++; + destroy = true; + } else + destroy = false; + malloc_mutex_unlock(ctx->lock); + if (destroy) + prof_ctx_destroy(ctx); +} + +static bool +prof_lookup_global(prof_bt_t *bt, prof_tdata_t *prof_tdata, void **p_btkey, + prof_ctx_t **p_ctx, bool *p_new_ctx) +{ + union { + prof_ctx_t *p; + void *v; + } ctx; + union { + prof_bt_t *p; + void *v; + } btkey; + bool new_ctx; + + prof_enter(prof_tdata); + if (ckh_search(&bt2ctx, bt, &btkey.v, &ctx.v)) { + /* bt has never been seen before. Insert it. */ + ctx.v = imalloc(sizeof(prof_ctx_t)); + if (ctx.v == NULL) { + prof_leave(prof_tdata); + return (true); + } + btkey.p = bt_dup(bt); + if (btkey.v == NULL) { + prof_leave(prof_tdata); + idalloc(ctx.v); + return (true); + } + prof_ctx_init(ctx.p, btkey.p); + if (ckh_insert(&bt2ctx, btkey.v, ctx.v)) { + /* OOM. */ + prof_leave(prof_tdata); + idalloc(btkey.v); + idalloc(ctx.v); + return (true); + } + new_ctx = true; + } else { + /* + * Increment nlimbo, in order to avoid a race condition with + * prof_ctx_merge()/prof_ctx_destroy(). + */ + malloc_mutex_lock(ctx.p->lock); + ctx.p->nlimbo++; + malloc_mutex_unlock(ctx.p->lock); + new_ctx = false; + } + prof_leave(prof_tdata); + + *p_btkey = btkey.v; + *p_ctx = ctx.p; + *p_new_ctx = new_ctx; + return (false); +} + prof_thr_cnt_t * prof_lookup(prof_bt_t *bt) { @@ -443,62 +584,16 @@ prof_lookup(prof_bt_t *bt) return (NULL); if (ckh_search(&prof_tdata->bt2cnt, bt, NULL, &ret.v)) { - union { - prof_bt_t *p; - void *v; - } btkey; - union { - prof_ctx_t *p; - void *v; - } ctx; + void *btkey; + prof_ctx_t *ctx; bool new_ctx; /* * This thread's cache lacks bt. Look for it in the global * cache. */ - prof_enter(prof_tdata); - if (ckh_search(&bt2ctx, bt, &btkey.v, &ctx.v)) { - /* bt has never been seen before. Insert it. */ - ctx.v = imalloc(sizeof(prof_ctx_t)); - if (ctx.v == NULL) { - prof_leave(prof_tdata); - return (NULL); - } - btkey.p = bt_dup(bt); - if (btkey.v == NULL) { - prof_leave(prof_tdata); - idalloc(ctx.v); - return (NULL); - } - ctx.p->bt = btkey.p; - ctx.p->lock = prof_ctx_mutex_choose(); - /* - * Set nlimbo to 1, in order to avoid a race condition - * with prof_ctx_merge()/prof_ctx_destroy(). - */ - ctx.p->nlimbo = 1; - memset(&ctx.p->cnt_merged, 0, sizeof(prof_cnt_t)); - ql_new(&ctx.p->cnts_ql); - if (ckh_insert(&bt2ctx, btkey.v, ctx.v)) { - /* OOM. */ - prof_leave(prof_tdata); - idalloc(btkey.v); - idalloc(ctx.v); - return (NULL); - } - new_ctx = true; - } else { - /* - * Increment nlimbo, in order to avoid a race condition - * with prof_ctx_merge()/prof_ctx_destroy(). - */ - malloc_mutex_lock(ctx.p->lock); - ctx.p->nlimbo++; - malloc_mutex_unlock(ctx.p->lock); - new_ctx = false; - } - prof_leave(prof_tdata); + if (prof_lookup_global(bt, prof_tdata, &btkey, &ctx, &new_ctx)) + return (NULL); /* Link a prof_thd_cnt_t into ctx for this thread. */ if (ckh_count(&prof_tdata->bt2cnt) == PROF_TCMAX) { @@ -511,7 +606,7 @@ prof_lookup(prof_bt_t *bt) assert(ret.v != NULL); if (ckh_remove(&prof_tdata->bt2cnt, ret.p->ctx->bt, NULL, NULL)) - assert(false); + not_reached(); ql_remove(&prof_tdata->lru_ql, ret.p, lru_link); prof_ctx_merge(ret.p->ctx, ret.p); /* ret can now be re-used. */ @@ -521,27 +616,27 @@ prof_lookup(prof_bt_t *bt) ret.v = imalloc(sizeof(prof_thr_cnt_t)); if (ret.p == NULL) { if (new_ctx) - prof_ctx_destroy(ctx.p); + prof_ctx_destroy(ctx); return (NULL); } ql_elm_new(ret.p, cnts_link); ql_elm_new(ret.p, lru_link); } /* Finish initializing ret. */ - ret.p->ctx = ctx.p; + ret.p->ctx = ctx; ret.p->epoch = 0; memset(&ret.p->cnts, 0, sizeof(prof_cnt_t)); - if (ckh_insert(&prof_tdata->bt2cnt, btkey.v, ret.v)) { + if (ckh_insert(&prof_tdata->bt2cnt, btkey, ret.v)) { if (new_ctx) - prof_ctx_destroy(ctx.p); + prof_ctx_destroy(ctx); idalloc(ret.v); return (NULL); } ql_head_insert(&prof_tdata->lru_ql, ret.p, lru_link); - malloc_mutex_lock(ctx.p->lock); - ql_tail_insert(&ctx.p->cnts_ql, ret.p, cnts_link); - ctx.p->nlimbo--; - malloc_mutex_unlock(ctx.p->lock); + malloc_mutex_lock(ctx->lock); + ql_tail_insert(&ctx->cnts_ql, ret.p, cnts_link); + ctx->nlimbo--; + malloc_mutex_unlock(ctx->lock); } else { /* Move ret to the front of the LRU. */ ql_remove(&prof_tdata->lru_ql, ret.p, lru_link); @@ -551,8 +646,52 @@ prof_lookup(prof_bt_t *bt) return (ret.p); } +#ifdef JEMALLOC_JET +size_t +prof_bt_count(void) +{ + size_t bt_count; + prof_tdata_t *prof_tdata; + + prof_tdata = prof_tdata_get(false); + if ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) + return (0); + + prof_enter(prof_tdata); + bt_count = ckh_count(&bt2ctx); + prof_leave(prof_tdata); + + return (bt_count); +} +#endif + +#ifdef JEMALLOC_JET +#undef prof_dump_open +#define prof_dump_open JEMALLOC_N(prof_dump_open_impl) +#endif +static int +prof_dump_open(bool propagate_err, const char *filename) +{ + int fd; + + fd = creat(filename, 0644); + if (fd == -1 && propagate_err == false) { + malloc_printf("<jemalloc>: creat(\"%s\"), 0644) failed\n", + filename); + if (opt_abort) + abort(); + } + + return (fd); +} +#ifdef JEMALLOC_JET +#undef prof_dump_open +#define prof_dump_open JEMALLOC_N(prof_dump_open) +prof_dump_open_t *prof_dump_open = JEMALLOC_N(prof_dump_open_impl); +#endif + static bool -prof_flush(bool propagate_err) +prof_dump_flush(bool propagate_err) { bool ret = false; ssize_t err; @@ -575,7 +714,20 @@ prof_flush(bool propagate_err) } static bool -prof_write(bool propagate_err, const char *s) +prof_dump_close(bool propagate_err) +{ + bool ret; + + assert(prof_dump_fd != -1); + ret = prof_dump_flush(propagate_err); + close(prof_dump_fd); + prof_dump_fd = -1; + + return (ret); +} + +static bool +prof_dump_write(bool propagate_err, const char *s) { unsigned i, slen, n; @@ -586,7 +738,7 @@ prof_write(bool propagate_err, const char *s) while (i < slen) { /* Flush the buffer if it is full. */ if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) - if (prof_flush(propagate_err) && propagate_err) + if (prof_dump_flush(propagate_err) && propagate_err) return (true); if (prof_dump_buf_end + slen <= PROF_DUMP_BUFSIZE) { @@ -606,7 +758,7 @@ prof_write(bool propagate_err, const char *s) JEMALLOC_ATTR(format(printf, 2, 3)) static bool -prof_printf(bool propagate_err, const char *format, ...) +prof_dump_printf(bool propagate_err, const char *format, ...) { bool ret; va_list ap; @@ -615,13 +767,14 @@ prof_printf(bool propagate_err, const char *format, ...) va_start(ap, format); malloc_vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); - ret = prof_write(propagate_err, buf); + ret = prof_dump_write(propagate_err, buf); return (ret); } static void -prof_ctx_sum(prof_ctx_t *ctx, prof_cnt_t *cnt_all, size_t *leak_nctx) +prof_dump_ctx_prep(prof_ctx_t *ctx, prof_cnt_t *cnt_all, size_t *leak_nctx, + prof_ctx_list_t *ctx_ql) { prof_thr_cnt_t *thr_cnt; prof_cnt_t tcnt; @@ -630,6 +783,14 @@ prof_ctx_sum(prof_ctx_t *ctx, prof_cnt_t *cnt_all, size_t *leak_nctx) malloc_mutex_lock(ctx->lock); + /* + * Increment nlimbo so that ctx won't go away before dump. + * Additionally, link ctx into the dump list so that it is included in + * prof_dump()'s second pass. + */ + ctx->nlimbo++; + ql_tail_insert(ctx_ql, ctx, dump_link); + memcpy(&ctx->cnt_summed, &ctx->cnt_merged, sizeof(prof_cnt_t)); ql_foreach(thr_cnt, &ctx->cnts_ql, cnts_link) { volatile unsigned *epoch = &thr_cnt->epoch; @@ -670,89 +831,52 @@ prof_ctx_sum(prof_ctx_t *ctx, prof_cnt_t *cnt_all, size_t *leak_nctx) malloc_mutex_unlock(ctx->lock); } -static void -prof_ctx_destroy(prof_ctx_t *ctx) +static bool +prof_dump_header(bool propagate_err, const prof_cnt_t *cnt_all) { - prof_tdata_t *prof_tdata; - cassert(config_prof); - - /* - * Check that ctx is still unused by any thread cache before destroying - * it. prof_lookup() increments ctx->nlimbo in order to avoid a race - * condition with this function, as does prof_ctx_merge() in order to - * avoid a race between the main body of prof_ctx_merge() and entry - * into this function. - */ - prof_tdata = prof_tdata_get(false); - assert((uintptr_t)prof_tdata > (uintptr_t)PROF_TDATA_STATE_MAX); - prof_enter(prof_tdata); - malloc_mutex_lock(ctx->lock); - if (ql_first(&ctx->cnts_ql) == NULL && ctx->cnt_merged.curobjs == 0 && - ctx->nlimbo == 1) { - assert(ctx->cnt_merged.curbytes == 0); - assert(ctx->cnt_merged.accumobjs == 0); - assert(ctx->cnt_merged.accumbytes == 0); - /* Remove ctx from bt2ctx. */ - if (ckh_remove(&bt2ctx, ctx->bt, NULL, NULL)) - assert(false); - prof_leave(prof_tdata); - /* Destroy ctx. */ - malloc_mutex_unlock(ctx->lock); - bt_destroy(ctx->bt); - idalloc(ctx); + if (opt_lg_prof_sample == 0) { + if (prof_dump_printf(propagate_err, + "heap profile: %"PRId64": %"PRId64 + " [%"PRIu64": %"PRIu64"] @ heapprofile\n", + cnt_all->curobjs, cnt_all->curbytes, + cnt_all->accumobjs, cnt_all->accumbytes)) + return (true); } else { - /* - * Compensate for increment in prof_ctx_merge() or - * prof_lookup(). - */ - ctx->nlimbo--; - malloc_mutex_unlock(ctx->lock); - prof_leave(prof_tdata); + if (prof_dump_printf(propagate_err, + "heap profile: %"PRId64": %"PRId64 + " [%"PRIu64": %"PRIu64"] @ heap_v2/%"PRIu64"\n", + cnt_all->curobjs, cnt_all->curbytes, + cnt_all->accumobjs, cnt_all->accumbytes, + ((uint64_t)1U << opt_lg_prof_sample))) + return (true); } + + return (false); } static void -prof_ctx_merge(prof_ctx_t *ctx, prof_thr_cnt_t *cnt) +prof_dump_ctx_cleanup_locked(prof_ctx_t *ctx, prof_ctx_list_t *ctx_ql) { - bool destroy; - cassert(config_prof); + ctx->nlimbo--; + ql_remove(ctx_ql, ctx, dump_link); +} + +static void +prof_dump_ctx_cleanup(prof_ctx_t *ctx, prof_ctx_list_t *ctx_ql) +{ - /* Merge cnt stats and detach from ctx. */ malloc_mutex_lock(ctx->lock); - ctx->cnt_merged.curobjs += cnt->cnts.curobjs; - ctx->cnt_merged.curbytes += cnt->cnts.curbytes; - ctx->cnt_merged.accumobjs += cnt->cnts.accumobjs; - ctx->cnt_merged.accumbytes += cnt->cnts.accumbytes; - ql_remove(&ctx->cnts_ql, cnt, cnts_link); - if (opt_prof_accum == false && ql_first(&ctx->cnts_ql) == NULL && - ctx->cnt_merged.curobjs == 0 && ctx->nlimbo == 0) { - /* - * Increment ctx->nlimbo in order to keep another thread from - * winning the race to destroy ctx while this one has ctx->lock - * dropped. Without this, it would be possible for another - * thread to: - * - * 1) Sample an allocation associated with ctx. - * 2) Deallocate the sampled object. - * 3) Successfully prof_ctx_destroy(ctx). - * - * The result would be that ctx no longer exists by the time - * this thread accesses it in prof_ctx_destroy(). - */ - ctx->nlimbo++; - destroy = true; - } else - destroy = false; + prof_dump_ctx_cleanup_locked(ctx, ctx_ql); malloc_mutex_unlock(ctx->lock); - if (destroy) - prof_ctx_destroy(ctx); } static bool -prof_dump_ctx(bool propagate_err, prof_ctx_t *ctx, prof_bt_t *bt) +prof_dump_ctx(bool propagate_err, prof_ctx_t *ctx, const prof_bt_t *bt, + prof_ctx_list_t *ctx_ql) { + bool ret; unsigned i; cassert(config_prof); @@ -764,66 +888,109 @@ prof_dump_ctx(bool propagate_err, prof_ctx_t *ctx, prof_bt_t *bt) * filled in. Avoid dumping any ctx that is an artifact of either * implementation detail. */ + malloc_mutex_lock(ctx->lock); if ((opt_prof_accum == false && ctx->cnt_summed.curobjs == 0) || (opt_prof_accum && ctx->cnt_summed.accumobjs == 0)) { assert(ctx->cnt_summed.curobjs == 0); assert(ctx->cnt_summed.curbytes == 0); assert(ctx->cnt_summed.accumobjs == 0); assert(ctx->cnt_summed.accumbytes == 0); - return (false); + ret = false; + goto label_return; } - if (prof_printf(propagate_err, "%"PRId64": %"PRId64 + if (prof_dump_printf(propagate_err, "%"PRId64": %"PRId64 " [%"PRIu64": %"PRIu64"] @", ctx->cnt_summed.curobjs, ctx->cnt_summed.curbytes, - ctx->cnt_summed.accumobjs, ctx->cnt_summed.accumbytes)) - return (true); + ctx->cnt_summed.accumobjs, ctx->cnt_summed.accumbytes)) { + ret = true; + goto label_return; + } for (i = 0; i < bt->len; i++) { - if (prof_printf(propagate_err, " %#"PRIxPTR, - (uintptr_t)bt->vec[i])) - return (true); + if (prof_dump_printf(propagate_err, " %#"PRIxPTR, + (uintptr_t)bt->vec[i])) { + ret = true; + goto label_return; + } } - if (prof_write(propagate_err, "\n")) - return (true); + if (prof_dump_write(propagate_err, "\n")) { + ret = true; + goto label_return; + } - return (false); + ret = false; +label_return: + prof_dump_ctx_cleanup_locked(ctx, ctx_ql); + malloc_mutex_unlock(ctx->lock); + return (ret); } static bool prof_dump_maps(bool propagate_err) { + bool ret; int mfd; char filename[PATH_MAX + 1]; cassert(config_prof); - +#ifdef __FreeBSD__ + malloc_snprintf(filename, sizeof(filename), "/proc/curproc/map"); +#else malloc_snprintf(filename, sizeof(filename), "/proc/%d/maps", (int)getpid()); +#endif mfd = open(filename, O_RDONLY); if (mfd != -1) { ssize_t nread; - if (prof_write(propagate_err, "\nMAPPED_LIBRARIES:\n") && - propagate_err) - return (true); + if (prof_dump_write(propagate_err, "\nMAPPED_LIBRARIES:\n") && + propagate_err) { + ret = true; + goto label_return; + } nread = 0; do { prof_dump_buf_end += nread; if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) { /* Make space in prof_dump_buf before read(). */ - if (prof_flush(propagate_err) && propagate_err) - return (true); + if (prof_dump_flush(propagate_err) && + propagate_err) { + ret = true; + goto label_return; + } } nread = read(mfd, &prof_dump_buf[prof_dump_buf_end], PROF_DUMP_BUFSIZE - prof_dump_buf_end); } while (nread > 0); + } else { + ret = true; + goto label_return; + } + + ret = false; +label_return: + if (mfd != -1) close(mfd); - } else - return (true); + return (ret); +} - return (false); +static void +prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_nctx, + const char *filename) +{ + + if (cnt_all->curbytes != 0) { + malloc_printf("<jemalloc>: Leak summary: %"PRId64" byte%s, %" + PRId64" object%s, %zu context%s\n", + cnt_all->curbytes, (cnt_all->curbytes != 1) ? "s" : "", + cnt_all->curobjs, (cnt_all->curobjs != 1) ? "s" : "", + leak_nctx, (leak_nctx != 1) ? "s" : ""); + malloc_printf( + "<jemalloc>: Run pprof on \"%s\" for leak detail\n", + filename); + } } static bool @@ -833,98 +1000,74 @@ prof_dump(bool propagate_err, const char *filename, bool leakcheck) prof_cnt_t cnt_all; size_t tabind; union { - prof_bt_t *p; - void *v; - } bt; - union { prof_ctx_t *p; void *v; } ctx; size_t leak_nctx; + prof_ctx_list_t ctx_ql; cassert(config_prof); prof_tdata = prof_tdata_get(false); if ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) return (true); - prof_enter(prof_tdata); - prof_dump_fd = creat(filename, 0644); - if (prof_dump_fd == -1) { - if (propagate_err == false) { - malloc_printf( - "<jemalloc>: creat(\"%s\"), 0644) failed\n", - filename); - if (opt_abort) - abort(); - } - goto label_error; - } + + malloc_mutex_lock(&prof_dump_mtx); /* Merge per thread profile stats, and sum them in cnt_all. */ memset(&cnt_all, 0, sizeof(prof_cnt_t)); leak_nctx = 0; + ql_new(&ctx_ql); + prof_enter(prof_tdata); for (tabind = 0; ckh_iter(&bt2ctx, &tabind, NULL, &ctx.v) == false;) - prof_ctx_sum(ctx.p, &cnt_all, &leak_nctx); + prof_dump_ctx_prep(ctx.p, &cnt_all, &leak_nctx, &ctx_ql); + prof_leave(prof_tdata); + + /* Create dump file. */ + if ((prof_dump_fd = prof_dump_open(propagate_err, filename)) == -1) + goto label_open_close_error; /* Dump profile header. */ - if (opt_lg_prof_sample == 0) { - if (prof_printf(propagate_err, - "heap profile: %"PRId64": %"PRId64 - " [%"PRIu64": %"PRIu64"] @ heapprofile\n", - cnt_all.curobjs, cnt_all.curbytes, - cnt_all.accumobjs, cnt_all.accumbytes)) - goto label_error; - } else { - if (prof_printf(propagate_err, - "heap profile: %"PRId64": %"PRId64 - " [%"PRIu64": %"PRIu64"] @ heap_v2/%"PRIu64"\n", - cnt_all.curobjs, cnt_all.curbytes, - cnt_all.accumobjs, cnt_all.accumbytes, - ((uint64_t)1U << opt_lg_prof_sample))) - goto label_error; - } + if (prof_dump_header(propagate_err, &cnt_all)) + goto label_write_error; - /* Dump per ctx profile stats. */ - for (tabind = 0; ckh_iter(&bt2ctx, &tabind, &bt.v, &ctx.v) - == false;) { - if (prof_dump_ctx(propagate_err, ctx.p, bt.p)) - goto label_error; + /* Dump per ctx profile stats. */ + while ((ctx.p = ql_first(&ctx_ql)) != NULL) { + if (prof_dump_ctx(propagate_err, ctx.p, ctx.p->bt, &ctx_ql)) + goto label_write_error; } /* Dump /proc/<pid>/maps if possible. */ if (prof_dump_maps(propagate_err)) - goto label_error; + goto label_write_error; - if (prof_flush(propagate_err)) - goto label_error; - close(prof_dump_fd); - prof_leave(prof_tdata); + if (prof_dump_close(propagate_err)) + goto label_open_close_error; - if (leakcheck && cnt_all.curbytes != 0) { - malloc_printf("<jemalloc>: Leak summary: %"PRId64" byte%s, %" - PRId64" object%s, %zu context%s\n", - cnt_all.curbytes, (cnt_all.curbytes != 1) ? "s" : "", - cnt_all.curobjs, (cnt_all.curobjs != 1) ? "s" : "", - leak_nctx, (leak_nctx != 1) ? "s" : ""); - malloc_printf( - "<jemalloc>: Run pprof on \"%s\" for leak detail\n", - filename); - } + malloc_mutex_unlock(&prof_dump_mtx); + + if (leakcheck) + prof_leakcheck(&cnt_all, leak_nctx, filename); return (false); -label_error: - prof_leave(prof_tdata); +label_write_error: + prof_dump_close(propagate_err); +label_open_close_error: + while ((ctx.p = ql_first(&ctx_ql)) != NULL) + prof_dump_ctx_cleanup(ctx.p, &ctx_ql); + malloc_mutex_unlock(&prof_dump_mtx); return (true); } #define DUMP_FILENAME_BUFSIZE (PATH_MAX + 1) +#define VSEQ_INVALID UINT64_C(0xffffffffffffffff) static void prof_dump_filename(char *filename, char v, int64_t vseq) { cassert(config_prof); - if (vseq != UINT64_C(0xffffffffffffffff)) { + if (vseq != VSEQ_INVALID) { /* "<prefix>.<pid>.<seq>.v<vseq>.heap" */ malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE, "%s.%d.%"PRIu64".%c%"PRId64".heap", @@ -950,7 +1093,7 @@ prof_fdump(void) if (opt_prof_final && opt_prof_prefix[0] != '\0') { malloc_mutex_lock(&prof_dump_seq_mtx); - prof_dump_filename(filename, 'f', UINT64_C(0xffffffffffffffff)); + prof_dump_filename(filename, 'f', VSEQ_INVALID); malloc_mutex_unlock(&prof_dump_seq_mtx); prof_dump(false, filename, opt_prof_leak); } @@ -1056,14 +1199,6 @@ prof_bt_keycomp(const void *k1, const void *k2) return (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0); } -static malloc_mutex_t * -prof_ctx_mutex_choose(void) -{ - unsigned nctxs = atomic_add_u(&cum_ctxs, 1); - - return (&ctx_locks[(nctxs - 1) % PROF_NCTX_LOCKS]); -} - prof_tdata_t * prof_tdata_init(void) { @@ -1208,6 +1343,8 @@ prof_boot2(void) if (malloc_mutex_init(&prof_dump_seq_mtx)) return (true); + if (malloc_mutex_init(&prof_dump_mtx)) + return (true); if (atexit(prof_fdump) != 0) { malloc_write("<jemalloc>: Error in atexit()\n"); @@ -1245,10 +1382,10 @@ prof_prefork(void) if (opt_prof) { unsigned i; - malloc_mutex_lock(&bt2ctx_mtx); - malloc_mutex_lock(&prof_dump_seq_mtx); + malloc_mutex_prefork(&bt2ctx_mtx); + malloc_mutex_prefork(&prof_dump_seq_mtx); for (i = 0; i < PROF_NCTX_LOCKS; i++) - malloc_mutex_lock(&ctx_locks[i]); + malloc_mutex_prefork(&ctx_locks[i]); } } diff --git a/dep/jemalloc/src/quarantine.c b/dep/jemalloc/src/quarantine.c index f96a948d5c7..5431511640a 100644 --- a/dep/jemalloc/src/quarantine.c +++ b/dep/jemalloc/src/quarantine.c @@ -141,8 +141,17 @@ quarantine(void *ptr) obj->usize = usize; quarantine->curbytes += usize; quarantine->curobjs++; - if (opt_junk) - memset(ptr, 0x5a, usize); + if (config_fill && opt_junk) { + /* + * Only do redzone validation if Valgrind isn't in + * operation. + */ + if ((config_valgrind == false || opt_valgrind == false) + && usize <= SMALL_MAXCLASS) + arena_quarantine_junk_small(ptr, usize); + else + memset(ptr, 0x5a, usize); + } } else { assert(quarantine->curbytes == 0); idalloc(ptr); diff --git a/dep/jemalloc/src/rtree.c b/dep/jemalloc/src/rtree.c index 90c6935a0ed..205957ac4e1 100644 --- a/dep/jemalloc/src/rtree.c +++ b/dep/jemalloc/src/rtree.c @@ -2,42 +2,55 @@ #include "jemalloc/internal/jemalloc_internal.h" rtree_t * -rtree_new(unsigned bits) +rtree_new(unsigned bits, rtree_alloc_t *alloc, rtree_dalloc_t *dalloc) { rtree_t *ret; - unsigned bits_per_level, height, i; + unsigned bits_per_level, bits_in_leaf, height, i; + + assert(bits > 0 && bits <= (sizeof(uintptr_t) << 3)); bits_per_level = ffs(pow2_ceil((RTREE_NODESIZE / sizeof(void *)))) - 1; - height = bits / bits_per_level; - if (height * bits_per_level != bits) - height++; - assert(height * bits_per_level >= bits); + bits_in_leaf = ffs(pow2_ceil((RTREE_NODESIZE / sizeof(uint8_t)))) - 1; + if (bits > bits_in_leaf) { + height = 1 + (bits - bits_in_leaf) / bits_per_level; + if ((height-1) * bits_per_level + bits_in_leaf != bits) + height++; + } else { + height = 1; + } + assert((height-1) * bits_per_level + bits_in_leaf >= bits); - ret = (rtree_t*)base_alloc(offsetof(rtree_t, level2bits) + + ret = (rtree_t*)alloc(offsetof(rtree_t, level2bits) + (sizeof(unsigned) * height)); if (ret == NULL) return (NULL); memset(ret, 0, offsetof(rtree_t, level2bits) + (sizeof(unsigned) * height)); + ret->alloc = alloc; + ret->dalloc = dalloc; if (malloc_mutex_init(&ret->mutex)) { - /* Leak the rtree. */ + if (dalloc != NULL) + dalloc(ret); return (NULL); } ret->height = height; - if (bits_per_level * height > bits) - ret->level2bits[0] = bits % bits_per_level; - else - ret->level2bits[0] = bits_per_level; - for (i = 1; i < height; i++) - ret->level2bits[i] = bits_per_level; - - ret->root = (void**)base_alloc(sizeof(void *) << ret->level2bits[0]); + if (height > 1) { + if ((height-1) * bits_per_level + bits_in_leaf > bits) { + ret->level2bits[0] = (bits - bits_in_leaf) % + bits_per_level; + } else + ret->level2bits[0] = bits_per_level; + for (i = 1; i < height-1; i++) + ret->level2bits[i] = bits_per_level; + ret->level2bits[height-1] = bits_in_leaf; + } else + ret->level2bits[0] = bits; + + ret->root = (void**)alloc(sizeof(void *) << ret->level2bits[0]); if (ret->root == NULL) { - /* - * We leak the rtree here, since there's no generic base - * deallocation. - */ + if (dalloc != NULL) + dalloc(ret); return (NULL); } memset(ret->root, 0, sizeof(void *) << ret->level2bits[0]); @@ -45,6 +58,31 @@ rtree_new(unsigned bits) return (ret); } +static void +rtree_delete_subtree(rtree_t *rtree, void **node, unsigned level) +{ + + if (level < rtree->height - 1) { + size_t nchildren, i; + + nchildren = ZU(1) << rtree->level2bits[level]; + for (i = 0; i < nchildren; i++) { + void **child = (void **)node[i]; + if (child != NULL) + rtree_delete_subtree(rtree, child, level + 1); + } + } + rtree->dalloc(node); +} + +void +rtree_delete(rtree_t *rtree) +{ + + rtree_delete_subtree(rtree, rtree->root, 0); + rtree->dalloc(rtree); +} + void rtree_prefork(rtree_t *rtree) { diff --git a/dep/jemalloc/src/stats.c b/dep/jemalloc/src/stats.c index 43f87af6700..bef2ab33cd4 100644 --- a/dep/jemalloc/src/stats.c +++ b/dep/jemalloc/src/stats.c @@ -345,25 +345,25 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, malloc_cprintf(write_cb, cbopaque, "Assertions %s\n", bv ? "enabled" : "disabled"); -#define OPT_WRITE_BOOL(n) \ +#define OPT_WRITE_BOOL(n) \ if ((err = je_mallctl("opt."#n, &bv, &bsz, NULL, 0)) \ == 0) { \ malloc_cprintf(write_cb, cbopaque, \ " opt."#n": %s\n", bv ? "true" : "false"); \ } -#define OPT_WRITE_SIZE_T(n) \ +#define OPT_WRITE_SIZE_T(n) \ if ((err = je_mallctl("opt."#n, &sv, &ssz, NULL, 0)) \ == 0) { \ malloc_cprintf(write_cb, cbopaque, \ " opt."#n": %zu\n", sv); \ } -#define OPT_WRITE_SSIZE_T(n) \ +#define OPT_WRITE_SSIZE_T(n) \ if ((err = je_mallctl("opt."#n, &ssv, &sssz, NULL, 0)) \ == 0) { \ malloc_cprintf(write_cb, cbopaque, \ " opt."#n": %zd\n", ssv); \ } -#define OPT_WRITE_CHAR_P(n) \ +#define OPT_WRITE_CHAR_P(n) \ if ((err = je_mallctl("opt."#n, &cpv, &cpsz, NULL, 0)) \ == 0) { \ malloc_cprintf(write_cb, cbopaque, \ diff --git a/dep/jemalloc/src/tcache.c b/dep/jemalloc/src/tcache.c index 98ed19edd52..6de92960b2d 100644 --- a/dep/jemalloc/src/tcache.c +++ b/dep/jemalloc/src/tcache.c @@ -260,8 +260,8 @@ tcache_arena_dissociate(tcache_t *tcache) /* Unlink from list of extant tcaches. */ malloc_mutex_lock(&tcache->arena->lock); ql_remove(&tcache->arena->tcache_ql, tcache, link); - malloc_mutex_unlock(&tcache->arena->lock); tcache_stats_merge(tcache, tcache->arena); + malloc_mutex_unlock(&tcache->arena->lock); } } @@ -292,7 +292,7 @@ tcache_create(arena_t *arena) else if (size <= tcache_maxclass) tcache = (tcache_t *)arena_malloc_large(arena, size, true); else - tcache = (tcache_t *)icallocx(size, false, arena); + tcache = (tcache_t *)icalloct(size, false, arena); if (tcache == NULL) return (NULL); @@ -366,7 +366,7 @@ tcache_destroy(tcache_t *tcache) arena_dalloc_large(arena, chunk, tcache); } else - idallocx(tcache, false); + idalloct(tcache, false); } void @@ -399,11 +399,14 @@ tcache_thread_cleanup(void *arg) } } +/* Caller must own arena->lock. */ void tcache_stats_merge(tcache_t *tcache, arena_t *arena) { unsigned i; + cassert(config_stats); + /* Merge and reset tcache stats. */ for (i = 0; i < NBINS; i++) { arena_bin_t *bin = &arena->bins[i]; diff --git a/dep/jemalloc/src/tsd.c b/dep/jemalloc/src/tsd.c index 961a546329c..700caabfe47 100644 --- a/dep/jemalloc/src/tsd.c +++ b/dep/jemalloc/src/tsd.c @@ -21,7 +21,7 @@ void malloc_tsd_dalloc(void *wrapper) { - idalloc(wrapper); + idalloct(wrapper, false); } void @@ -105,3 +105,37 @@ JEMALLOC_SECTION(".CRT$XLY") JEMALLOC_ATTR(used) static const BOOL (WINAPI *tls_callback)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) = _tls_callback; #endif + +#if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \ + !defined(_WIN32)) +void * +tsd_init_check_recursion(tsd_init_head_t *head, tsd_init_block_t *block) +{ + pthread_t self = pthread_self(); + tsd_init_block_t *iter; + + /* Check whether this thread has already inserted into the list. */ + malloc_mutex_lock(&head->lock); + ql_foreach(iter, &head->blocks, link) { + if (iter->thread == self) { + malloc_mutex_unlock(&head->lock); + return (iter->data); + } + } + /* Insert block into list. */ + ql_elm_new(block, link); + block->thread = self; + ql_tail_insert(&head->blocks, block, link); + malloc_mutex_unlock(&head->lock); + return (NULL); +} + +void +tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block) +{ + + malloc_mutex_lock(&head->lock); + ql_remove(&head->blocks, block, link); + malloc_mutex_unlock(&head->lock); +} +#endif diff --git a/dep/jemalloc/src/util.c b/dep/jemalloc/src/util.c index b3a01143698..93a19fd16f7 100644 --- a/dep/jemalloc/src/util.c +++ b/dep/jemalloc/src/util.c @@ -77,7 +77,7 @@ malloc_write(const char *s) * provide a wrapper. */ int -buferror(char *buf, size_t buflen) +buferror(int err, char *buf, size_t buflen) { #ifdef _WIN32 @@ -85,34 +85,36 @@ buferror(char *buf, size_t buflen) (LPSTR)buf, buflen, NULL); return (0); #elif defined(_GNU_SOURCE) - char *b = strerror_r(errno, buf, buflen); + char *b = strerror_r(err, buf, buflen); if (b != buf) { strncpy(buf, b, buflen); buf[buflen-1] = '\0'; } return (0); #else - return (strerror_r(errno, buf, buflen)); + return (strerror_r(err, buf, buflen)); #endif } uintmax_t -malloc_strtoumax(const char *nptr, char **endptr, int base) +malloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base) { uintmax_t ret, digit; int b; bool neg; const char *p, *ns; + p = nptr; if (base < 0 || base == 1 || base > 36) { + ns = p; set_errno(EINVAL); - return (UINTMAX_MAX); + ret = UINTMAX_MAX; + goto label_return; } b = base; /* Swallow leading whitespace and get sign, if any. */ neg = false; - p = nptr; while (true) { switch (*p) { case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': @@ -146,7 +148,7 @@ malloc_strtoumax(const char *nptr, char **endptr, int base) if (b == 8) p++; break; - case 'x': + case 'X': case 'x': switch (p[2]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': @@ -164,7 +166,9 @@ malloc_strtoumax(const char *nptr, char **endptr, int base) } break; default: - break; + p++; + ret = 0; + goto label_return; } } if (b == 0) @@ -181,13 +185,22 @@ malloc_strtoumax(const char *nptr, char **endptr, int base) if (ret < pret) { /* Overflow. */ set_errno(ERANGE); - return (UINTMAX_MAX); + ret = UINTMAX_MAX; + goto label_return; } p++; } if (neg) ret = -ret; + if (p == ns) { + /* No conversion performed. */ + set_errno(EINVAL); + ret = UINTMAX_MAX; + goto label_return; + } + +label_return: if (endptr != NULL) { if (p == ns) { /* No characters were converted. */ @@ -195,7 +208,6 @@ malloc_strtoumax(const char *nptr, char **endptr, int base) } else *endptr = (char *)p; } - return (ret); } @@ -331,7 +343,7 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) APPEND_C(' '); \ } \ } while (0) -#define GET_ARG_NUMERIC(val, len) do { \ +#define GET_ARG_NUMERIC(val, len) do { \ switch (len) { \ case '?': \ val = va_arg(ap, int); \ @@ -354,6 +366,9 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) case 'j': \ val = va_arg(ap, intmax_t); \ break; \ + case 'j' | 0x80: \ + val = va_arg(ap, uintmax_t); \ + break; \ case 't': \ val = va_arg(ap, ptrdiff_t); \ break; \ @@ -385,11 +400,6 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) unsigned char len = '?'; f++; - if (*f == '%') { - /* %% */ - APPEND_C(*f); - break; - } /* Flags. */ while (true) { switch (*f) { @@ -419,6 +429,10 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) case '*': width = va_arg(ap, int); f++; + if (width < 0) { + left_justify = true; + width = -width; + } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { @@ -428,19 +442,16 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) assert(uwidth != UINTMAX_MAX || get_errno() != ERANGE); width = (int)uwidth; - if (*f == '.') { - f++; - goto label_precision; - } else - goto label_length; break; - } case '.': - f++; - goto label_precision; - default: goto label_length; + } default: + break; } + /* Width/precision separator. */ + if (*f == '.') + f++; + else + goto label_length; /* Precision. */ - label_precision: switch (*f) { case '*': prec = va_arg(ap, int); @@ -469,16 +480,8 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) } else len = 'l'; break; - case 'j': - len = 'j'; - f++; - break; - case 't': - len = 't'; - f++; - break; - case 'z': - len = 'z'; + case 'q': case 'j': case 't': case 'z': + len = *f; f++; break; default: break; @@ -487,6 +490,11 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) switch (*f) { char *s; size_t slen; + case '%': + /* %% */ + APPEND_C(*f); + f++; + break; case 'd': case 'i': { intmax_t val JEMALLOC_CC_SILENCE_INIT(0); char buf[D2S_BUFSIZE]; @@ -540,7 +548,7 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) assert(len == '?' || len == 'l'); assert_not_implemented(len != 'l'); s = va_arg(ap, char *); - slen = (prec == -1) ? strlen(s) : prec; + slen = (prec < 0) ? strlen(s) : prec; APPEND_PADDED_S(s, slen, width, left_justify); f++; break; @@ -553,8 +561,7 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) APPEND_PADDED_S(s, slen, width, left_justify); f++; break; - } - default: not_implemented(); + } default: not_reached(); } break; } default: { diff --git a/dep/jemalloc/src/zone.c b/dep/jemalloc/src/zone.c index c62c183f65e..e0302ef4edc 100644 --- a/dep/jemalloc/src/zone.c +++ b/dep/jemalloc/src/zone.c @@ -137,7 +137,7 @@ zone_destroy(malloc_zone_t *zone) { /* This function should never be called. */ - assert(false); + not_reached(); return (NULL); } diff --git a/dep/recastnavigation/Detour/CMakeLists.txt b/dep/recastnavigation/Detour/CMakeLists.txt index b7c0853efc4..5f3542e96b9 100644 --- a/dep/recastnavigation/Detour/CMakeLists.txt +++ b/dep/recastnavigation/Detour/CMakeLists.txt @@ -9,13 +9,14 @@ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. set(Detour_STAT_SRCS - DetourAlloc.cpp - DetourCommon.cpp - DetourNavMesh.cpp - DetourNavMeshBuilder.cpp - DetourNavMeshQuery.cpp - DetourNode.cpp + Source/DetourAlloc.cpp + Source/DetourCommon.cpp + Source/DetourNavMesh.cpp + Source/DetourNavMeshBuilder.cpp + Source/DetourNavMeshQuery.cpp + Source/DetourNode.cpp ) +include_directories(Include) if(WIN32) include_directories( diff --git a/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp b/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp index a255c9b3fd1..d3f90b7ab17 100644 --- a/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp +++ b/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp @@ -25,6 +25,7 @@ #include <float.h> #include <new> +static const float DT_PI = 3.14159265f; static int sweepCircleCircle(const float* c0, const float r0, const float* v, const float* c1, const float r1, @@ -206,12 +207,6 @@ void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr) dtObstacleAvoidanceQuery::dtObstacleAvoidanceQuery() : - m_velBias(0.0f), - m_weightDesVel(0.0f), - m_weightCurVel(0.0f), - m_weightSide(0.0f), - m_weightToi(0.0f), - m_horizTime(0.0f), m_maxCircles(0), m_circles(0), m_ncircles(0), @@ -318,11 +313,11 @@ void dtObstacleAvoidanceQuery::prepare(const float* pos, const float* dvel) float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs, const float* pos, const float rad, - const float vmax, const float* vel, const float* dvel, + const float* vel, const float* dvel, dtObstacleAvoidanceDebugData* debug) { // Find min time of impact and exit amongst all obstacles. - float tmin = m_horizTime; + float tmin = m_params.horizTime; float side = 0; int nside = 0; @@ -395,11 +390,10 @@ float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs if (nside) side /= nside; - const float ivmax = 1.0f / vmax; - const float vpen = m_weightDesVel * (dtVdist2D(vcand, dvel) * ivmax); - const float vcpen = m_weightCurVel * (dtVdist2D(vcand, vel) * ivmax); - const float spen = m_weightSide * side; - const float tpen = m_weightToi * (1.0f/(0.1f+tmin / m_horizTime)); + const float vpen = m_params.weightDesVel * (dtVdist2D(vcand, dvel) * m_invVmax); + const float vcpen = m_params.weightCurVel * (dtVdist2D(vcand, vel) * m_invVmax); + const float spen = m_params.weightSide * side; + const float tpen = m_params.weightToi * (1.0f/(0.1f+tmin*m_invHorizTime)); const float penalty = vpen + vcpen + spen + tpen; @@ -410,28 +404,34 @@ float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs return penalty; } -void dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float rad, const float vmax, - const float* vel, const float* dvel, - float* nvel, const int gsize, - dtObstacleAvoidanceDebugData* debug) +int dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float rad, const float vmax, + const float* vel, const float* dvel, float* nvel, + const dtObstacleAvoidanceParams* params, + dtObstacleAvoidanceDebugData* debug) { prepare(pos, dvel); + memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams)); + m_invHorizTime = 1.0f / m_params.horizTime; + m_vmax = vmax; + m_invVmax = 1.0f / vmax; + dtVset(nvel, 0,0,0); if (debug) debug->reset(); - const float cvx = dvel[0] * m_velBias; - const float cvz = dvel[2] * m_velBias; - const float cs = vmax * 2 * (1 - m_velBias) / (float)(gsize-1); - const float half = (gsize-1)*cs*0.5f; + const float cvx = dvel[0] * m_params.velBias; + const float cvz = dvel[2] * m_params.velBias; + const float cs = vmax * 2 * (1 - m_params.velBias) / (float)(m_params.gridSize-1); + const float half = (m_params.gridSize-1)*cs*0.5f; float minPenalty = FLT_MAX; + int ns = 0; - for (int y = 0; y < gsize; ++y) + for (int y = 0; y < m_params.gridSize; ++y) { - for (int x = 0; x < gsize; ++x) + for (int x = 0; x < m_params.gridSize; ++x) { float vcand[3]; vcand[0] = cvx + x*cs - half; @@ -440,7 +440,8 @@ void dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+cs/2)) continue; - const float penalty = processSample(vcand, cs, pos,rad,vmax,vel,dvel, debug); + const float penalty = processSample(vcand, cs, pos,rad,vel,dvel, debug); + ns++; if (penalty < minPenalty) { minPenalty = penalty; @@ -448,31 +449,38 @@ void dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float } } } + + return ns; } -static const float DT_PI = 3.14159265f; - -void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax, - const float* vel, const float* dvel, float* nvel, - const int ndivs, const int nrings, const int depth, - dtObstacleAvoidanceDebugData* debug) +int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax, + const float* vel, const float* dvel, float* nvel, + const dtObstacleAvoidanceParams* params, + dtObstacleAvoidanceDebugData* debug) { prepare(pos, dvel); + memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams)); + m_invHorizTime = 1.0f / m_params.horizTime; + m_vmax = vmax; + m_invVmax = 1.0f / vmax; + dtVset(nvel, 0,0,0); if (debug) debug->reset(); - + // Build sampling pattern aligned to desired velocity. - static const int MAX_PATTERN_DIVS = 32; - static const int MAX_PATTERN_RINGS = 4; - float pat[(MAX_PATTERN_DIVS*MAX_PATTERN_RINGS+1)*2]; + float pat[(DT_MAX_PATTERN_DIVS*DT_MAX_PATTERN_RINGS+1)*2]; int npat = 0; - const int nd = dtClamp(ndivs, 1, MAX_PATTERN_DIVS); - const int nr = dtClamp(nrings, 1, MAX_PATTERN_RINGS); + const int ndivs = (int)m_params.adaptiveDivs; + const int nrings= (int)m_params.adaptiveRings; + const int depth = (int)m_params.adaptiveDepth; + + const int nd = dtClamp(ndivs, 1, DT_MAX_PATTERN_DIVS); + const int nr = dtClamp(nrings, 1, DT_MAX_PATTERN_RINGS); const float da = (1.0f/nd) * DT_PI*2; const float dang = atan2f(dvel[2], dvel[0]); @@ -483,21 +491,22 @@ void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const fl for (int j = 0; j < nr; ++j) { - const float rad = (float)(nr-j)/(float)nr; + const float r = (float)(nr-j)/(float)nr; float a = dang + (j&1)*0.5f*da; for (int i = 0; i < nd; ++i) { - pat[npat*2+0] = cosf(a)*rad; - pat[npat*2+1] = sinf(a)*rad; + pat[npat*2+0] = cosf(a)*r; + pat[npat*2+1] = sinf(a)*r; npat++; a += da; } } // Start sampling. - float cr = vmax * (1.0f-m_velBias); + float cr = vmax * (1.0f - m_params.velBias); float res[3]; - dtVset(res, dvel[0] * m_velBias, 0, dvel[2] * m_velBias); + dtVset(res, dvel[0] * m_params.velBias, 0, dvel[2] * m_params.velBias); + int ns = 0; for (int k = 0; k < depth; ++k) { @@ -514,7 +523,8 @@ void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const fl if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+0.001f)) continue; - const float penalty = processSample(vcand,cr/10, pos,rad,vmax,vel,dvel, debug); + const float penalty = processSample(vcand,cr/10, pos,rad,vel,dvel, debug); + ns++; if (penalty < minPenalty) { minPenalty = penalty; @@ -528,5 +538,7 @@ void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const fl } dtVcopy(nvel, res); + + return ns; } diff --git a/dep/recastnavigation/Detour/DetourObstacleAvoidance.h b/dep/recastnavigation/Detour/DetourObstacleAvoidance.h index 4a7187a7998..8ff6211e867 100644 --- a/dep/recastnavigation/Detour/DetourObstacleAvoidance.h +++ b/dep/recastnavigation/Detour/DetourObstacleAvoidance.h @@ -21,21 +21,19 @@ struct dtObstacleCircle { - float p[3]; // Position of the obstacle - float vel[3]; // Velocity of the obstacle - float dvel[3]; // Velocity of the obstacle - float rad; // Radius of the obstacle - float dp[3], np[3]; // Use for side selection during sampling. + float p[3]; ///< Position of the obstacle + float vel[3]; ///< Velocity of the obstacle + float dvel[3]; ///< Velocity of the obstacle + float rad; ///< Radius of the obstacle + float dp[3], np[3]; ///< Use for side selection during sampling. }; struct dtObstacleSegment { - float p[3], q[3]; // End points of the obstacle segment + float p[3], q[3]; ///< End points of the obstacle segment bool touch; }; -static const int RVO_SAMPLE_RAD = 15; -static const int MAX_RVO_SAMPLES = (RVO_SAMPLE_RAD*2+1)*(RVO_SAMPLE_RAD*2+1) + 100; class dtObstacleAvoidanceDebugData { @@ -75,6 +73,23 @@ dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData(); void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr); +static const int DT_MAX_PATTERN_DIVS = 32; ///< Max numver of adaptive divs. +static const int DT_MAX_PATTERN_RINGS = 4; ///< Max number of adaptive rings. + +struct dtObstacleAvoidanceParams +{ + float velBias; + float weightDesVel; + float weightCurVel; + float weightSide; + float weightToi; + float horizTime; + unsigned char gridSize; ///< grid + unsigned char adaptiveDivs; ///< adaptive + unsigned char adaptiveRings; ///< adaptive + unsigned char adaptiveDepth; ///< adaptive +}; + class dtObstacleAvoidanceQuery { public: @@ -90,22 +105,15 @@ public: void addSegment(const float* p, const float* q); - inline void setVelocitySelectionBias(float v) { m_velBias = v; } - inline void setDesiredVelocityWeight(float w) { m_weightDesVel = w; } - inline void setCurrentVelocityWeight(float w) { m_weightCurVel = w; } - inline void setPreferredSideWeight(float w) { m_weightSide = w; } - inline void setCollisionTimeWeight(float w) { m_weightToi = w; } - inline void setTimeHorizon(float t) { m_horizTime = t; } - - void sampleVelocityGrid(const float* pos, const float rad, const float vmax, - const float* vel, const float* dvel, float* nvel, - const int gsize, - dtObstacleAvoidanceDebugData* debug = 0); - - void sampleVelocityAdaptive(const float* pos, const float rad, const float vmax, - const float* vel, const float* dvel, float* nvel, - const int ndivs, const int nrings, const int depth, - dtObstacleAvoidanceDebugData* debug = 0); + int sampleVelocityGrid(const float* pos, const float rad, const float vmax, + const float* vel, const float* dvel, float* nvel, + const dtObstacleAvoidanceParams* params, + dtObstacleAvoidanceDebugData* debug = 0); + + int sampleVelocityAdaptive(const float* pos, const float rad, const float vmax, + const float* vel, const float* dvel, float* nvel, + const dtObstacleAvoidanceParams* params, + dtObstacleAvoidanceDebugData* debug = 0); inline int getObstacleCircleCount() const { return m_ncircles; } const dtObstacleCircle* getObstacleCircle(const int i) { return &m_circles[i]; } @@ -119,19 +127,17 @@ private: float processSample(const float* vcand, const float cs, const float* pos, const float rad, - const float vmax, const float* vel, const float* dvel, + const float* vel, const float* dvel, dtObstacleAvoidanceDebugData* debug); dtObstacleCircle* insertCircle(const float dist); dtObstacleSegment* insertSegment(const float dist); - float m_velBias; - float m_weightDesVel; - float m_weightCurVel; - float m_weightSide; - float m_weightToi; - float m_horizTime; - + dtObstacleAvoidanceParams m_params; + float m_invHorizTime; + float m_vmax; + float m_invVmax; + int m_maxCircles; dtObstacleCircle* m_circles; int m_ncircles; @@ -145,4 +151,4 @@ dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery(); void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr); -#endif // DETOUROBSTACLEAVOIDANCE_H
\ No newline at end of file +#endif // DETOUROBSTACLEAVOIDANCE_H diff --git a/dep/recastnavigation/Detour/DetourAlloc.h b/dep/recastnavigation/Detour/Include/DetourAlloc.h index e814b62a716..e814b62a716 100644 --- a/dep/recastnavigation/Detour/DetourAlloc.h +++ b/dep/recastnavigation/Detour/Include/DetourAlloc.h diff --git a/dep/recastnavigation/Detour/DetourAssert.h b/dep/recastnavigation/Detour/Include/DetourAssert.h index 3cf652288fa..3cf652288fa 100644 --- a/dep/recastnavigation/Detour/DetourAssert.h +++ b/dep/recastnavigation/Detour/Include/DetourAssert.h diff --git a/dep/recastnavigation/Detour/DetourCommon.h b/dep/recastnavigation/Detour/Include/DetourCommon.h index ed7c5149db9..0888614ea9b 100644 --- a/dep/recastnavigation/Detour/DetourCommon.h +++ b/dep/recastnavigation/Detour/Include/DetourCommon.h @@ -32,6 +32,11 @@ feature to find minor members. /// @name General helper functions /// @{ +/// Used to ignore a function parameter. VS complains about unused parameters +/// and this silences the warning. +/// @param [in] _ Unused parameter +template<class T> void dtIgnoreUnused(const T&) { } + /// Swaps the values of the two parameters. /// @param[in,out] a Value A /// @param[in,out] b Value B diff --git a/dep/recastnavigation/Detour/DetourNavMesh.h b/dep/recastnavigation/Detour/Include/DetourNavMesh.h index 9c61a9bb649..782ddbc2edd 100644 --- a/dep/recastnavigation/Detour/DetourNavMesh.h +++ b/dep/recastnavigation/Detour/Include/DetourNavMesh.h @@ -44,10 +44,6 @@ typedef uint64_t uint64_d; // Edited by TC // We cannot have over 31 bits for either tile nor poly // without changing polyCount to use 64bits too. -static const int STATIC_SALT_BITS = 12; -static const int STATIC_TILE_BITS = 21; -static const int STATIC_POLY_BITS = 31; - /// A handle to a polygon within a navigation mesh tile. /// @ingroup detour typedef uint64_d dtPolyRef; // Edited by TC @@ -94,6 +90,12 @@ static const unsigned int DT_OFFMESH_CON_BIDIR = 1; /// @ingroup detour static const int DT_MAX_AREAS = 64; +static const int STATIC_SALT_BITS = 12; +static const int STATIC_TILE_BITS = 21; +static const int STATIC_POLY_BITS = 31; +// we cannot have over 31 bits for either tile nor poly +// without changing polyCount to use 64bits too. + /// Tile flags used for various functions and fields. /// For an example, see dtNavMesh::addTile(). enum dtTileFlags @@ -117,6 +119,25 @@ enum dtStraightPathOptions DT_STRAIGHTPATH_ALL_CROSSINGS = 0x02, ///< Add a vertex at every polygon edge crossing. }; + +/// Options for dtNavMeshQuery::findPath +enum dtFindPathOptions +{ + DT_FINDPATH_LOW_QUALITY_FAR = 0x01, ///< [provisional] trade quality for performance far from the origin. The idea is that by then a new query will be issued + DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs) +}; + +/// Options for dtNavMeshQuery::raycast +enum dtRaycastOptions +{ + DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost +}; + + +/// Limit raycasting during any angle pahfinding +/// The limit is given as a multiple of the character radius +static const float DT_RAY_CAST_LIMIT_PROPORTIONS = 50.0f; + /// Flags representing the type of a navigation mesh polygon. enum dtPolyTypes { @@ -585,8 +606,7 @@ private: dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents, float* nearestPt) const; /// Returns closest point on polygon. - void closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip, - const float* pos, float* closest) const; + void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const; dtNavMeshParams m_params; ///< Current initialization params. TODO: do not store this info twice. float m_orig[3]; ///< Origin of the tile (0,0) diff --git a/dep/recastnavigation/Detour/DetourNavMeshBuilder.h b/dep/recastnavigation/Detour/Include/DetourNavMeshBuilder.h index c80d1717630..c80d1717630 100644 --- a/dep/recastnavigation/Detour/DetourNavMeshBuilder.h +++ b/dep/recastnavigation/Detour/Include/DetourNavMeshBuilder.h diff --git a/dep/recastnavigation/Detour/DetourNavMeshQuery.h b/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h index d431bf177bd..c7b360dcdc6 100644 --- a/dep/recastnavigation/Detour/DetourNavMeshQuery.h +++ b/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h @@ -41,6 +41,10 @@ class dtQueryFilter public: dtQueryFilter(); +#ifdef DT_VIRTUAL_QUERYFILTER + virtual ~dtQueryFilter() { } +#endif + /// Returns true if the polygon can be visited. (I.e. Is traversable.) /// @param[in] ref The reference id of the polygon test. /// @param[in] tile The tile containing the polygon. @@ -115,6 +119,34 @@ public: }; + + +/// Provides information about raycast hit +/// filled by dtNavMeshQuery::raycast +/// @ingroup detour +struct dtRaycastHit +{ + /// The hit parameter. (FLT_MAX if no wall hit.) + float t; + + /// hitNormal The normal of the nearest wall hit. [(x, y, z)] + float hitNormal[3]; + + /// Pointer to an array of reference ids of the visited polygons. [opt] + dtPolyRef* path; + + /// The number of visited polygons. [opt] + int pathCount; + + /// The maximum number of polygons the @p path array can hold. + int maxPath; + + /// The cost of the path until hit. + float pathCost; +}; + + + /// Provides the ability to perform pathfinding related queries against /// a navigation mesh. /// @ingroup detour @@ -179,10 +211,11 @@ public: /// @param[in] startPos A position within the start polygon. [(x, y, z)] /// @param[in] endPos A position within the end polygon. [(x, y, z)] /// @param[in] filter The polygon filter to apply to the query. + /// @param[in] options query options (see: #dtFindPathOptions) /// @returns The status flags for the query. dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef, const float* startPos, const float* endPos, - const dtQueryFilter* filter); + const dtQueryFilter* filter, const unsigned int options = 0); /// Updates an in-progress sliced path query. /// @param[in] maxIter The maximum number of iterations to perform. @@ -200,8 +233,8 @@ public: /// Finalizes and returns the results of an incomplete sliced path query, returning the path to the furthest /// polygon on the existing path that was visited during the search. - /// @param[out] existing An array of polygon references for the existing path. - /// @param[out] existingSize The number of polygon in the @p existing array. + /// @param[in] existing An array of polygon references for the existing path. + /// @param[in] existingSize The number of polygon in the @p existing array. /// @param[out] path An ordered list of polygon references representing the path. (Start to end.) /// [(polyRef) * @p pathCount] /// @param[out] pathCount The number of polygons returned in the @p path array. @@ -308,6 +341,7 @@ public: /// Casts a 'walkability' ray along the surface of the navigation mesh from /// the start position toward the end position. + /// @note A wrapper around raycast(..., RaycastHit*). Retained for backward compatibility. /// @param[in] startRef The reference id of the start polygon. /// @param[in] startPos A position within the start polygon representing /// the start of the ray. [(x, y, z)] @@ -323,6 +357,22 @@ public: const dtQueryFilter* filter, float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const; + /// Casts a 'walkability' ray along the surface of the navigation mesh from + /// the start position toward the end position. + /// @param[in] startRef The reference id of the start polygon. + /// @param[in] startPos A position within the start polygon representing + /// the start of the ray. [(x, y, z)] + /// @param[in] endPos The position to cast the ray toward. [(x, y, z)] + /// @param[in] filter The polygon filter to apply to the query. + /// @param[in] flags govern how the raycast behaves. See dtRaycastOptions + /// @param[out] hit Pointer to a raycast hit structure which will be filled by the results. + /// @param[in] prevRef parent of start ref. Used during for cost calculation [opt] + /// @returns The status flags for the query. + dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos, + const dtQueryFilter* filter, const unsigned int options, + dtRaycastHit* hit, dtPolyRef prevRef = 0) const; + + /// Finds the distance from the specified position to the nearest polygon wall. /// @param[in] startRef The reference id of the polygon containing @p centerPos. /// @param[in] centerPos The center of the search circle. [(x, y, z)] @@ -378,8 +428,9 @@ public: /// @param[in] ref The reference id of the polygon. /// @param[in] pos The position to check. [(x, y, z)] /// @param[out] closest The closest point on the polygon. [(x, y, z)] + /// @param[out] posOverPoly True of the position is over the polygon. /// @returns The status flags for the query. - dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const; + dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const; /// Returns a point on the boundary closest to the source point if the source point is outside the /// polygon's xz-bounds. @@ -428,12 +479,7 @@ private: /// Queries polygons within a tile. int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, const dtQueryFilter* filter, dtPolyRef* polys, const int maxPolys) const; - /// Find nearest polygon within a tile. - dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents, - const dtQueryFilter* filter, float* nearestPt) const; - /// Returns closest point on polygon. - void closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* closest) const; - + /// Returns portal points between two polygons. dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right, unsigned char& fromType, unsigned char& toType) const; @@ -467,6 +513,8 @@ private: dtPolyRef startRef, endRef; float startPos[3], endPos[3]; const dtQueryFilter* filter; + unsigned int options; + float raycastLimitSqr; }; dtQueryData m_query; ///< Sliced query state. diff --git a/dep/recastnavigation/Detour/DetourNode.h b/dep/recastnavigation/Detour/Include/DetourNode.h index b68c922d038..6fefdc8e0f3 100644 --- a/dep/recastnavigation/Detour/DetourNode.h +++ b/dep/recastnavigation/Detour/Include/DetourNode.h @@ -25,6 +25,7 @@ enum dtNodeFlags { DT_NODE_OPEN = 0x01, DT_NODE_CLOSED = 0x02, + DT_NODE_PARENT_DETACHED = 0x04, // parent of the node is not adjacent. Found using raycast. }; typedef unsigned short dtNodeIndex; @@ -35,12 +36,17 @@ struct dtNode float pos[3]; ///< Position of the node. float cost; ///< Cost from previous node to current node. float total; ///< Cost up to the node. - unsigned int pidx : 30; ///< Index to parent node. - unsigned int flags : 2; ///< Node flags 0/open/closed. + unsigned int pidx : 24; ///< Index to parent node. + unsigned int state : 2; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE + unsigned int flags : 3; ///< Node flags. A combination of dtNodeFlags. dtPolyRef id; ///< Polygon ref the node corresponds to. }; +static const int DT_MAX_STATES_PER_NODE = 4; // number of extra states per node. See dtNode::state + + + class dtNodePool { public: @@ -48,8 +54,12 @@ public: ~dtNodePool(); inline void operator=(const dtNodePool&) {} void clear(); - dtNode* getNode(dtPolyRef id); - dtNode* findNode(dtPolyRef id); + + // Get a dtNode by ref and extra state information. If there is none then - allocate + // There can be more than one node for the same polyRef but with different extra state information + dtNode* getNode(dtPolyRef id, unsigned char state=0); + dtNode* findNode(dtPolyRef id, unsigned char state); + unsigned int findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes); inline unsigned int getNodeIdx(const dtNode* node) const { @@ -82,6 +92,7 @@ public: inline int getHashSize() const { return m_hashSize; } inline dtNodeIndex getFirst(int bucket) const { return m_first[bucket]; } inline dtNodeIndex getNext(int i) const { return m_next[i]; } + inline int getNodeCount() const { return m_nodeCount; } private: diff --git a/dep/recastnavigation/Detour/DetourStatus.h b/dep/recastnavigation/Detour/Include/DetourStatus.h index af822c4a92d..af822c4a92d 100644 --- a/dep/recastnavigation/Detour/DetourStatus.h +++ b/dep/recastnavigation/Detour/Include/DetourStatus.h diff --git a/dep/recastnavigation/Detour/DetourAlloc.cpp b/dep/recastnavigation/Detour/Source/DetourAlloc.cpp index 5f671df5bdb..5f671df5bdb 100644 --- a/dep/recastnavigation/Detour/DetourAlloc.cpp +++ b/dep/recastnavigation/Detour/Source/DetourAlloc.cpp diff --git a/dep/recastnavigation/Detour/DetourCommon.cpp b/dep/recastnavigation/Detour/Source/DetourCommon.cpp index b5700f5930b..b5700f5930b 100644 --- a/dep/recastnavigation/Detour/DetourCommon.cpp +++ b/dep/recastnavigation/Detour/Source/DetourCommon.cpp diff --git a/dep/recastnavigation/Detour/DetourNavMesh.cpp b/dep/recastnavigation/Detour/Source/DetourNavMesh.cpp index 6b8e2d9d649..51740509950 100644 --- a/dep/recastnavigation/Detour/DetourNavMesh.cpp +++ b/dep/recastnavigation/Detour/Source/DetourNavMesh.cpp @@ -609,10 +609,12 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile) } } -void dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip, - const float* pos, float* closest) const +void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const { - const dtPoly* poly = &tile->polys[ip]; + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + getTileAndPolyByRefUnsafe(ref, &tile, &poly); + // Off-mesh connections don't have detail polygons. if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) { @@ -622,11 +624,14 @@ void dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip const float d1 = dtVdist(pos, v1); const float u = d0 / (d0+d1); dtVlerp(closest, v0, v1, u); + if (posOverPoly) + *posOverPoly = false; return; } + const unsigned int ip = (unsigned int)(poly - tile->polys); const dtPolyDetail* pd = &tile->detailMeshes[ip]; - + // Clamp point to be inside the polygon. float verts[DT_VERTS_PER_POLYGON*3]; float edged[DT_VERTS_PER_POLYGON]; @@ -652,6 +657,14 @@ void dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip const float* va = &verts[imin*3]; const float* vb = &verts[((imin+1)%nv)*3]; dtVlerp(closest, va, vb, edget[imin]); + + if (posOverPoly) + *posOverPoly = false; + } + else + { + if (posOverPoly) + *posOverPoly = true; } // Find height at the location. @@ -694,12 +707,27 @@ dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile, { dtPolyRef ref = polys[i]; float closestPtPoly[3]; - closestPointOnPolyInTile(tile, decodePolyIdPoly(ref), center, closestPtPoly); - float d = dtVdistSqr(center, closestPtPoly); + float diff[3]; + bool posOverPoly = false; + float d = 0; + closestPointOnPoly(ref, center, closestPtPoly, &posOverPoly); + + // If a point is directly over a polygon and closer than + // climb height, favor that instead of straight line nearest point. + dtVsub(diff, center, closestPtPoly); + if (posOverPoly) + { + d = dtAbs(diff[1]) - tile->header->walkableClimb; + d = d > 0 ? d*d : 0; + } + else + { + d = dtVlenSqr(diff); + } + if (d < nearestDistanceSqr) { - if (nearestPt) - dtVcopy(nearestPt, closestPtPoly); + dtVcopy(nearestPt, closestPtPoly); nearestDistanceSqr = d; nearest = ref; } diff --git a/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp b/dep/recastnavigation/Detour/Source/DetourNavMeshBuilder.cpp index 9d8471b96a1..9d8471b96a1 100644 --- a/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp +++ b/dep/recastnavigation/Detour/Source/DetourNavMeshBuilder.cpp diff --git a/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp b/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp index e6557cf707e..ec3a2946ea5 100644 --- a/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp +++ b/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp @@ -502,7 +502,7 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f /// /// See closestPointOnPolyBoundary() for a limited but faster option. /// -dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const +dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const { dtAssert(m_nav); const dtMeshTile* tile = 0; @@ -511,19 +511,7 @@ dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, flo return DT_FAILURE | DT_INVALID_PARAM; if (!tile) return DT_FAILURE | DT_INVALID_PARAM; - - // Edited by TC - if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) - return DT_FAILURE; - - closestPointOnPolyInTile(tile, poly, pos, closest); - return DT_SUCCESS; -} - -void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly, - const float* pos, float* closest) const -{ // Off-mesh connections don't have detail polygons. if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) { @@ -533,7 +521,9 @@ void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPo const float d1 = dtVdist(pos, v1); const float u = d0 / (d0+d1); dtVlerp(closest, v0, v1, u); - return; + if (posOverPoly) + *posOverPoly = false; + return DT_SUCCESS; } const unsigned int ip = (unsigned int)(poly - tile->polys); @@ -564,6 +554,14 @@ void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPo const float* va = &verts[imin*3]; const float* vb = &verts[((imin+1)%nv)*3]; dtVlerp(closest, va, vb, edget[imin]); + + if (posOverPoly) + *posOverPoly = false; + } + else + { + if (posOverPoly) + *posOverPoly = true; } // Find height at the location. @@ -585,30 +583,8 @@ void dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPo break; } } - -/* float closestDistSqr = FLT_MAX; - for (int j = 0; j < pd->triCount; ++j) - { - const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4]; - const float* v[3]; - for (int k = 0; k < 3; ++k) - { - if (t[k] < poly->vertCount) - v[k] = &tile->verts[poly->verts[t[k]]*3]; - else - v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3]; - } - - float pt[3]; - dtClosestPtPointTriangle(pt, pos, v[0], v[1], v[2]); - float d = dtVdistSqr(pos, pt); - - if (d < closestDistSqr) - { - dtVcopy(closest, pt); - closestDistSqr = d; - } - }*/ + + return DT_SUCCESS; } /// @par @@ -687,8 +663,8 @@ dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* h { const float* v0 = &tile->verts[poly->verts[0]*3]; const float* v1 = &tile->verts[poly->verts[1]*3]; - const float d0 = dtVdist(pos, v0); - const float d1 = dtVdist(pos, v1); + const float d0 = dtVdist2D(pos, v0); + const float d1 = dtVdist2D(pos, v1); const float u = d0 / (d0+d1); if (height) *height = v0[1] + (v1[1] - v0[1]) * u; @@ -752,8 +728,27 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten { dtPolyRef ref = polys[i]; float closestPtPoly[3]; - closestPointOnPoly(ref, center, closestPtPoly); - float d = dtVdistSqr(center, closestPtPoly); + float diff[3]; + bool posOverPoly = false; + float d = 0; + closestPointOnPoly(ref, center, closestPtPoly, &posOverPoly); + + // If a point is directly over a polygon and closer than + // climb height, favor that instead of straight line nearest point. + dtVsub(diff, center, closestPtPoly); + if (posOverPoly) + { + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + m_nav->getTileAndPolyByRefUnsafe(polys[i], &tile, &poly); + d = dtAbs(diff[1]) - tile->header->walkableClimb; + d = d > 0 ? d*d : 0; + } + else + { + d = dtVlenSqr(diff); + } + if (d < nearestDistanceSqr) { if (nearestPt) @@ -769,42 +764,6 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten return DT_SUCCESS; } -dtPolyRef dtNavMeshQuery::findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents, - const dtQueryFilter* filter, float* nearestPt) const -{ - dtAssert(m_nav); - - float bmin[3], bmax[3]; - dtVsub(bmin, center, extents); - dtVadd(bmax, center, extents); - - // Get nearby polygons from proximity grid. - dtPolyRef polys[128]; - int polyCount = queryPolygonsInTile(tile, bmin, bmax, filter, polys, 128); - - // Find nearest polygon amongst the nearby polygons. - dtPolyRef nearest = 0; - float nearestDistanceSqr = FLT_MAX; - for (int i = 0; i < polyCount; ++i) - { - dtPolyRef ref = polys[i]; - const dtPoly* poly = &tile->polys[m_nav->decodePolyIdPoly(ref)]; - float closestPtPoly[3]; - closestPointOnPolyInTile(tile, poly, center, closestPtPoly); - - float d = dtVdistSqr(center, closestPtPoly); - if (d < nearestDistanceSqr) - { - if (nearestPt) - dtVcopy(nearestPt, closestPtPoly); - nearestDistanceSqr = d; - nearest = ref; - } - } - - return nearest; -} - int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, const dtQueryFilter* filter, dtPolyRef* polys, const int maxPolys) const @@ -1052,7 +1011,13 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef, if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) continue; - dtNode* neighbourNode = m_nodePool->getNode(neighbourRef); + // deal explicitly with crossing tile boundaries + unsigned char crossSide = 0; + if (bestTile->links[i].side != 0xff) + crossSide = bestTile->links[i].side >> 1; + + // get the node + dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide); if (!neighbourNode) { status |= DT_OUT_OF_NODES; @@ -1180,7 +1145,7 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef, /// dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef, const float* startPos, const float* endPos, - const dtQueryFilter* filter) + const dtQueryFilter* filter, const unsigned int options) { dtAssert(m_nav); dtAssert(m_nodePool); @@ -1194,6 +1159,8 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef dtVcopy(m_query.startPos, startPos); dtVcopy(m_query.endPos, endPos); m_query.filter = filter; + m_query.options = options; + m_query.raycastLimitSqr = FLT_MAX; if (!startRef || !endRef) return DT_FAILURE | DT_INVALID_PARAM; @@ -1202,6 +1169,16 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef)) return DT_FAILURE | DT_INVALID_PARAM; + // trade quality with performance? + if (options & DT_FINDPATH_ANY_ANGLE) + { + // limiting to several times the character radius yields nice results. It is not sensitive + // so it is enough to compute it from the first tile. + const dtMeshTile* tile = m_nav->getTileByRef(startRef); + float agentRadius = tile->header->walkableRadius; + m_query.raycastLimitSqr = dtSqr(agentRadius * DT_RAY_CAST_LIMIT_PROPORTIONS); + } + if (startRef == endRef) { m_query.status = DT_SUCCESS; @@ -1238,6 +1215,9 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) m_query.status = DT_FAILURE; return DT_FAILURE; } + + dtRaycastHit rayHit; + rayHit.maxPath = 0; int iter = 0; while (iter < maxIter && !m_openList->empty()) @@ -1274,15 +1254,22 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) return m_query.status; } - // Get parent poly and tile. - dtPolyRef parentRef = 0; + // Get parent and grand parent poly and tile. + dtPolyRef parentRef = 0, grandpaRef = 0; const dtMeshTile* parentTile = 0; const dtPoly* parentPoly = 0; + dtNode* parentNode = 0; if (bestNode->pidx) - parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id; + { + parentNode = m_nodePool->getNodeAtIdx(bestNode->pidx); + parentRef = parentNode->id; + if (parentNode->pidx) + grandpaRef = m_nodePool->getNodeAtIdx(parentNode->pidx)->id; + } if (parentRef) { - if (dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly))) + bool invalidParent = dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly)); + if (invalidParent || (grandpaRef && !m_nav->isValidPolyRef(grandpaRef)) ) { // The polygon has disappeared during the sliced query, fail. m_query.status = DT_FAILURE; @@ -1291,6 +1278,14 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) return m_query.status; } } + + // decide whether to test raycast to previous nodes + bool tryLOS = false; + if (m_query.options & DT_FINDPATH_ANY_ANGLE) + { + if ((parentRef != 0) && (dtVdistSqr(parentNode->pos, bestNode->pos) < m_query.raycastLimitSqr)) + tryLOS = true; + } for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next) { @@ -1309,13 +1304,22 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) if (!m_query.filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) continue; - dtNode* neighbourNode = m_nodePool->getNode(neighbourRef); + // deal explicitly with crossing tile boundaries + unsigned char crossSide = 0; + if (bestTile->links[i].side != 0xff) + crossSide = bestTile->links[i].side >> 1; + + dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide); if (!neighbourNode) { m_query.status |= DT_OUT_OF_NODES; continue; } + // do not expand to nodes that were already visited from the same parent + if (neighbourNode->pidx != 0 && neighbourNode->pidx == bestNode->pidx) + continue; + // If the node is visited the first time, calculate node position. if (neighbourNode->flags == 0) { @@ -1328,30 +1332,44 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) float cost = 0; float heuristic = 0; - // Special case for last node. - if (neighbourRef == m_query.endRef) + // raycast parent + bool foundShortCut = false; + rayHit.pathCost = rayHit.t = 0; + if (tryLOS) { - // Cost + raycast(parentRef, parentNode->pos, neighbourNode->pos, m_query.filter, DT_RAYCAST_USE_COSTS, &rayHit, grandpaRef); + foundShortCut = rayHit.t >= 1.0f; + } + + // update move cost + if (foundShortCut) + { + // shortcut found using raycast. Using shorter cost instead + cost = parentNode->cost + rayHit.pathCost; + } + else + { + // No shortcut found. const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos, parentRef, parentTile, parentPoly, - bestRef, bestTile, bestPoly, - neighbourRef, neighbourTile, neighbourPoly); + bestRef, bestTile, bestPoly, + neighbourRef, neighbourTile, neighbourPoly); + cost = bestNode->cost + curCost; + } + + // Special case for last node. + if (neighbourRef == m_query.endRef) + { const float endCost = m_query.filter->getCost(neighbourNode->pos, m_query.endPos, bestRef, bestTile, bestPoly, neighbourRef, neighbourTile, neighbourPoly, 0, 0, 0); - cost = bestNode->cost + curCost + endCost; + cost = cost + endCost; heuristic = 0; } else { - // Cost - const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos, - parentRef, parentTile, parentPoly, - bestRef, bestTile, bestPoly, - neighbourRef, neighbourTile, neighbourPoly); - cost = bestNode->cost + curCost; heuristic = dtVdist(neighbourNode->pos, m_query.endPos)*H_SCALE; } @@ -1365,11 +1383,13 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) continue; // Add or update the node. - neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode); + neighbourNode->pidx = foundShortCut ? bestNode->pidx : m_nodePool->getNodeIdx(bestNode); neighbourNode->id = neighbourRef; - neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED); + neighbourNode->flags = (neighbourNode->flags & ~(DT_NODE_CLOSED | DT_NODE_PARENT_DETACHED)); neighbourNode->cost = cost; neighbourNode->total = total; + if (foundShortCut) + neighbourNode->flags = (neighbourNode->flags | DT_NODE_PARENT_DETACHED); if (neighbourNode->flags & DT_NODE_OPEN) { @@ -1433,11 +1453,15 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, dtNode* prev = 0; dtNode* node = m_query.lastBestNode; + int prevRay = 0; do { dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); node->pidx = m_nodePool->getNodeIdx(prev); prev = node; + int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut) + node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node + prevRay = nextRay; node = next; } while (node); @@ -1446,13 +1470,31 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, node = prev; do { - path[n++] = node->id; - if (n >= maxPath) + dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); + dtStatus status = 0; + if (node->flags & DT_NODE_PARENT_DETACHED) + { + float t, normal[3]; + int m; + status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n); + n += m; + // raycast ends on poly boundary and the path might include the next poly boundary. + if (path[n-1] == next->id) + n--; // remove to avoid duplicates + } + else + { + path[n++] = node->id; + if (n >= maxPath) + status = DT_BUFFER_TOO_SMALL; + } + + if (status & DT_STATUS_DETAIL_MASK) { - m_query.status |= DT_BUFFER_TOO_SMALL; + m_query.status |= status & DT_STATUS_DETAIL_MASK; break; } - node = m_nodePool->getNodeAtIdx(node->pidx); + node = next; } while (node); } @@ -1498,7 +1540,7 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing dtNode* node = 0; for (int i = existingSize-1; i >= 0; --i) { - node = m_nodePool->findNode(existing[i]); + m_nodePool->findNodes(existing[i], &node, 1); if (node) break; } @@ -1511,11 +1553,15 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing } // Reverse the path. + int prevRay = 0; do { dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); node->pidx = m_nodePool->getNodeIdx(prev); prev = node; + int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut) + node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node + prevRay = nextRay; node = next; } while (node); @@ -1524,13 +1570,31 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing node = prev; do { - path[n++] = node->id; - if (n >= maxPath) + dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); + dtStatus status = 0; + if (node->flags & DT_NODE_PARENT_DETACHED) { - m_query.status |= DT_BUFFER_TOO_SMALL; + float t, normal[3]; + int m; + status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n); + n += m; + // raycast ends on poly boundary and the path might include the next poly boundary. + if (path[n-1] == next->id) + n--; // remove to avoid duplicates + } + else + { + path[n++] = node->id; + if (n >= maxPath) + status = DT_BUFFER_TOO_SMALL; + } + + if (status & DT_STATUS_DETAIL_MASK) + { + m_query.status |= status & DT_STATUS_DETAIL_MASK; break; } - node = m_nodePool->getNodeAtIdx(node->pidx); + node = next; } while (node); } @@ -2202,6 +2266,8 @@ dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, return DT_SUCCESS; } + + /// @par /// /// This method is meant to be used for quick, short distance checks. @@ -2244,73 +2310,144 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons const dtQueryFilter* filter, float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const { - dtAssert(m_nav); + dtRaycastHit hit; + hit.path = path; + hit.maxPath = maxPath; + + dtStatus status = raycast(startRef, startPos, endPos, filter, 0, &hit); - *t = 0; + *t = hit.t; + if (hitNormal) + dtVcopy(hitNormal, hit.hitNormal); if (pathCount) - *pathCount = 0; + *pathCount = hit.pathCount; + + return status; +} + + +/// @par +/// +/// This method is meant to be used for quick, short distance checks. +/// +/// If the path array is too small to hold the result, it will be filled as +/// far as possible from the start postion toward the end position. +/// +/// <b>Using the Hit Parameter t of RaycastHit</b> +/// +/// If the hit parameter is a very high value (FLT_MAX), then the ray has hit +/// the end position. In this case the path represents a valid corridor to the +/// end position and the value of @p hitNormal is undefined. +/// +/// If the hit parameter is zero, then the start position is on the wall that +/// was hit and the value of @p hitNormal is undefined. +/// +/// If 0 < t < 1.0 then the following applies: +/// +/// @code +/// distanceToHitBorder = distanceToEndPosition * t +/// hitPoint = startPos + (endPos - startPos) * t +/// @endcode +/// +/// <b>Use Case Restriction</b> +/// +/// The raycast ignores the y-value of the end position. (2D check.) This +/// places significant limits on how it can be used. For example: +/// +/// Consider a scene where there is a main floor with a second floor balcony +/// that hangs over the main floor. So the first floor mesh extends below the +/// balcony mesh. The start position is somewhere on the first floor. The end +/// position is on the balcony. +/// +/// The raycast will search toward the end position along the first floor mesh. +/// If it reaches the end position's xz-coordinates it will indicate FLT_MAX +/// (no wall hit), meaning it reached the end position. This is one example of why +/// this method is meant for short distance checks. +/// +dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos, + const dtQueryFilter* filter, const unsigned int options, + dtRaycastHit* hit, dtPolyRef prevRef) const +{ + dtAssert(m_nav); + hit->t = 0; + hit->pathCount = 0; + hit->pathCost = 0; + // Validate input if (!startRef || !m_nav->isValidPolyRef(startRef)) return DT_FAILURE | DT_INVALID_PARAM; + if (prevRef && !m_nav->isValidPolyRef(prevRef)) + return DT_FAILURE | DT_INVALID_PARAM; - dtPolyRef curRef = startRef; - float verts[DT_VERTS_PER_POLYGON*3]; + float dir[3], curPos[3], lastPos[3]; + float verts[DT_VERTS_PER_POLYGON*3+3]; int n = 0; - - hitNormal[0] = 0; - hitNormal[1] = 0; - hitNormal[2] = 0; - + + dtVcopy(curPos, startPos); + dtVsub(dir, endPos, startPos); + dtVset(hit->hitNormal, 0, 0, 0); + dtStatus status = DT_SUCCESS; - + + const dtMeshTile* prevTile, *tile, *nextTile; + const dtPoly* prevPoly, *poly, *nextPoly; + dtPolyRef curRef, nextRef; + + // The API input has been checked already, skip checking internal data. + nextRef = curRef = startRef; + tile = 0; + poly = 0; + m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly); + nextTile = prevTile = tile; + nextPoly = prevPoly = poly; + if (prevRef) + m_nav->getTileAndPolyByRefUnsafe(prevRef, &prevTile, &prevPoly); + while (curRef) { // Cast ray against current polygon. - // The API input has been cheked already, skip checking internal data. - const dtMeshTile* tile = 0; - const dtPoly* poly = 0; - m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly); - // Collect vertices. int nv = 0; for (int i = 0; i < (int)poly->vertCount; ++i) { dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]); nv++; - } + } float tmin, tmax; int segMin, segMax; if (!dtIntersectSegmentPoly2D(startPos, endPos, verts, nv, tmin, tmax, segMin, segMax)) { // Could not hit the polygon, keep the old t and report hit. - if (pathCount) - *pathCount = n; + hit->pathCount = n; return status; } // Keep track of furthest t so far. - if (tmax > *t) - *t = tmax; + if (tmax > hit->t) + hit->t = tmax; // Store visited polygons. - if (n < maxPath) - path[n++] = curRef; + if (n < hit->maxPath) + hit->path[n++] = curRef; else status |= DT_BUFFER_TOO_SMALL; - + // Ray end is completely inside the polygon. if (segMax == -1) { - *t = FLT_MAX; - if (pathCount) - *pathCount = n; + hit->t = FLT_MAX; + hit->pathCount = n; + + // add the cost + if (options & DT_RAYCAST_USE_COSTS) + hit->pathCost += filter->getCost(curPos, endPos, prevRef, prevTile, prevPoly, curRef, tile, poly, curRef, tile, poly); return status; } - + // Follow neighbours. - dtPolyRef nextRef = 0; + nextRef = 0; for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next) { @@ -2321,8 +2458,8 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons continue; // Get pointer to the next polygon. - const dtMeshTile* nextTile = 0; - const dtPoly* nextPoly = 0; + nextTile = 0; + nextPoly = 0; m_nav->getTileAndPolyByRefUnsafe(link->ref, &nextTile, &nextPoly); // Skip off-mesh connections. @@ -2390,6 +2527,24 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons } } + // add the cost + if (options & DT_RAYCAST_USE_COSTS) + { + // compute the intersection point at the furthest end of the polygon + // and correct the height (since the raycast moves in 2d) + dtVcopy(lastPos, curPos); + dtVmad(curPos, startPos, dir, hit->t); + float* e1 = &verts[segMax*3]; + float* e2 = &verts[((segMax+1)%nv)*3]; + float eDir[3], diff[3]; + dtVsub(eDir, e2, e1); + dtVsub(diff, curPos, e1); + float s = dtSqr(eDir[0]) > dtSqr(eDir[2]) ? diff[0] / eDir[0] : diff[2] / eDir[2]; + curPos[1] = e1[1] + eDir[1] * s; + + hit->pathCost += filter->getCost(lastPos, curPos, prevRef, prevTile, prevPoly, curRef, tile, poly, nextRef, nextTile, nextPoly); + } + if (!nextRef) { // No neighbour, we hit a wall. @@ -2401,22 +2556,25 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons const float* vb = &verts[b*3]; const float dx = vb[0] - va[0]; const float dz = vb[2] - va[2]; - hitNormal[0] = dz; - hitNormal[1] = 0; - hitNormal[2] = -dx; - dtVnormalize(hitNormal); + hit->hitNormal[0] = dz; + hit->hitNormal[1] = 0; + hit->hitNormal[2] = -dx; + dtVnormalize(hit->hitNormal); - if (pathCount) - *pathCount = n; + hit->pathCount = n; return status; } - + // No hit, advance to neighbour polygon. + prevRef = curRef; curRef = nextRef; + prevTile = tile; + tile = nextTile; + prevPoly = poly; + poly = nextPoly; } - if (pathCount) - *pathCount = n; + hit->pathCount = n; return status; } @@ -3347,7 +3505,7 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen dtVsub(hitNormal, centerPos, hitPos); dtVnormalize(hitNormal); - *hitDist = dtSqrt(radiusSqr); + *hitDist = sqrtf(radiusSqr); return status; } @@ -3374,6 +3532,15 @@ bool dtNavMeshQuery::isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter) bool dtNavMeshQuery::isInClosedList(dtPolyRef ref) const { if (!m_nodePool) return false; - const dtNode* node = m_nodePool->findNode(ref); - return node && node->flags & DT_NODE_CLOSED; + + dtNode* nodes[DT_MAX_STATES_PER_NODE]; + int n= m_nodePool->findNodes(ref, nodes, DT_MAX_STATES_PER_NODE); + + for (int i=0; i<n; i++) + { + if (nodes[i]->flags & DT_NODE_CLOSED) + return true; + } + + return false; } diff --git a/dep/recastnavigation/Detour/DetourNode.cpp b/dep/recastnavigation/Detour/Source/DetourNode.cpp index 4c8215e20d0..1d1897708f4 100644 --- a/dep/recastnavigation/Detour/DetourNode.cpp +++ b/dep/recastnavigation/Detour/Source/DetourNode.cpp @@ -71,27 +71,46 @@ void dtNodePool::clear() m_nodeCount = 0; } -dtNode* dtNodePool::findNode(dtPolyRef id) +unsigned int dtNodePool::findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes) { + int n = 0; unsigned int bucket = dtHashRef(id) & (m_hashSize-1); dtNodeIndex i = m_first[bucket]; while (i != DT_NULL_IDX) { if (m_nodes[i].id == id) + { + if (n >= maxNodes) + return n; + nodes[n++] = &m_nodes[i]; + } + i = m_next[i]; + } + + return n; +} + +dtNode* dtNodePool::findNode(dtPolyRef id, unsigned char state) +{ + unsigned int bucket = dtHashRef(id) & (m_hashSize-1); + dtNodeIndex i = m_first[bucket]; + while (i != DT_NULL_IDX) + { + if (m_nodes[i].id == id && m_nodes[i].state == state) return &m_nodes[i]; i = m_next[i]; } return 0; } -dtNode* dtNodePool::getNode(dtPolyRef id) +dtNode* dtNodePool::getNode(dtPolyRef id, unsigned char state) { unsigned int bucket = dtHashRef(id) & (m_hashSize-1); dtNodeIndex i = m_first[bucket]; dtNode* node = 0; while (i != DT_NULL_IDX) { - if (m_nodes[i].id == id) + if (m_nodes[i].id == id && m_nodes[i].state == state) return &m_nodes[i]; i = m_next[i]; } @@ -108,6 +127,7 @@ dtNode* dtNodePool::getNode(dtPolyRef id) node->cost = 0; node->total = 0; node->id = id; + node->state = state; node->flags = 0; m_next[i] = m_first[bucket]; diff --git a/dep/recastnavigation/Readme.txt b/dep/recastnavigation/Readme.txt index 0c2f7b1675f..1383b01d582 100644 --- a/dep/recastnavigation/Readme.txt +++ b/dep/recastnavigation/Readme.txt @@ -32,9 +32,6 @@ the regions as simple polygons. The toolset code is located in the Recast folder and demo application using the Recast toolset is located in the RecastDemo folder. -The project files with this distribution can be compiled with Microsoft Visual C++ 2008 -(you can download it for free) and XCode 3.1. - Detour @@ -43,78 +40,7 @@ Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. Y Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes. -Latest code available at http://code.google.com/p/recastnavigation/ - - --- - -Release Notes - ----------------- -* Recast 1.4 - Released August 24th, 2009 - -- Added detail height mesh generation (RecastDetailMesh.cpp) for single, - tiled statmeshes as well as tilemesh. -- Added feature to contour tracing which detects extra vertices along - tile edges which should be removed later. -- Changed the tiled stat mesh preprocess, so that it first generated - polymeshes per tile and finally combines them. -- Fixed bug in the GUI code where invisible buttons could be pressed. - ----------------- -* Recast 1.31 - Released July 24th, 2009 - -- Better cost and heuristic functions. -- Fixed tile navmesh raycast on tile borders. - ----------------- -* Recast 1.3 - Released July 14th, 2009 - -- Added dtTileNavMesh which allows to dynamically add and remove navmesh pieces at runtime. -- Renamed stat navmesh types to dtStat* (i.e. dtPoly is now dtStatPoly). -- Moved common code used by tile and stat navmesh to DetourNode.h/cpp and DetourCommon.h/cpp. -- Refactores the demo code. - ----------------- -* Recast 1.2 - Released June 17th, 2009 - -- Added tiled mesh generation. The tiled generation allows to generate navigation for - much larger worlds, it removes some of the artifacts that comes from distance fields - in open areas, and allows later streaming and dynamic runtime generation -- Improved and added some debug draw modes -- API change: The helper function rcBuildNavMesh does not exists anymore, - had to change few internal things to cope with the tiled processing, - similar API functionality will be added later once the tiled process matures -- The demo is getting way too complicated, need to split demos -- Fixed several filtering functions so that the mesh is tighter to the geometry, - sometimes there could be up error up to tow voxel units close to walls, - now it should be just one. - ----------------- -* Recast 1.1 - Released April 11th, 2009 - -This is the first release of Detour. - ----------------- -* Recast 1.0 - Released March 29th, 2009 - -This is the first release of Recast. - -The process is not always as robust as I would wish. The watershed phase sometimes swallows tiny islands -which are close to edges. These droppings are handled in rcBuildContours, but the code is not -particularly robust either. - -Another non-robust case is when portal contours (contours shared between two regions) are always -assumed to be straight. That can lead to overlapping contours specially when the level has -large open areas. - - +Latest code available at https://github.com/memononen/recastnavigation Mikko Mononen memon@inside.org diff --git a/dep/recastnavigation/Recast/CMakeLists.txt b/dep/recastnavigation/Recast/CMakeLists.txt index 09f20b4ed2f..f4869bf8773 100644 --- a/dep/recastnavigation/Recast/CMakeLists.txt +++ b/dep/recastnavigation/Recast/CMakeLists.txt @@ -9,18 +9,20 @@ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. set(Recast_STAT_SRCS - Recast.cpp - RecastAlloc.cpp - RecastArea.cpp - RecastContour.cpp - RecastFilter.cpp - RecastLayers.cpp - RecastMesh.cpp - RecastMeshDetail.cpp - RecastRasterization.cpp - RecastRegion.cpp + Source/Recast.cpp + Source/RecastAlloc.cpp + Source/RecastArea.cpp + Source/RecastContour.cpp + Source/RecastFilter.cpp + Source/RecastLayers.cpp + Source/RecastMesh.cpp + Source/RecastMeshDetail.cpp + Source/RecastRasterization.cpp + Source/RecastRegion.cpp ) +include_directories(Include) + if(WIN32) include_directories( ${CMAKE_SOURCE_DIR}/dep/zlib diff --git a/dep/recastnavigation/Recast/Recast.h b/dep/recastnavigation/Recast/Include/Recast.h index fb36aa4c5cf..66974cdbcc3 100644 --- a/dep/recastnavigation/Recast/Recast.h +++ b/dep/recastnavigation/Recast/Include/Recast.h @@ -219,7 +219,7 @@ struct rcConfig int maxEdgeLen; /// The maximum distance a simplfied contour's border edges should deviate - /// the original raw contour. [Limit: >=0] [Units: wu] + /// the original raw contour. [Limit: >=0] [Units: vx] float maxSimplificationError; /// The minimum number of cells allowed to form isolated island areas. [Limit: >=0] [Units: vx] @@ -338,7 +338,7 @@ struct rcHeightfieldLayer int maxy; ///< The maximum y-bounds of usable data. (Along the z-axis.) int hmin; ///< The minimum height bounds of usable data. (Along the y-axis.) int hmax; ///< The maximum height bounds of usable data. (Along the y-axis.) - unsigned char* heights; ///< The heightfield. [Size: (width - borderSize*2) * (h - borderSize*2)] + unsigned char* heights; ///< The heightfield. [Size: width * height] unsigned char* areas; ///< Area ids. [Size: Same as #heights] unsigned char* cons; ///< Packed neighbor connection information. [Size: Same as #heights] }; @@ -549,6 +549,11 @@ static const int RC_NOT_CONNECTED = 0x3f; /// @name General helper functions /// @{ +/// Used to ignore a function parameter. VS complains about unused parameters +/// and this silences the warning. +/// @param [in] _ Unused parameter +template<class T> void rcIgnoreUnused(const T&) { } + /// Swaps the values of the two parameters. /// @param[in,out] a Value A /// @param[in,out] b Value B @@ -964,7 +969,7 @@ void rcMarkCylinderArea(rcContext* ctx, const float* pos, /// @returns True if the operation completed successfully. bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf); -/// Builds region data for the heightfield using watershed partitioning. +/// Builds region data for the heightfield using watershed partitioning. /// @ingroup recast /// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] chf A populated compact heightfield. @@ -978,6 +983,18 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf); bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, const int borderSize, const int minRegionArea, const int mergeRegionArea); +/// Builds region data for the heightfield by partitioning the heightfield in non-overlapping layers. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @param[in] borderSize The size of the non-navigable border around the heightfield. +/// [Limit: >=0] [Units: vx] +/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. +/// [Limit: >=0] [Units: vx]. +/// @returns True if the operation completed successfully. +bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea); + /// Builds region data for the heightfield using simple monotone partitioning. /// @ingroup recast /// @param[in,out] ctx The build context to use during the operation. @@ -992,7 +1009,6 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, const int borderSize, const int minRegionArea, const int mergeRegionArea); - /// Sets the neighbor connection data for the specified direction. /// @param[in] s The span to update. /// @param[in] dir The direction to set. [Limits: 0 <= value < 4] diff --git a/dep/recastnavigation/Recast/RecastAlloc.h b/dep/recastnavigation/Recast/Include/RecastAlloc.h index 438be9ea56b..438be9ea56b 100644 --- a/dep/recastnavigation/Recast/RecastAlloc.h +++ b/dep/recastnavigation/Recast/Include/RecastAlloc.h diff --git a/dep/recastnavigation/Recast/RecastAssert.h b/dep/recastnavigation/Recast/Include/RecastAssert.h index 2aca0d9a14f..2aca0d9a14f 100644 --- a/dep/recastnavigation/Recast/RecastAssert.h +++ b/dep/recastnavigation/Recast/Include/RecastAssert.h diff --git a/dep/recastnavigation/Recast/Recast.cpp b/dep/recastnavigation/Recast/Source/Recast.cpp index 803daac3bcf..b9d86036c3f 100644 --- a/dep/recastnavigation/Recast/Recast.cpp +++ b/dep/recastnavigation/Recast/Source/Recast.cpp @@ -208,12 +208,11 @@ void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* /// See the #rcConfig documentation for more information on the configuration parameters. /// /// @see rcAllocHeightfield, rcHeightfield -bool rcCreateHeightfield(rcContext* /*ctx*/, rcHeightfield& hf, int width, int height, +bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height, const float* bmin, const float* bmax, float cs, float ch) { - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(ctx); + rcIgnoreUnused(ctx); hf.width = width; hf.height = height; @@ -245,13 +244,12 @@ static void calcTriNormal(const float* v0, const float* v1, const float* v2, flo /// See the #rcConfig documentation for more information on the configuration parameters. /// /// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles -void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle, +void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int /*nv*/, const int* tris, int nt, unsigned char* areas) { - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(ctx); + rcIgnoreUnused(ctx); const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI); @@ -275,13 +273,12 @@ void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle, /// See the #rcConfig documentation for more information on the configuration parameters. /// /// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles -void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle, +void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int /*nv*/, const int* tris, int nt, unsigned char* areas) { - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(ctx); + rcIgnoreUnused(ctx); const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI); @@ -297,10 +294,9 @@ void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAng } } -int rcGetHeightFieldSpanCount(rcContext* /*ctx*/, rcHeightfield& hf) +int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf) { - // TODO: VC complains about unref formal variable, figure out a way to handle this better. -// rcAssert(ctx); + rcIgnoreUnused(ctx); const int w = hf.width; const int h = hf.height; diff --git a/dep/recastnavigation/Recast/RecastAlloc.cpp b/dep/recastnavigation/Recast/Source/RecastAlloc.cpp index b5ec1516146..b5ec1516146 100644 --- a/dep/recastnavigation/Recast/RecastAlloc.cpp +++ b/dep/recastnavigation/Recast/Source/RecastAlloc.cpp diff --git a/dep/recastnavigation/Recast/RecastArea.cpp b/dep/recastnavigation/Recast/Source/RecastArea.cpp index 1a338cd9b8c..1a338cd9b8c 100644 --- a/dep/recastnavigation/Recast/RecastArea.cpp +++ b/dep/recastnavigation/Recast/Source/RecastArea.cpp diff --git a/dep/recastnavigation/Recast/RecastContour.cpp b/dep/recastnavigation/Recast/Source/RecastContour.cpp index 5c324bcedfe..8aa9d1d92a1 100644 --- a/dep/recastnavigation/Recast/RecastContour.cpp +++ b/dep/recastnavigation/Recast/Source/RecastContour.cpp @@ -20,6 +20,7 @@ #include <math.h> #include <string.h> #include <stdio.h> +#include <stdlib.h> #include "Recast.h" #include "RecastAlloc.h" #include "RecastAssert.h" @@ -36,7 +37,7 @@ static int getCornerHeight(int x, int y, int i, int dir, unsigned int regs[4] = {0,0,0,0}; // Combine region and area codes in order to prevent - // border vertices which are in between two areas to be removed. + // border vertices which are in between two areas to be removed. regs[0] = chf.spans[i].reg | (chf.areas[i] << 16); if (rcGetCon(s, dir) != RC_NOT_CONNECTED) @@ -187,27 +188,6 @@ static float distancePtSeg(const int x, const int z, const int px, const int pz, const int qx, const int qz) { -/* float pqx = (float)(qx - px); - float pqy = (float)(qy - py); - float pqz = (float)(qz - pz); - float dx = (float)(x - px); - float dy = (float)(y - py); - float dz = (float)(z - pz); - float d = pqx*pqx + pqy*pqy + pqz*pqz; - float t = pqx*dx + pqy*dy + pqz*dz; - if (d > 0) - t /= d; - if (t < 0) - t = 0; - else if (t > 1) - t = 1; - - dx = px + t*pqx - x; - dy = py + t*pqy - y; - dz = pz + t*pqz - z; - - return dx*dx + dy*dy + dz*dz;*/ - float pqx = (float)(qx - px); float pqz = (float)(qz - pz); float dx = (float)(x - px); @@ -257,13 +237,13 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, simplified.push(points[i*4+2]); simplified.push(i); } - } + } } if (simplified.size() == 0) { // If there is no connections at all, - // create some initial points for the simplification process. + // create some initial points for the simplification process. // Find lower-left and upper-right vertices of the contour. int llx = points[0]; int lly = points[1]; @@ -311,19 +291,19 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, { int ii = (i+1) % (simplified.size()/4); - const int ax = simplified[i*4+0]; - const int az = simplified[i*4+2]; - const int ai = simplified[i*4+3]; - - const int bx = simplified[ii*4+0]; - const int bz = simplified[ii*4+2]; - const int bi = simplified[ii*4+3]; + int ax = simplified[i*4+0]; + int az = simplified[i*4+2]; + int ai = simplified[i*4+3]; + + int bx = simplified[ii*4+0]; + int bz = simplified[ii*4+2]; + int bi = simplified[ii*4+3]; // Find maximum deviation from the segment. float maxd = 0; int maxi = -1; int ci, cinc, endi; - + // Traverse the segment in lexilogical order so that the // max deviation is calculated similarly when traversing // opposite segments. @@ -338,6 +318,8 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, cinc = pn-1; ci = (bi+cinc) % pn; endi = ai; + rcSwap(ax, bx); + rcSwap(az, bz); } // Tessellate only outer edges or edges between areas. @@ -397,11 +379,11 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, const int bx = simplified[ii*4+0]; const int bz = simplified[ii*4+2]; const int bi = simplified[ii*4+3]; - + // Find maximum deviation from the segment. int maxi = -1; int ci = (ai+1) % pn; - + // Tessellate only outer edges or edges between areas. bool tess = false; // Wall edges. @@ -469,32 +451,6 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, } -static void removeDegenerateSegments(rcIntArray& simplified) -{ - // Remove adjacent vertices which are equal on xz-plane, - // or else the triangulator will get confused. - for (int i = 0; i < simplified.size()/4; ++i) - { - int ni = i+1; - if (ni >= (simplified.size()/4)) - ni = 0; - - if (simplified[i*4+0] == simplified[ni*4+0] && - simplified[i*4+2] == simplified[ni*4+2]) - { - // Degenerate segment, remove. - for (int j = i; j < simplified.size()/4-1; ++j) - { - simplified[j*4+0] = simplified[(j+1)*4+0]; - simplified[j*4+1] = simplified[(j+1)*4+1]; - simplified[j*4+2] = simplified[(j+1)*4+2]; - simplified[j*4+3] = simplified[(j+1)*4+3]; - } - simplified.resize(simplified.size()-4); - } - } -} - static int calcAreaOfPolygon2D(const int* verts, const int nverts) { int area = 0; @@ -507,54 +463,155 @@ static int calcAreaOfPolygon2D(const int* verts, const int nverts) return (area+1) / 2; } -inline bool ileft(const int* a, const int* b, const int* c) +// TODO: these are the same as in RecastMesh.cpp, consider using the same. + +inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; } +inline int next(int i, int n) { return i+1 < n ? i+1 : 0; } + +inline int area2(const int* a, const int* b, const int* c) +{ + return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]); +} + +// Exclusive or: true iff exactly one argument is true. +// The arguments are negated to ensure that they are 0/1 +// values. Then the bitwise Xor operator may apply. +// (This idea is due to Michael Baldwin.) +inline bool xorb(bool x, bool y) +{ + return !x ^ !y; +} + +// Returns true iff c is strictly to the left of the directed +// line through a to b. +inline bool left(const int* a, const int* b, const int* c) +{ + return area2(a, b, c) < 0; +} + +inline bool leftOn(const int* a, const int* b, const int* c) +{ + return area2(a, b, c) <= 0; +} + +inline bool collinear(const int* a, const int* b, const int* c) +{ + return area2(a, b, c) == 0; +} + +// Returns true iff ab properly intersects cd: they share +// a point interior to both segments. The properness of the +// intersection is ensured by using strict leftness. +static bool intersectProp(const int* a, const int* b, const int* c, const int* d) +{ + // Eliminate improper cases. + if (collinear(a,b,c) || collinear(a,b,d) || + collinear(c,d,a) || collinear(c,d,b)) + return false; + + return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b)); +} + +// Returns T iff (a,b,c) are collinear and point c lies +// on the closed segement ab. +static bool between(const int* a, const int* b, const int* c) +{ + if (!collinear(a, b, c)) + return false; + // If ab not vertical, check betweenness on x; else on y. + if (a[0] != b[0]) + return ((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0])); + else + return ((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2])); +} + +// Returns true iff segments ab and cd intersect, properly or improperly. +static bool intersect(const int* a, const int* b, const int* c, const int* d) +{ + if (intersectProp(a, b, c, d)) + return true; + else if (between(a, b, c) || between(a, b, d) || + between(c, d, a) || between(c, d, b)) + return true; + else + return false; +} + +static bool vequal(const int* a, const int* b) +{ + return a[0] == b[0] && a[2] == b[2]; +} + +static bool intersectSegCountour(const int* d0, const int* d1, int i, int n, const int* verts) { - return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0; + // For each edge (k,k+1) of P + for (int k = 0; k < n; k++) + { + int k1 = next(k, n); + // Skip edges incident to i. + if (i == k || i == k1) + continue; + const int* p0 = &verts[k * 4]; + const int* p1 = &verts[k1 * 4]; + if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1)) + continue; + + if (intersect(d0, d1, p0, p1)) + return true; + } + return false; } -static void getClosestIndices(const int* vertsa, const int nvertsa, - const int* vertsb, const int nvertsb, - int& ia, int& ib) +static bool inCone(int i, int n, const int* verts, const int* pj) { - int closestDist = 0xfffffff; - ia = -1, ib = -1; - for (int i = 0; i < nvertsa; ++i) + const int* pi = &verts[i * 4]; + const int* pi1 = &verts[next(i, n) * 4]; + const int* pin1 = &verts[prev(i, n) * 4]; + + // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ]. + if (leftOn(pin1, pi, pi1)) + return left(pi, pj, pin1) && left(pj, pi, pi1); + // Assume (i-1,i,i+1) not collinear. + // else P[i] is reflex. + return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1)); +} + + +static void removeDegenerateSegments(rcIntArray& simplified) +{ + // Remove adjacent vertices which are equal on xz-plane, + // or else the triangulator will get confused. + int npts = simplified.size()/4; + for (int i = 0; i < npts; ++i) { - const int in = (i+1) % nvertsa; - const int ip = (i+nvertsa-1) % nvertsa; - const int* va = &vertsa[i*4]; - const int* van = &vertsa[in*4]; - const int* vap = &vertsa[ip*4]; + int ni = next(i, npts); - for (int j = 0; j < nvertsb; ++j) + if (vequal(&simplified[i*4], &simplified[ni*4])) { - const int* vb = &vertsb[j*4]; - // vb must be "infront" of va. - if (ileft(vap,va,vb) && ileft(va,van,vb)) + // Degenerate segment, remove. + for (int j = i; j < simplified.size()/4-1; ++j) { - const int dx = vb[0] - va[0]; - const int dz = vb[2] - va[2]; - const int d = dx*dx + dz*dz; - if (d < closestDist) - { - ia = i; - ib = j; - closestDist = d; - } + simplified[j*4+0] = simplified[(j+1)*4+0]; + simplified[j*4+1] = simplified[(j+1)*4+1]; + simplified[j*4+2] = simplified[(j+1)*4+2]; + simplified[j*4+3] = simplified[(j+1)*4+3]; } + simplified.resize(simplified.size()-4); + npts--; } } } + static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib) { const int maxVerts = ca.nverts + cb.nverts + 2; int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM); if (!verts) return false; - + int nv = 0; - + // Copy contour A. for (int i = 0; i <= ca.nverts; ++i) { @@ -582,7 +639,7 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib) rcFree(ca.verts); ca.verts = verts; ca.nverts = nv; - + rcFree(cb.verts); cb.verts = 0; cb.nverts = 0; @@ -590,18 +647,179 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib) return true; } +struct rcContourHole +{ + rcContour* contour; + int minx, minz, leftmost; +}; + +struct rcContourRegion +{ + rcContour* outline; + rcContourHole* holes; + int nholes; +}; + +struct rcPotentialDiagonal +{ + int vert; + int dist; +}; + +// Finds the lowest leftmost vertex of a contour. +static void findLeftMostVertex(rcContour* contour, int* minx, int* minz, int* leftmost) +{ + *minx = contour->verts[0]; + *minz = contour->verts[2]; + *leftmost = 0; + for (int i = 1; i < contour->nverts; i++) + { + const int x = contour->verts[i*4+0]; + const int z = contour->verts[i*4+2]; + if (x < *minx || (x == *minx && z < *minz)) + { + *minx = x; + *minz = z; + *leftmost = i; + } + } +} + +static int compareHoles(const void* va, const void* vb) +{ + const rcContourHole* a = (const rcContourHole*)va; + const rcContourHole* b = (const rcContourHole*)vb; + if (a->minx == b->minx) + { + if (a->minz < b->minz) + return -1; + if (a->minz > b->minz) + return 1; + } + else + { + if (a->minx < b->minx) + return -1; + if (a->minx > b->minx) + return 1; + } + return 0; +} + + +static int compareDiagDist(const void* va, const void* vb) +{ + const rcPotentialDiagonal* a = (const rcPotentialDiagonal*)va; + const rcPotentialDiagonal* b = (const rcPotentialDiagonal*)vb; + if (a->dist < b->dist) + return -1; + if (a->dist > b->dist) + return 1; + return 0; +} + + +static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region) +{ + // Sort holes from left to right. + for (int i = 0; i < region.nholes; i++) + findLeftMostVertex(region.holes[i].contour, ®ion.holes[i].minx, ®ion.holes[i].minz, ®ion.holes[i].leftmost); + + qsort(region.holes, region.nholes, sizeof(rcContourHole), compareHoles); + + int maxVerts = region.outline->nverts; + for (int i = 0; i < region.nholes; i++) + maxVerts += region.holes[i].contour->nverts; + + rcScopedDelete<rcPotentialDiagonal> diags = (rcPotentialDiagonal*)rcAlloc(sizeof(rcPotentialDiagonal)*maxVerts, RC_ALLOC_TEMP); + if (!diags) + { + ctx->log(RC_LOG_WARNING, "mergeRegionHoles: Failed to allocated diags %d.", maxVerts); + return; + } + + rcContour* outline = region.outline; + + // Merge holes into the outline one by one. + for (int i = 0; i < region.nholes; i++) + { + rcContour* hole = region.holes[i].contour; + + int index = -1; + int bestVertex = region.holes[i].leftmost; + for (int iter = 0; iter < hole->nverts; iter++) + { + // Find potential diagonals. + // The 'best' vertex must be in the cone described by 3 cosequtive vertices of the outline. + // ..o j-1 + // | + // | * best + // | + // j o-----o j+1 + // : + int ndiags = 0; + const int* corner = &hole->verts[bestVertex*4]; + for (int j = 0; j < outline->nverts; j++) + { + if (inCone(j, outline->nverts, outline->verts, corner)) + { + int dx = outline->verts[j*4+0] - corner[0]; + int dz = outline->verts[j*4+2] - corner[2]; + diags[ndiags].vert = j; + diags[ndiags].dist = dx*dx + dz*dz; + ndiags++; + } + } + // Sort potential diagonals by distance, we want to make the connection as short as possible. + qsort(diags, ndiags, sizeof(rcPotentialDiagonal), compareDiagDist); + + // Find a diagonal that is not intersecting the outline not the remaining holes. + index = -1; + for (int j = 0; j < ndiags; j++) + { + const int* pt = &outline->verts[diags[j].vert*4]; + bool intersect = intersectSegCountour(pt, corner, diags[i].vert, outline->nverts, outline->verts); + for (int k = i; k < region.nholes && !intersect; k++) + intersect |= intersectSegCountour(pt, corner, -1, region.holes[k].contour->nverts, region.holes[k].contour->verts); + if (!intersect) + { + index = diags[j].vert; + break; + } + } + // If found non-intersecting diagonal, stop looking. + if (index != -1) + break; + // All the potential diagonals for the current vertex were intersecting, try next vertex. + bestVertex = (bestVertex + 1) % hole->nverts; + } + + if (index == -1) + { + ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to find merge points for %p and %p.", region.outline, hole); + continue; + } + if (!mergeContours(*region.outline, *hole, index, bestVertex)) + { + ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to merge contours %p and %p.", region.outline, hole); + continue; + } + } +} + + /// @par /// /// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen /// parameters control how closely the simplified contours will match the raw contours. /// -/// Simplified contours are generated such that the vertices for portals between areas match up. +/// Simplified contours are generated such that the vertices for portals between areas match up. /// (They are considered mandatory vertices.) /// /// Setting @p maxEdgeLength to zero will disabled the edge length feature. -/// +/// /// See the #rcConfig documentation for more information on the configuration parameters. -/// +/// /// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, const float maxError, const int maxEdgeLen, @@ -704,17 +922,17 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, verts.resize(0); simplified.resize(0); - + ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE); walkContour(x, y, i, chf, flags, verts); ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE); - + ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags); removeDegenerateSegments(simplified); ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); - + // Store region->contour remap info. // Create contour. if (simplified.size()/4 >= 3) @@ -722,7 +940,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, if (cset.nconts >= maxContours) { // Allocate more contours. - // This can happen when there are tiny holes in the heightfield. + // This happens when a region has holes. const int oldMax = maxContours; maxContours *= 2; rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); @@ -735,10 +953,10 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, } rcFree(cset.conts); cset.conts = newConts; - + ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours); } - + rcContour* cont = &cset.conts[cset.nconts++]; cont->nverts = simplified.size()/4; @@ -779,17 +997,6 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, } } -/* cont->cx = cont->cy = cont->cz = 0; - for (int i = 0; i < cont->nverts; ++i) - { - cont->cx += cont->verts[i*4+0]; - cont->cy += cont->verts[i*4+1]; - cont->cz += cont->verts[i*4+2]; - } - cont->cx /= cont->nverts; - cont->cy /= cont->nverts; - cont->cz /= cont->nverts;*/ - cont->reg = reg; cont->area = area; } @@ -797,52 +1004,100 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, } } - // Check and merge droppings. - // Sometimes the previous algorithms can fail and create several contours - // per area. This pass will try to merge the holes into the main region. - for (int i = 0; i < cset.nconts; ++i) + // Merge holes if needed. + if (cset.nconts > 0) { - rcContour& cont = cset.conts[i]; - // Check if the contour is would backwards. - if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0) + // Calculate winding of all polygons. + rcScopedDelete<char> winding = (char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP); + if (!winding) { - // Find another contour which has the same region ID. - int mergeIdx = -1; - for (int j = 0; j < cset.nconts; ++j) + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts); + return false; + } + int nholes = 0; + for (int i = 0; i < cset.nconts; ++i) + { + rcContour& cont = cset.conts[i]; + // If the contour is wound backwards, it is a hole. + winding[i] = calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0 ? -1 : 1; + if (winding[i] < 0) + nholes++; + } + + if (nholes > 0) + { + // Collect outline contour and holes contours per region. + // We assume that there is one outline and multiple holes. + const int nregions = chf.maxRegions+1; + rcScopedDelete<rcContourRegion> regions = (rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP); + if (!regions) + { + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'regions' (%d).", nregions); + return false; + } + memset(regions, 0, sizeof(rcContourRegion)*nregions); + + rcScopedDelete<rcContourHole> holes = (rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP); + if (!holes) + { + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'holes' (%d).", cset.nconts); + return false; + } + memset(holes, 0, sizeof(rcContourHole)*cset.nconts); + + for (int i = 0; i < cset.nconts; ++i) { - if (i == j) continue; - if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg) + rcContour& cont = cset.conts[i]; + // Positively would contours are outlines, negative holes. + if (winding[i] > 0) { - // Make sure the polygon is correctly oriented. - if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts)) - { - mergeIdx = j; - break; - } + if (regions[cont.reg].outline) + ctx->log(RC_LOG_ERROR, "rcBuildContours: Multiple outlines for region %d.", cont.reg); + regions[cont.reg].outline = &cont; + } + else + { + regions[cont.reg].nholes++; } } - if (mergeIdx == -1) + int index = 0; + for (int i = 0; i < nregions; i++) { - ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i); + if (regions[i].nholes > 0) + { + regions[i].holes = &holes[index]; + index += regions[i].nholes; + regions[i].nholes = 0; + } } - else + for (int i = 0; i < cset.nconts; ++i) + { + rcContour& cont = cset.conts[i]; + rcContourRegion& reg = regions[cont.reg]; + if (winding[i] < 0) + reg.holes[reg.nholes++].contour = &cont; + } + + // Finally merge each regions holes into the outline. + for (int i = 0; i < nregions; i++) { - rcContour& mcont = cset.conts[mergeIdx]; - // Merge by closest points. - int ia = 0, ib = 0; - getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib); - if (ia == -1 || ib == -1) + rcContourRegion& reg = regions[i]; + if (!reg.nholes) continue; + + if (reg.outline) { - ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx); - continue; + mergeRegionHoles(ctx, reg); } - if (!mergeContours(mcont, cont, ia, ib)) + else { - ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx); - continue; + // The region does not have an outline. + // This can happen if the contour becaomes selfoverlapping because of + // too aggressive simplification settings. + ctx->log(RC_LOG_ERROR, "rcBuildContours: Bad outline for region %d, contour simplification is likely too aggressive.", i); } } } + } ctx->stopTimer(RC_TIMER_BUILD_CONTOURS); diff --git a/dep/recastnavigation/Recast/RecastFilter.cpp b/dep/recastnavigation/Recast/Source/RecastFilter.cpp index bf985c362c9..bf985c362c9 100644 --- a/dep/recastnavigation/Recast/RecastFilter.cpp +++ b/dep/recastnavigation/Recast/Source/RecastFilter.cpp diff --git a/dep/recastnavigation/Recast/RecastLayers.cpp b/dep/recastnavigation/Recast/Source/RecastLayers.cpp index 5ea6cb79d16..cb1a39f4bda 100644 --- a/dep/recastnavigation/Recast/RecastLayers.cpp +++ b/dep/recastnavigation/Recast/Source/RecastLayers.cpp @@ -38,7 +38,7 @@ struct rcLayerRegion unsigned char layerId; // Layer ID unsigned char nlayers; // Layer count unsigned char nneis; // Neighbour count - unsigned char base; // Flag indicating if the region is hte base of merged regions. + unsigned char base; // Flag indicating if the region is the base of merged regions. }; @@ -325,7 +325,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, continue; // Skip if the height range would become too large. const int ymin = rcMin(root.ymin, regn.ymin); - const int ymax = rcMax(root.ymax, regn.ymax); // Edited by TC + const int ymax = rcMax(root.ymax, regn.ymax); if ((ymax - ymin) >= 255) continue; @@ -373,7 +373,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, continue; // Skip if the height range would become too large. const int ymin = rcMin(ri.ymin, rj.ymin); - const int ymax = rcMax(ri.ymax, rj.ymax); // Edited by TC + const int ymax = rcMax(ri.ymax, rj.ymax); if ((ymax - ymin) >= 255) continue; diff --git a/dep/recastnavigation/Recast/RecastMesh.cpp b/dep/recastnavigation/Recast/Source/RecastMesh.cpp index 13aad2af01c..e4f9c4b3629 100644 --- a/dep/recastnavigation/Recast/RecastMesh.cpp +++ b/dep/recastnavigation/Recast/Source/RecastMesh.cpp @@ -288,6 +288,53 @@ static bool diagonal(int i, int j, int n, const int* verts, int* indices) return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices); } + +static bool diagonalieLoose(int i, int j, int n, const int* verts, int* indices) +{ + const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4]; + const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4]; + + // For each edge (k,k+1) of P + for (int k = 0; k < n; k++) + { + int k1 = next(k, n); + // Skip edges incident to i or j + if (!((k == i) || (k1 == i) || (k == j) || (k1 == j))) + { + const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4]; + const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4]; + + if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1)) + continue; + + if (intersectProp(d0, d1, p0, p1)) + return false; + } + } + return true; +} + +static bool inConeLoose(int i, int j, int n, const int* verts, int* indices) +{ + const int* pi = &verts[(indices[i] & 0x0fffffff) * 4]; + const int* pj = &verts[(indices[j] & 0x0fffffff) * 4]; + const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4]; + const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4]; + + // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ]. + if (leftOn(pin1, pi, pi1)) + return leftOn(pi, pj, pin1) && leftOn(pj, pi, pi1); + // Assume (i-1,i,i+1) not collinear. + // else P[i] is reflex. + return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1)); +} + +static bool diagonalLoose(int i, int j, int n, const int* verts, int* indices) +{ + return inConeLoose(i, j, n, verts, indices) && diagonalieLoose(i, j, n, verts, indices); +} + + static int triangulate(int n, const int* verts, int* indices, int* tris) { int ntris = 0; @@ -328,14 +375,41 @@ static int triangulate(int n, const int* verts, int* indices, int* tris) if (mini == -1) { - // Should not happen. -/* printf("mini == -1 ntris=%d n=%d\n", ntris, n); + // We might get here because the contour has overlapping segments, like this: + // + // A o-o=====o---o B + // / |C D| \ + // o o o o + // : : : : + // We'll try to recover by loosing up the inCone test a bit so that a diagonal + // like A-B or C-D can be found and we can continue. + minLen = -1; + mini = -1; for (int i = 0; i < n; i++) { - printf("%d ", indices[i] & 0x0fffffff); + int i1 = next(i, n); + int i2 = next(i1, n); + if (diagonalLoose(i, i2, n, verts, indices)) + { + const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4]; + const int* p2 = &verts[(indices[next(i2, n)] & 0x0fffffff) * 4]; + int dx = p2[0] - p0[0]; + int dy = p2[2] - p0[2]; + int len = dx*dx + dy*dy; + + if (minLen < 0 || len < minLen) + { + minLen = len; + mini = i; + } + } + } + if (mini == -1) + { + // The contour is messed up. This sometimes happens + // if the contour simplification is too aggressive. + return -ntris; } - printf("\n");*/ - return -ntris; } int i = mini; @@ -661,7 +735,8 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short } // Remove the polygon. unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2]; - memcpy(p,p2,sizeof(unsigned short)*nvp); + if (p != p2) + memcpy(p,p2,sizeof(unsigned short)*nvp); memset(p+nvp,0xff,sizeof(unsigned short)*nvp); mesh.regs[i] = mesh.regs[mesh.npolys-1]; mesh.areas[i] = mesh.areas[mesh.npolys-1]; @@ -861,7 +936,9 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); - memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); + unsigned short* last = &polys[(npolys-1)*nvp]; + if (pb != last) + memcpy(pb, last, sizeof(unsigned short)*nvp); pregs[bestPb] = pregs[npolys-1]; pareas[bestPb] = pareas[npolys-1]; npolys--; @@ -1105,7 +1182,9 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); - memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); + unsigned short* lastPoly = &polys[(npolys-1)*nvp]; + if (pb != lastPoly) + memcpy(pb, lastPoly, sizeof(unsigned short)*nvp); npolys--; } else @@ -1319,6 +1398,12 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f); const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f); + bool isMinX = (ox == 0); + bool isMinZ = (oz == 0); + bool isMaxX = ((unsigned short)floorf((mesh.bmax[0] - pmesh->bmax[0]) / mesh.cs + 0.5f)) == 0; + bool isMaxZ = ((unsigned short)floorf((mesh.bmax[2] - pmesh->bmax[2]) / mesh.cs + 0.5f)) == 0; + bool isOnBorder = (isMinX || isMinZ || isMaxX || isMaxZ); + for (int j = 0; j < pmesh->nverts; ++j) { unsigned short* v = &pmesh->verts[j*3]; @@ -1339,6 +1424,36 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r if (src[k] == RC_MESH_NULL_IDX) break; tgt[k] = vremap[src[k]]; } + + if (isOnBorder) + { + for (int k = mesh.nvp; k < mesh.nvp * 2; ++k) + { + if (src[k] & 0x8000 && src[k] != 0xffff) + { + unsigned short dir = src[k] & 0xf; + switch (dir) + { + case 0: // Portal x- + if (isMinX) + tgt[k] = src[k]; + break; + case 1: // Portal z+ + if (isMaxZ) + tgt[k] = src[k]; + break; + case 2: // Portal x+ + if (isMaxX) + tgt[k] = src[k]; + break; + case 3: // Portal z- + if (isMinZ) + tgt[k] = src[k]; + break; + } + } + } + } } } @@ -1422,7 +1537,7 @@ bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst) ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.flags' (%d).", src.npolys); return false; } - memcpy(dst.flags, src.flags, sizeof(unsigned char)*src.npolys); + memcpy(dst.flags, src.flags, sizeof(unsigned short)*src.npolys); return true; } diff --git a/dep/recastnavigation/Recast/RecastMeshDetail.cpp b/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp index f49d67400c2..5cc2adf0320 100644 --- a/dep/recastnavigation/Recast/RecastMeshDetail.cpp +++ b/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp @@ -56,7 +56,7 @@ inline float vdist2(const float* p, const float* q) } inline float vcross2(const float* p1, const float* p2, const float* p3) -{ +{ const float u1 = p2[0] - p1[0]; const float v1 = p2[2] - p1[2]; const float u2 = p3[0] - p1[0]; @@ -68,21 +68,27 @@ static bool circumCircle(const float* p1, const float* p2, const float* p3, float* c, float& r) { static const float EPS = 1e-6f; + // Calculate the circle relative to p1, to avoid some precision issues. + const float v1[3] = {0,0,0}; + float v2[3], v3[3]; + rcVsub(v2, p2,p1); + rcVsub(v3, p3,p1); - const float cp = vcross2(p1, p2, p3); + const float cp = vcross2(v1, v2, v3); if (fabsf(cp) > EPS) { - const float p1Sq = vdot2(p1,p1); - const float p2Sq = vdot2(p2,p2); - const float p3Sq = vdot2(p3,p3); - c[0] = (p1Sq*(p2[2]-p3[2]) + p2Sq*(p3[2]-p1[2]) + p3Sq*(p1[2]-p2[2])) / (2*cp); - c[2] = (p1Sq*(p3[0]-p2[0]) + p2Sq*(p1[0]-p3[0]) + p3Sq*(p2[0]-p1[0])) / (2*cp); - r = vdist2(c, p1); + const float v1Sq = vdot2(v1,v1); + const float v2Sq = vdot2(v2,v2); + const float v3Sq = vdot2(v3,v3); + c[0] = (v1Sq*(v2[2]-v3[2]) + v2Sq*(v3[2]-v1[2]) + v3Sq*(v1[2]-v2[2])) / (2*cp); + c[1] = 0; + c[2] = (v1Sq*(v3[0]-v2[0]) + v2Sq*(v1[0]-v3[0]) + v3Sq*(v2[0]-v1[0])) / (2*cp); + r = vdist2(c, v1); + rcVadd(c, c, p1); return true; } - - c[0] = p1[0]; - c[2] = p1[2]; + + rcVcopy(c, p1); r = 0; return false; } @@ -93,7 +99,7 @@ static float distPtTri(const float* p, const float* a, const float* b, const flo rcVsub(v0, c,a); rcVsub(v1, b,a); rcVsub(v2, p,a); - + const float dot00 = vdot2(v0, v0); const float dot01 = vdot2(v0, v1); const float dot02 = vdot2(v0, v2); @@ -178,7 +184,7 @@ static float distToTriMesh(const float* p, const float* verts, const int /*nvert static float distToPoly(int nvert, const float* verts, const float* p) { - + float dmin = FLT_MAX; int i, j, c = 0; for (i = 0, j = nvert-1; i < nvert; j = i++) @@ -200,8 +206,8 @@ static unsigned short getHeight(const float fx, const float fy, const float fz, { int ix = (int)floorf(fx*ics + 0.01f); int iz = (int)floorf(fz*ics + 0.01f); - ix = rcClamp(ix-hp.xmin, 0, hp.width); - iz = rcClamp(iz-hp.ymin, 0, hp.height); + ix = rcClamp(ix-hp.xmin, 0, hp.width - 1); + iz = rcClamp(iz-hp.ymin, 0, hp.height - 1); unsigned short h = hp.data[ix+iz*hp.width]; if (h == RC_UNSET_HEIGHT) { @@ -216,22 +222,13 @@ static unsigned short getHeight(const float fx, const float fy, const float fz, if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) continue; const unsigned short nh = hp.data[nx+nz*hp.width]; if (nh == RC_UNSET_HEIGHT) continue; - + const float d = fabsf(nh*ch - fy); if (d < dmin) { h = nh; dmin = d; } - -/* const float dx = (nx+0.5f)*cs - fx; - const float dz = (nz+0.5f)*cs - fz; - const float d = dx*dx+dz*dz; - if (d < dmin) - { - h = nh; - dmin = d; - } */ } } return h; @@ -263,7 +260,7 @@ static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges, return UNDEF; } - // Add edge if not already in the triangulation. + // Add edge if not already in the triangulation. int e = findEdge(edges, nedges, s, t); if (e == UNDEF) { @@ -286,7 +283,7 @@ static void updateLeftFace(int* e, int s, int t, int f) e[2] = f; else if (e[1] == s && e[0] == t && e[3] == UNDEF) e[3] = f; -} +} static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float* d) { @@ -298,7 +295,7 @@ static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float a4 = a3 + a2 - a1; if (a3 * a4 < 0.0f) return 1; - } + } return 0; } @@ -320,7 +317,7 @@ static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1, static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges, int& nedges, const int maxEdges, int& nfaces, int e) { static const float EPS = 1e-5f; - + int* edge = &edges[e*4]; // Cache s and t. @@ -337,11 +334,11 @@ static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges } else { - // Edge already completed. + // Edge already completed. return; } - // Find best point on left of edge. + // Find best point on left of edge. int pt = npts; float c[3] = {0,0,0}; float r = -1; @@ -385,20 +382,20 @@ static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges } } - // Add new triangle or update edge info if s-t is on hull. + // Add new triangle or update edge info if s-t is on hull. if (pt < npts) { - // Update face information of edge being completed. + // Update face information of edge being completed. updateLeftFace(&edges[e*4], s, t, nfaces); - // Add new edge or update face info of old edge. + // Add new edge or update face info of old edge. e = findEdge(edges, nedges, pt, s); if (e == UNDEF) addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, UNDEF); else updateLeftFace(&edges[e*4], pt, s, nfaces); - // Add new edge or update face info of old edge. + // Add new edge or update face info of old edge. e = findEdge(edges, nedges, t, pt); if (e == UNDEF) addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, UNDEF); @@ -434,7 +431,7 @@ static void delaunayHull(rcContext* ctx, const int npts, const float* pts, completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge); currentEdge++; } - + // Create tris tris.resize(nfaces*4); for (int i = 0; i < nfaces*4; ++i) @@ -489,6 +486,103 @@ static void delaunayHull(rcContext* ctx, const int npts, const float* pts, } } +// Calculate minimum extend of the polygon. +static float polyMinExtent(const float* verts, const int nverts) +{ + float minDist = FLT_MAX; + for (int i = 0; i < nverts; i++) + { + const int ni = (i+1) % nverts; + const float* p1 = &verts[i*3]; + const float* p2 = &verts[ni*3]; + float maxEdgeDist = 0; + for (int j = 0; j < nverts; j++) + { + if (j == i || j == ni) continue; + float d = distancePtSeg2d(&verts[j*3], p1,p2); + maxEdgeDist = rcMax(maxEdgeDist, d); + } + minDist = rcMin(minDist, maxEdgeDist); + } + return rcSqrt(minDist); +} + +inline int next(int i, int n) +{ + return (i+1) % n; +} + +inline int prev(int i, int n) +{ + return (i + n-1) % n; +} + +static void triangulateHull(const int nverts, const float* verts, const int nhull, const int* hull, rcIntArray& tris) +{ + int start = 0, left = 1, right = nhull-1; + + // Start from an ear with shortest perimeter. + // This tends to favor well formed triangles as starting point. + float dmin = 0; + for (int i = 0; i < nhull; i++) + { + int pi = prev(i, nhull); + int ni = next(i, nhull); + const float* pv = &verts[hull[pi]*3]; + const float* cv = &verts[hull[i]*3]; + const float* nv = &verts[hull[ni]*3]; + const float d = vdist2(pv,cv) + vdist2(cv,nv) + vdist2(nv,pv); + if (d < dmin) + { + start = i; + left = ni; + right = pi; + dmin = d; + } + } + + // Add first triangle + tris.push(hull[start]); + tris.push(hull[left]); + tris.push(hull[right]); + tris.push(0); + + // Triangulate the polygon by moving left or right, + // depending on which triangle has shorter perimeter. + // This heuristic was chose emprically, since it seems + // handle tesselated straight edges well. + while (next(left, nhull) != right) + { + // Check to see if se should advance left or right. + int nleft = next(left, nhull); + int nright = prev(right, nhull); + + const float* cvleft = &verts[hull[left]*3]; + const float* nvleft = &verts[hull[nleft]*3]; + const float* cvright = &verts[hull[right]*3]; + const float* nvright = &verts[hull[nright]*3]; + const float dleft = vdist2(cvleft, nvleft) + vdist2(nvleft, cvright); + const float dright = vdist2(cvright, nvright) + vdist2(cvleft, nvright); + + if (dleft < dright) + { + tris.push(hull[left]); + tris.push(hull[nleft]); + tris.push(hull[right]); + tris.push(0); + left = nleft; + } + else + { + tris.push(hull[left]); + tris.push(hull[nright]); + tris.push(hull[right]); + tris.push(0); + right = nright; + } + } +} + inline float getJitterX(const int i) { @@ -512,16 +606,22 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, float edge[(MAX_VERTS_PER_EDGE+1)*3]; int hull[MAX_VERTS]; int nhull = 0; - + nverts = 0; - + for (int i = 0; i < nin; ++i) rcVcopy(&verts[i*3], &in[i*3]); nverts = nin; + edges.resize(0); + tris.resize(0); + const float cs = chf.cs; const float ics = 1.0f/cs; + // Calculate minimum extents of the polygon based on input data. + float minExtent = polyMinExtent(verts, nverts); + // Tessellate outlines. // This is done in separate pass in order to ensure // seamless height values across the ply boundaries. @@ -554,7 +654,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, float dx = vi[0] - vj[0]; float dy = vi[1] - vj[1]; float dz = vi[2] - vj[2]; - float d = rcSqrt(dx*dx + dz*dz); + float d = sqrtf(dx*dx + dz*dz); int nn = 1 + (int)floorf(d/sampleDist); if (nn >= MAX_VERTS_PER_EDGE) nn = MAX_VERTS_PER_EDGE-1; if (nverts+nn >= MAX_VERTS) @@ -628,27 +728,26 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, } } - + // If the polygon minimum extent is small (sliver or small triangle), do not try to add internal points. + if (minExtent < sampleDist*2) + { + triangulateHull(nverts, verts, nhull, hull, tris); + return true; + } + // Tessellate the base mesh. - edges.resize(0); - tris.resize(0); - - delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); + // We're using the triangulateHull instead of delaunayHull as it tends to + // create a bit better triangulation for long thing triangles when there + // are no internal points. + triangulateHull(nverts, verts, nhull, hull, tris); if (tris.size() == 0) { // Could not triangulate the poly, make sure there is some valid data there. - ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data."); - for (int i = 2; i < nverts; ++i) - { - tris.push(0); - tris.push(i-1); - tris.push(i); - tris.push(0); - } + ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon (%d verts).", nverts); return true; } - + if (sampleDist > 0) { // Create sample locations in a grid. @@ -681,7 +780,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, samples.push(0); // Not added } } - + // Add the samples starting from the one that has the most // error. The procedure stops when all samples are added // or when the max error is within treshold. @@ -690,7 +789,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, { if (nverts >= MAX_VERTS) break; - + // Find sample with most error. float bestpt[3] = {0,0,0}; float bestd = 0; @@ -728,23 +827,24 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, edges.resize(0); tris.resize(0); delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); - } + } } - + const int ntris = tris.size()/4; if (ntris > MAX_TRIS) { tris.resize(MAX_TRIS*4); ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from %d to max %d.", ntris, MAX_TRIS); } - + return true; } -static void getHeightData(const rcCompactHeightfield& chf, - const unsigned short* poly, const int npoly, - const unsigned short* verts, const int bs, - rcHeightPatch& hp, rcIntArray& stack) + +static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf, + const unsigned short* poly, const int npoly, + const unsigned short* verts, const int bs, + rcHeightPatch& hp, rcIntArray& stack) { // Floodfill the heightfield to get 2D height data, // starting at vertex locations as seeds. @@ -848,7 +948,7 @@ static void getHeightData(const rcCompactHeightfield& chf, continue; const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir); - + int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; hp.data[idx] = 1; @@ -857,9 +957,9 @@ static void getHeightData(const rcCompactHeightfield& chf, stack.push(ai); } } - + memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); - + // Mark start locations. for (int i = 0; i < stack.size(); i += 3) { @@ -869,8 +969,83 @@ static void getHeightData(const rcCompactHeightfield& chf, int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; const rcCompactSpan& cs = chf.spans[ci]; hp.data[idx] = cs.y; + + // getHeightData seeds are given in coordinates with borders + stack[i+0] += bs; + stack[i+1] += bs; } +} + + + +static void getHeightData(const rcCompactHeightfield& chf, + const unsigned short* poly, const int npoly, + const unsigned short* verts, const int bs, + rcHeightPatch& hp, rcIntArray& stack, + int region) +{ + // Note: Reads to the compact heightfield are offset by border size (bs) + // since border size offset is already removed from the polymesh vertices. + + stack.resize(0); + memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); + + bool empty = true; + + // Copy the height from the same region, and mark region borders + // as seed points to fill the rest. + for (int hy = 0; hy < hp.height; hy++) + { + int y = hp.ymin + hy + bs; + for (int hx = 0; hx < hp.width; hx++) + { + int x = hp.xmin + hx + bs; + const rcCompactCell& c = chf.cells[x+y*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + if (s.reg == region) + { + // Store height + hp.data[hx + hy*hp.width] = s.y; + empty = false; + + // If any of the neighbours is not in same region, + // add the current location as flood fill start + bool border = false; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); + const rcCompactSpan& as = chf.spans[ai]; + if (as.reg != region) + { + border = true; + break; + } + } + } + if (border) + { + stack.push(x); + stack.push(y); + stack.push(i); + } + break; + } + } + } + } + + // if the polygon does not contian any points from the current region (rare, but happens) + // then use the cells closest to the polygon vertices as seeds to fill the height field + if (empty) + getHeightDataSeedsFromVertices(chf, poly, npoly, verts, bs, hp, stack); + static const int RETRACT_SIZE = 256; int head = 0; @@ -887,7 +1062,7 @@ static void getHeightData(const rcCompactHeightfield& chf, memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3)); stack.resize(stack.size()-RETRACT_SIZE*3); } - + const rcCompactSpan& cs = chf.spans[ci]; for (int dir = 0; dir < 4; ++dir) { @@ -895,26 +1070,25 @@ static void getHeightData(const rcCompactHeightfield& chf, const int ax = cx + rcGetDirOffsetX(dir); const int ay = cy + rcGetDirOffsetY(dir); + const int hx = ax - hp.xmin - bs; + const int hy = ay - hp.ymin - bs; - if (ax < hp.xmin || ax >= (hp.xmin+hp.width) || - ay < hp.ymin || ay >= (hp.ymin+hp.height)) + if (hx < 0 || hx >= hp.width || hy < 0 || hy >= hp.height) continue; - if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != RC_UNSET_HEIGHT) + if (hp.data[hx + hy*hp.width] != RC_UNSET_HEIGHT) continue; - const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir); - + const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(cs, dir); const rcCompactSpan& as = chf.spans[ai]; - int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; - hp.data[idx] = as.y; - + + hp.data[hx + hy*hp.width] = as.y; + stack.push(ax); stack.push(ay); stack.push(ai); } } - } static unsigned char getEdgeFlags(const float* va, const float* vb, @@ -924,7 +1098,7 @@ static unsigned char getEdgeFlags(const float* va, const float* vb, static const float thrSqr = rcSqr(0.001f); for (int i = 0, j = npoly-1; i < npoly; j=i++) { - if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr && + if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr && distancePtSeg2d(vb, &vpoly[j*3], &vpoly[i*3]) < thrSqr) return 1; } @@ -953,7 +1127,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa rcAssert(ctx); ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL); - + if (mesh.nverts == 0 || mesh.npolys == 0) return true; @@ -1032,10 +1206,10 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4); return false; } - + int vcap = nPolyVerts+nPolyVerts/2; int tcap = vcap*2; - + dmesh.nverts = 0; dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM); if (!dmesh.verts) @@ -1044,7 +1218,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa return false; } dmesh.ntris = 0; - dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char*)*tcap*4, RC_ALLOC_PERM); + dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM); if (!dmesh.tris) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4); @@ -1072,7 +1246,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa hp.ymin = bounds[i*4+2]; hp.width = bounds[i*4+1]-bounds[i*4+0]; hp.height = bounds[i*4+3]-bounds[i*4+2]; - getHeightData(chf, p, npoly, mesh.verts, borderSize, hp, stack); + getHeightData(chf, p, npoly, mesh.verts, borderSize, hp, stack, mesh.regs[i]); // Build detail mesh. int nverts = 0; @@ -1083,7 +1257,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa { return false; } - + // Move detail verts to world space. for (int j = 0; j < nverts; ++j) { @@ -1098,21 +1272,21 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa poly[j*3+1] += orig[1]; poly[j*3+2] += orig[2]; } - + // Store detail submesh. const int ntris = tris.size()/4; - + dmesh.meshes[i*4+0] = (unsigned int)dmesh.nverts; dmesh.meshes[i*4+1] = (unsigned int)nverts; dmesh.meshes[i*4+2] = (unsigned int)dmesh.ntris; - dmesh.meshes[i*4+3] = (unsigned int)ntris; + dmesh.meshes[i*4+3] = (unsigned int)ntris; // Store vertices, allocate more memory if necessary. if (dmesh.nverts+nverts > vcap) { while (dmesh.nverts+nverts > vcap) vcap += 256; - + float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM); if (!newv) { @@ -1158,9 +1332,9 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa dmesh.ntris++; } } - + ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL); - + return true; } @@ -1170,11 +1344,11 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int rcAssert(ctx); ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL); - + int maxVerts = 0; int maxTris = 0; int maxMeshes = 0; - + for (int i = 0; i < nmeshes; ++i) { if (!meshes[i]) continue; @@ -1182,7 +1356,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int maxTris += meshes[i]->ntris; maxMeshes += meshes[i]->nmeshes; } - + mesh.nmeshes = 0; mesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*maxMeshes*4, RC_ALLOC_PERM); if (!mesh.meshes) @@ -1190,7 +1364,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4); return false; } - + mesh.ntris = 0; mesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris*4, RC_ALLOC_PERM); if (!mesh.tris) @@ -1198,7 +1372,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4); return false; } - + mesh.nverts = 0; mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM); if (!mesh.verts) @@ -1222,7 +1396,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int dst[3] = src[3]; mesh.nmeshes++; } - + for (int k = 0; k < dm->nverts; ++k) { rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]); @@ -1237,9 +1411,8 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int mesh.ntris++; } } - + ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL); return true; } - diff --git a/dep/recastnavigation/Recast/RecastRasterization.cpp b/dep/recastnavigation/Recast/Source/RecastRasterization.cpp index d2bb7c98f18..45a7d35bf3e 100644 --- a/dep/recastnavigation/Recast/RecastRasterization.cpp +++ b/dep/recastnavigation/Recast/Source/RecastRasterization.cpp @@ -95,7 +95,7 @@ static void addSpan(rcHeightfield& hf, const int x, const int y, s->area = area; s->next = 0; - // Empty cell, add he first span. + // Empty cell, add the first span. if (!hf.spans[idx]) { hf.spans[idx] = s; @@ -169,36 +169,64 @@ void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y, addSpan(hf, x,y, smin, smax, area, flagMergeThr); } -static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, float pd) +// divides a convex polygons into two convex polygons on both sides of a line +static void dividePoly(const float* in, int nin, + float* out1, int* nout1, + float* out2, int* nout2, + float x, int axis) { float d[12]; - for (int i = 0; i < n; ++i) - d[i] = pnx*in[i*3+0] + pnz*in[i*3+2] + pd; - - int m = 0; - for (int i = 0, j = n-1; i < n; j=i, ++i) + for (int i = 0; i < nin; ++i) + d[i] = x - in[i*3+axis]; + + int m = 0, n = 0; + for (int i = 0, j = nin-1; i < nin; j=i, ++i) { bool ina = d[j] >= 0; bool inb = d[i] >= 0; if (ina != inb) { float s = d[j] / (d[j] - d[i]); - out[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s; - out[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s; - out[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s; + out1[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s; + out1[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s; + out1[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s; + rcVcopy(out2 + n*3, out1 + m*3); m++; + n++; + // add the i'th point to the right polygon. Do NOT add points that are on the dividing line + // since these were already added above + if (d[i] > 0) + { + rcVcopy(out1 + m*3, in + i*3); + m++; + } + else if (d[i] < 0) + { + rcVcopy(out2 + n*3, in + i*3); + n++; + } } - if (inb) + else // same side { - out[m*3+0] = in[i*3+0]; - out[m*3+1] = in[i*3+1]; - out[m*3+2] = in[i*3+2]; - m++; + // add the i'th point to the right polygon. Addition is done even for points on the dividing line + if (d[i] >= 0) + { + rcVcopy(out1 + m*3, in + i*3); + m++; + if (d[i] != 0) + continue; + } + rcVcopy(out2 + n*3, in + i*3); + n++; } } - return m; + + *nout1 = m; + *nout2 = n; } + + static void rasterizeTri(const float* v0, const float* v1, const float* v2, const unsigned char area, rcHeightfield& hf, const float* bmin, const float* bmax, @@ -222,48 +250,57 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2, if (!overlapBounds(bmin, bmax, tmin, tmax)) return; - // Calculate the footpring of the triangle on the grid. - int x0 = (int)((tmin[0] - bmin[0])*ics); + // Calculate the footprint of the triangle on the grid's y-axis int y0 = (int)((tmin[2] - bmin[2])*ics); - int x1 = (int)((tmax[0] - bmin[0])*ics); int y1 = (int)((tmax[2] - bmin[2])*ics); - x0 = rcClamp(x0, 0, w-1); y0 = rcClamp(y0, 0, h-1); - x1 = rcClamp(x1, 0, w-1); y1 = rcClamp(y1, 0, h-1); // Clip the triangle into all grid cells it touches. - float in[7*3], out[7*3], inrow[7*3]; + float buf[7*3*4]; + float *in = buf, *inrow = buf+7*3, *p1 = inrow+7*3, *p2 = p1+7*3; + + rcVcopy(&in[0], v0); + rcVcopy(&in[1*3], v1); + rcVcopy(&in[2*3], v2); + int nvrow, nvIn = 3; for (int y = y0; y <= y1; ++y) { - // Clip polygon to row. - rcVcopy(&in[0], v0); - rcVcopy(&in[1*3], v1); - rcVcopy(&in[2*3], v2); - int nvrow = 3; + // Clip polygon to row. Store the remaining polygon as well const float cz = bmin[2] + y*cs; - nvrow = clipPoly(in, nvrow, out, 0, 1, -cz); - if (nvrow < 3) continue; - nvrow = clipPoly(out, nvrow, inrow, 0, -1, cz+cs); + dividePoly(in, nvIn, inrow, &nvrow, p1, &nvIn, cz+cs, 2); + rcSwap(in, p1); if (nvrow < 3) continue; + // find the horizontal bounds in the row + float minX = inrow[0], maxX = inrow[0]; + for (int i=1; i<nvrow; ++i) + { + if (minX > inrow[i*3]) minX = inrow[i*3]; + if (maxX < inrow[i*3]) maxX = inrow[i*3]; + } + int x0 = (int)((minX - bmin[0])*ics); + int x1 = (int)((maxX - bmin[0])*ics); + x0 = rcClamp(x0, 0, w-1); + x1 = rcClamp(x1, 0, w-1); + + int nv, nv2 = nvrow; + for (int x = x0; x <= x1; ++x) { - // Clip polygon to column. - int nv = nvrow; + // Clip polygon to column. store the remaining polygon as well const float cx = bmin[0] + x*cs; - nv = clipPoly(inrow, nv, out, 1, 0, -cx); - if (nv < 3) continue; - nv = clipPoly(out, nv, in, -1, 0, cx+cs); + dividePoly(inrow, nv2, p1, &nv, p2, &nv2, cx+cs, 0); + rcSwap(inrow, p2); if (nv < 3) continue; // Calculate min and max of the span. - float smin = in[1], smax = in[1]; + float smin = p1[1], smax = p1[1]; for (int i = 1; i < nv; ++i) { - smin = rcMin(smin, in[i*3+1]); - smax = rcMax(smax, in[i*3+1]); + smin = rcMin(smin, p1[i*3+1]); + smax = rcMax(smax, p1[i*3+1]); } smin -= bmin[1]; smax -= bmin[1]; diff --git a/dep/recastnavigation/Recast/RecastRegion.cpp b/dep/recastnavigation/Recast/Source/RecastRegion.cpp index 76e631cc5fb..38bc4ff476f 100644 --- a/dep/recastnavigation/Recast/RecastRegion.cpp +++ b/dep/recastnavigation/Recast/Source/RecastRegion.cpp @@ -286,7 +286,10 @@ static bool floodRegion(int x, int y, int i, if (nr & RC_BORDER_REG) // Do not take borders into account. continue; if (nr != 0 && nr != r) + { ar = nr; + break; + } const rcCompactSpan& as = chf.spans[ai]; @@ -300,7 +303,10 @@ static bool floodRegion(int x, int y, int i, continue; unsigned short nr2 = srcReg[ai2]; if (nr2 != 0 && nr2 != r) + { ar = nr2; + break; + } } } } @@ -309,6 +315,7 @@ static bool floodRegion(int x, int y, int i, srcReg[ci] = 0; continue; } + count++; // Expand neighbours. @@ -340,30 +347,44 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, rcCompactHeightfield& chf, unsigned short* srcReg, unsigned short* srcDist, unsigned short* dstReg, unsigned short* dstDist, - rcIntArray& stack) + rcIntArray& stack, + bool fillStack) { const int w = chf.width; const int h = chf.height; - // Find cells revealed by the raised level. - stack.resize(0); - for (int y = 0; y < h; ++y) + if (fillStack) { - for (int x = 0; x < w; ++x) + // Find cells revealed by the raised level. + stack.resize(0); + for (int y = 0; y < h; ++y) { - const rcCompactCell& c = chf.cells[x+y*w]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + for (int x = 0; x < w; ++x) { - if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA) + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { - stack.push(x); - stack.push(y); - stack.push(i); + if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA) + { + stack.push(x); + stack.push(y); + stack.push(i); + } } } } } - + else // use cells in the input stack + { + // mark all cells which already have a region + for (int j=0; j<stack.size(); j+=3) + { + int i = stack[j+2]; + if (srcReg[i] != 0) + stack[j+2] = -1; + } + } + int iter = 0; while (stack.size() > 0) { @@ -434,6 +455,61 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, } + +static void sortCellsByLevel(unsigned short startLevel, + rcCompactHeightfield& chf, + unsigned short* srcReg, + unsigned int nbStacks, rcIntArray* stacks, + unsigned short loglevelsPerStack) // the levels per stack (2 in our case) as a bit shift +{ + const int w = chf.width; + const int h = chf.height; + startLevel = startLevel >> loglevelsPerStack; + + for (unsigned int j=0; j<nbStacks; ++j) + stacks[j].resize(0); + + // put all cells in the level range into the appropriate stacks + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (chf.areas[i] == RC_NULL_AREA || srcReg[i] != 0) + continue; + + int level = chf.dist[i] >> loglevelsPerStack; + int sId = startLevel - level; + if (sId >= (int)nbStacks) + continue; + if (sId < 0) + sId = 0; + + stacks[sId].push(x); + stacks[sId].push(y); + stacks[sId].push(i); + } + } + } +} + + +static void appendStacks(rcIntArray& srcStack, rcIntArray& dstStack, + unsigned short* srcReg) +{ + for (int j=0; j<srcStack.size(); j+=3) + { + int i = srcStack[j+2]; + if ((i < 0) || (srcReg[i] != 0)) + continue; + dstStack.push(srcStack[j]); + dstStack.push(srcStack[j+1]); + dstStack.push(srcStack[j+2]); + } +} + struct rcRegion { inline rcRegion(unsigned short i) : @@ -441,7 +517,11 @@ struct rcRegion id(i), areaType(0), remap(false), - visited(false) + visited(false), + overlap(false), + connectsToBorder(false), + ymin(0xffff), + ymax(0) {} int spanCount; // Number of spans belonging to this region @@ -449,6 +529,9 @@ struct rcRegion unsigned char areaType; // Are type. bool remap; bool visited; + bool overlap; + bool connectsToBorder; + unsigned short ymin, ymax; rcIntArray connections; rcIntArray floors; }; @@ -693,10 +776,11 @@ static void walkContour(int x, int y, int i, int dir, } } -static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize, - unsigned short& maxRegionId, - rcCompactHeightfield& chf, - unsigned short* srcReg) + +static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize, + unsigned short& maxRegionId, + rcCompactHeightfield& chf, + unsigned short* srcReg, rcIntArray& overlaps) { const int w = chf.width; const int h = chf.height; @@ -705,7 +789,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP); if (!regions) { - ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg); + ctx->log(RC_LOG_ERROR, "mergeAndFilterRegions: Out of memory 'regions' (%d).", nreg); return false; } @@ -728,7 +812,6 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio rcRegion& reg = regions[r]; reg.spanCount++; - // Update floors. for (int j = (int)c.index; j < ni; ++j) { @@ -736,6 +819,8 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio unsigned short floorId = srcReg[j]; if (floorId == 0 || floorId >= nreg) continue; + if (floorId == r) + reg.overlap = true; addUniqueFloorRegion(reg, floorId); } @@ -831,7 +916,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio } } } - + // Merge too small regions to neighbour regions. int mergeCount = 0 ; do @@ -841,7 +926,9 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio { rcRegion& reg = regions[i]; if (reg.id == 0 || (reg.id & RC_BORDER_REG)) - continue; + continue; + if (reg.overlap) + continue; if (reg.spanCount == 0) continue; @@ -858,7 +945,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio { if (reg.connections[j] & RC_BORDER_REG) continue; rcRegion& mreg = regions[reg.connections[j]]; - if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue; + if (mreg.id == 0 || (mreg.id & RC_BORDER_REG) || mreg.overlap) continue; if (mreg.spanCount < smallest && canMergeWithRegion(reg, mreg) && canMergeWithRegion(mreg, reg)) @@ -928,6 +1015,224 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio if ((srcReg[i] & RC_BORDER_REG) == 0) srcReg[i] = regions[srcReg[i]].id; } + + // Return regions that we found to be overlapping. + for (int i = 0; i < nreg; ++i) + if (regions[i].overlap) + overlaps.push(regions[i].id); + + for (int i = 0; i < nreg; ++i) + regions[i].~rcRegion(); + rcFree(regions); + + + return true; +} + + +static void addUniqueConnection(rcRegion& reg, int n) +{ + for (int i = 0; i < reg.connections.size(); ++i) + if (reg.connections[i] == n) + return; + reg.connections.push(n); +} + +static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea, + unsigned short& maxRegionId, + rcCompactHeightfield& chf, + unsigned short* srcReg, rcIntArray& overlaps) +{ + const int w = chf.width; + const int h = chf.height; + + const int nreg = maxRegionId+1; + rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP); + if (!regions) + { + ctx->log(RC_LOG_ERROR, "mergeAndFilterLayerRegions: Out of memory 'regions' (%d).", nreg); + return false; + } + + // Construct regions + for (int i = 0; i < nreg; ++i) + new(®ions[i]) rcRegion((unsigned short)i); + + // Find region neighbours and overlapping regions. + rcIntArray lregs(32); + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + lregs.resize(0); + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + const unsigned short ri = srcReg[i]; + if (ri == 0 || ri >= nreg) continue; + rcRegion& reg = regions[ri]; + + reg.spanCount++; + + reg.ymin = rcMin(reg.ymin, s.y); + reg.ymax = rcMax(reg.ymax, s.y); + + // Collect all region layers. + lregs.push(ri); + + // Update neighbours + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + const unsigned short rai = srcReg[ai]; + if (rai > 0 && rai < nreg && rai != ri) + addUniqueConnection(reg, rai); + if (rai & RC_BORDER_REG) + reg.connectsToBorder = true; + } + } + + } + + // Update overlapping regions. + for (int i = 0; i < lregs.size()-1; ++i) + { + for (int j = i+1; j < lregs.size(); ++j) + { + if (lregs[i] != lregs[j]) + { + rcRegion& ri = regions[lregs[i]]; + rcRegion& rj = regions[lregs[j]]; + addUniqueFloorRegion(ri, lregs[j]); + addUniqueFloorRegion(rj, lregs[i]); + } + } + } + + } + } + + // Create 2D layers from regions. + unsigned short layerId = 1; + + for (int i = 0; i < nreg; ++i) + regions[i].id = 0; + + // Merge montone regions to create non-overlapping areas. + rcIntArray stack(32); + for (int i = 1; i < nreg; ++i) + { + rcRegion& root = regions[i]; + // Skip already visited. + if (root.id != 0) + continue; + + // Start search. + root.id = layerId; + + stack.resize(0); + stack.push(i); + + while (stack.size() > 0) + { + // Pop front + rcRegion& reg = regions[stack[0]]; + for (int j = 0; j < stack.size()-1; ++j) + stack[j] = stack[j+1]; + stack.resize(stack.size()-1); + + const int ncons = (int)reg.connections.size(); + for (int j = 0; j < ncons; ++j) + { + const int nei = reg.connections[j]; + rcRegion& regn = regions[nei]; + // Skip already visited. + if (regn.id != 0) + continue; + // Skip if the neighbour is overlapping root region. + bool overlap = false; + for (int k = 0; k < root.floors.size(); k++) + { + if (root.floors[k] == nei) + { + overlap = true; + break; + } + } + if (overlap) + continue; + + // Deepen + stack.push(nei); + + // Mark layer id + regn.id = layerId; + // Merge current layers to root. + for (int k = 0; k < regn.floors.size(); ++k) + addUniqueFloorRegion(root, regn.floors[k]); + root.ymin = rcMin(root.ymin, regn.ymin); + root.ymax = rcMax(root.ymax, regn.ymax); + root.spanCount += regn.spanCount; + regn.spanCount = 0; + root.connectsToBorder = root.connectsToBorder || regn.connectsToBorder; + } + } + + layerId++; + } + + // Remove small regions + for (int i = 0; i < nreg; ++i) + { + if (regions[i].spanCount > 0 && regions[i].spanCount < minRegionArea && !regions[i].connectsToBorder) + { + unsigned short reg = regions[i].id; + for (int j = 0; j < nreg; ++j) + if (regions[j].id == reg) + regions[j].id = 0; + } + } + + // Compress region Ids. + for (int i = 0; i < nreg; ++i) + { + regions[i].remap = false; + if (regions[i].id == 0) continue; // Skip nil regions. + if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions. + regions[i].remap = true; + } + + unsigned short regIdGen = 0; + for (int i = 0; i < nreg; ++i) + { + if (!regions[i].remap) + continue; + unsigned short oldId = regions[i].id; + unsigned short newId = ++regIdGen; + for (int j = i; j < nreg; ++j) + { + if (regions[j].id == oldId) + { + regions[j].id = newId; + regions[j].remap = false; + } + } + } + maxRegionId = regIdGen; + + // Remap regions. + for (int i = 0; i < chf.spanCount; ++i) + { + if ((srcReg[i] & RC_BORDER_REG) == 0) + srcReg[i] = regions[srcReg[i]].id; + } for (int i = 0; i < nreg; ++i) regions[i].~rcRegion(); @@ -936,6 +1241,8 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio return true; } + + /// @par /// /// This is usually the second to the last step in creating a fully built @@ -1181,13 +1488,17 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, } } + ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); - // Filter out small regions. + // Merge regions and filter out small regions. + rcIntArray overlaps; chf.maxRegions = id; - if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg)) + if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps)) return false; + // Monotone partitioning does not generate overlapping regions. + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); // Store the result out. @@ -1236,7 +1547,13 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, } ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); - + + const int LOG_NB_STACKS = 3; + const int NB_STACKS = 1 << LOG_NB_STACKS; + rcIntArray lvlStacks[NB_STACKS]; + for (int i=0; i<NB_STACKS; ++i) + lvlStacks[i].resize(1024); + rcIntArray stack(1024); rcIntArray visited(1024); @@ -1271,14 +1588,25 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, chf.borderSize = borderSize; } + int sId = -1; while (level > 0) { level = level >= 2 ? level-2 : 0; - + sId = (sId+1) & (NB_STACKS-1); + +// ctx->startTimer(RC_TIMER_DIVIDE_TO_LEVELS); + + if (sId == 0) + sortCellsByLevel(level, chf, srcReg, NB_STACKS, lvlStacks, 1); + else + appendStacks(lvlStacks[sId-1], lvlStacks[sId], srcReg); // copy left overs from last level + +// ctx->stopTimer(RC_TIMER_DIVIDE_TO_LEVELS); + ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND); // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) + if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); @@ -1289,18 +1617,15 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD); // Mark new regions with IDs. - for (int y = 0; y < h; ++y) + for (int j=0; j<lvlStacks[sId].size(); j+=3) { - for (int x = 0; x < w; ++x) + int x = lvlStacks[sId][j]; + int y = lvlStacks[sId][j+1]; + int i = lvlStacks[sId][j+2]; + if (i >= 0 && srcReg[i] == 0) { - const rcCompactCell& c = chf.cells[x+y*w]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) - { - if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA) - continue; - if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) - regionId++; - } + if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) + regionId++; } } @@ -1308,7 +1633,7 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, } // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) + if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack, true) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); @@ -1318,11 +1643,18 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); - // Filter out small regions. + // Merge regions and filter out smalle regions. + rcIntArray overlaps; chf.maxRegions = regionId; - if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg)) + if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps)) return false; - + + // If overlapping regions were found during merging, split those regions. + if (overlaps.size() > 0) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegions: %d overlapping regions.", overlaps.size()); + } + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); // Write the result out. @@ -1335,3 +1667,157 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, } +bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea) +{ + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_BUILD_REGIONS); + + const int w = chf.width; + const int h = chf.height; + unsigned short id = 1; + + rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); + if (!srcReg) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount); + return false; + } + memset(srcReg,0,sizeof(unsigned short)*chf.spanCount); + + const int nsweeps = rcMax(chf.width,chf.height); + rcScopedDelete<rcSweepSpan> sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP); + if (!sweeps) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps); + return false; + } + + + // Mark border regions. + if (borderSize > 0) + { + // Make sure border will not overflow. + const int bw = rcMin(w, borderSize); + const int bh = rcMin(h, borderSize); + // Paint regions + paintRectRegion(0, bw, 0, h, id|RC_BORDER_REG, chf, srcReg); id++; + paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++; + paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++; + paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++; + + chf.borderSize = borderSize; + } + + rcIntArray prev(256); + + // Sweep one line at a time. + for (int y = borderSize; y < h-borderSize; ++y) + { + // Collect spans from this row. + prev.resize(id+1); + memset(&prev[0],0,sizeof(int)*id); + unsigned short rid = 1; + + for (int x = borderSize; x < w-borderSize; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + if (chf.areas[i] == RC_NULL_AREA) continue; + + // -x + unsigned short previd = 0; + if (rcGetCon(s, 0) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(0); + const int ay = y + rcGetDirOffsetY(0); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); + if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai]) + previd = srcReg[ai]; + } + + if (!previd) + { + previd = rid++; + sweeps[previd].rid = previd; + sweeps[previd].ns = 0; + sweeps[previd].nei = 0; + } + + // -y + if (rcGetCon(s,3) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(3); + const int ay = y + rcGetDirOffsetY(3); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); + if (srcReg[ai] && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai]) + { + unsigned short nr = srcReg[ai]; + if (!sweeps[previd].nei || sweeps[previd].nei == nr) + { + sweeps[previd].nei = nr; + sweeps[previd].ns++; + prev[nr]++; + } + else + { + sweeps[previd].nei = RC_NULL_NEI; + } + } + } + + srcReg[i] = previd; + } + } + + // Create unique ID. + for (int i = 1; i < rid; ++i) + { + if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 && + prev[sweeps[i].nei] == (int)sweeps[i].ns) + { + sweeps[i].id = sweeps[i].nei; + } + else + { + sweeps[i].id = id++; + } + } + + // Remap IDs + for (int x = borderSize; x < w-borderSize; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (srcReg[i] > 0 && srcReg[i] < rid) + srcReg[i] = sweeps[srcReg[i]].id; + } + } + } + + + ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); + + // Merge monotone regions to layers and remove small regions. + rcIntArray overlaps; + chf.maxRegions = id; + if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg, overlaps)) + return false; + + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); + + + // Store the result out. + for (int i = 0; i < chf.spanCount; ++i) + chf.spans[i].reg = srcReg[i]; + + ctx->stopTimer(RC_TIMER_BUILD_REGIONS); + + return true; +} diff --git a/dep/recastnavigation/TODO.txt b/dep/recastnavigation/TODO.txt deleted file mode 100644 index b911c0e4720..00000000000 --- a/dep/recastnavigation/TODO.txt +++ /dev/null @@ -1,20 +0,0 @@ -TODO/Roadmap - -Summer/Autumn 2009 - -- Off mesh links (jump links) -- Area annotations -- Embed extra data per polygon -- Height conforming navmesh - - -Autumn/Winter 2009/2010 - -- Detour path following -- More dynamic example with tile navmesh -- Faster small tile process - - -More info at http://digestingduck.blogspot.com/2009/07/recast-and-detour-roadmap.html - -- diff --git a/dep/recastnavigation/recast_hotfix1.diff b/dep/recastnavigation/recast_hotfix1.diff deleted file mode 100644 index d370b0a68c8..00000000000 --- a/dep/recastnavigation/recast_hotfix1.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/dep/recastnavigation/Detour/DetourNavMesh.h b/dep/recastnavigation/Detour/DetourNavMesh.h -index 52d2c50..99e30c7 100644 ---- a/dep/recastnavigation/Detour/DetourNavMesh.h -+++ b/dep/recastnavigation/Detour/DetourNavMesh.h -@@ -21,7 +21,7 @@ - - #include "DetourAlloc.h" - --#ifdef WIN32 -+#if defined(WIN32) && !defined(__MINGW32__) - typedef unsigned __int64 uint64; - #else - #include <stdint.h> diff --git a/dep/recastnavigation/recast_hotfix2.diff b/dep/recastnavigation/recast_hotfix2.diff deleted file mode 100644 index cd73165dcb7..00000000000 --- a/dep/recastnavigation/recast_hotfix2.diff +++ /dev/null @@ -1,38 +0,0 @@ -diff --git a/dep/recastnavigation/Detour/DetourNavMesh.h b/dep/recastnavigation/Detour/DetourNavMesh.h -index c094e41..9c61a9b 100644 ---- a/dep/recastnavigation/Detour/DetourNavMesh.h -+++ b/dep/recastnavigation/Detour/DetourNavMesh.h -@@ -25,7 +25,8 @@ - - // Edited by TC - #if defined(WIN32) && !defined(__MINGW32__) --typedef unsigned __int64 uint64; -+/// Do not rename back to uint64. Otherwise mac complains about typedef redefinition -+typedef unsigned __int64 uint64_d; - #else - #include <stdint.h> - #ifndef uint64_t -@@ -33,7 +34,8 @@ typedef unsigned __int64 uint64; - #include <linux/types.h> - #endif - #endif --typedef uint64_t uint64; -+/// Do not rename back to uint64. Otherwise mac complains about typedef redefinition -+typedef uint64_t uint64_d; - #endif - - // Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef. -@@ -48,11 +50,11 @@ static const int STATIC_POLY_BITS = 31; - - /// A handle to a polygon within a navigation mesh tile. - /// @ingroup detour --typedef uint64 dtPolyRef; // Edited by TC -+typedef uint64_d dtPolyRef; // Edited by TC - - /// A handle to a tile within a navigation mesh. - /// @ingroup detour --typedef uint64 dtTileRef; // Edited by TC -+typedef uint64_d dtTileRef; // Edited by TC - - /// The maximum number of vertices per navigation polygon. - /// @ingroup detour diff --git a/dep/recastnavigation/recastnavigation.diff b/dep/recastnavigation/recastnavigation.diff new file mode 100644 index 00000000000..42bab6474e3 --- /dev/null +++ b/dep/recastnavigation/recastnavigation.diff @@ -0,0 +1,448 @@ +From b84e9ffbd1d1e1fb2f5d78cc53d2bb7b56c3fce3 Mon Sep 17 00:00:00 2001 +From: jackpoz <giacomopoz@gmail.com> +Date: Fri, 20 Jun 2014 23:15:04 +0200 +Subject: [PATCH] Add custom trinitycore changes + +--- + Detour/Include/DetourNavMesh.h | 76 ++++++++------------------ + Detour/Source/DetourCommon.cpp | 4 +- + Detour/Source/DetourNavMesh.cpp | 32 ++++------- + Detour/Source/DetourNavMeshBuilder.cpp | 6 +- + Detour/Source/DetourNavMeshQuery.cpp | 9 +-- + Detour/Source/DetourNode.cpp | 29 +++------- + DetourCrowd/Source/DetourObstacleAvoidance.cpp | 6 +- + Recast/Include/Recast.h | 8 +-- + 8 files changed, 59 insertions(+), 111 deletions(-) + +diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h +index 1060845..782ddbc 100644 +--- a/Detour/Include/DetourNavMesh.h ++++ b/Detour/Include/DetourNavMesh.h +@@ -22,39 +22,35 @@ + #include "DetourAlloc.h" + #include "DetourStatus.h" + +-// Undefine (or define in a build cofnig) the following line to use 64bit polyref. +-// Generally not needed, useful for very large worlds. +-// Note: tiles build using 32bit refs are not compatible with 64bit refs! +-//#define DT_POLYREF64 1 +- +-#ifdef DT_POLYREF64 +-// TODO: figure out a multiplatform version of uint64_t +-// - maybe: https://code.google.com/p/msinttypes/ +-// - or: http://www.azillionmonkeys.com/qed/pstdint.h ++ ++// Edited by TC ++#if defined(WIN32) && !defined(__MINGW32__) ++/// Do not rename back to uint64. Otherwise mac complains about typedef redefinition ++typedef unsigned __int64 uint64_d; ++#else + #include <stdint.h> ++#ifndef uint64_t ++#ifdef __linux__ ++#include <linux/types.h> ++#endif + #endif ++/// Do not rename back to uint64. Otherwise mac complains about typedef redefinition ++typedef uint64_t uint64_d; ++#endif + + // Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef. + // It is also recommended that you change dtHashRef() to a proper 64-bit hash. + ++// Edited by TC ++// We cannot have over 31 bits for either tile nor poly ++// without changing polyCount to use 64bits too. + /// A handle to a polygon within a navigation mesh tile. + /// @ingroup detour +-#ifdef DT_POLYREF64 +-static const unsigned int DT_SALT_BITS = 16; +-static const unsigned int DT_TILE_BITS = 28; +-static const unsigned int DT_POLY_BITS = 20; +-typedef uint64_t dtPolyRef; +-#else +-typedef unsigned int dtPolyRef; +-#endif ++typedef uint64_d dtPolyRef; // Edited by TC + + /// A handle to a tile within a navigation mesh. + /// @ingroup detour +-#ifdef DT_POLYREF64 +-typedef uint64_t dtTileRef; +-#else +-typedef unsigned int dtTileRef; +-#endif ++typedef uint64_d dtTileRef; // Edited by TC + + /// The maximum number of vertices per navigation polygon. + /// @ingroup detour +@@ -94,6 +90,12 @@ static const unsigned int DT_OFFMESH_CON_BIDIR = 1; + /// @ingroup detour + static const int DT_MAX_AREAS = 64; + ++static const int STATIC_SALT_BITS = 12; ++static const int STATIC_TILE_BITS = 21; ++static const int STATIC_POLY_BITS = 31; ++// we cannot have over 31 bits for either tile nor poly ++// without changing polyCount to use 64bits too. ++ + /// Tile flags used for various functions and fields. + /// For an example, see dtNavMesh::addTile(). + enum dtTileFlags +@@ -511,11 +513,7 @@ public: + /// @param[in] ip The index of the polygon within the tile. + inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const + { +-#ifdef DT_POLYREF64 +- return ((dtPolyRef)salt << (DT_POLY_BITS+DT_TILE_BITS)) | ((dtPolyRef)it << DT_POLY_BITS) | (dtPolyRef)ip; +-#else + return ((dtPolyRef)salt << (m_polyBits+m_tileBits)) | ((dtPolyRef)it << m_polyBits) | (dtPolyRef)ip; +-#endif + } + + /// Decodes a standard polygon reference. +@@ -527,21 +525,12 @@ public: + /// @see #encodePolyId + inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const + { +-#ifdef DT_POLYREF64 +- const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1; +- const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1; +- const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1; +- salt = (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask); +- it = (unsigned int)((ref >> DT_POLY_BITS) & tileMask); +- ip = (unsigned int)(ref & polyMask); +-#else + const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1; + const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1; + const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1; + salt = (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask); + it = (unsigned int)((ref >> m_polyBits) & tileMask); + ip = (unsigned int)(ref & polyMask); +-#endif + } + + /// Extracts a tile's salt value from the specified polygon reference. +@@ -550,13 +539,8 @@ public: + /// @see #encodePolyId + inline unsigned int decodePolyIdSalt(dtPolyRef ref) const + { +-#ifdef DT_POLYREF64 +- const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1; +- return (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask); +-#else + const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1; + return (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask); +-#endif + } + + /// Extracts the tile's index from the specified polygon reference. +@@ -565,13 +549,8 @@ public: + /// @see #encodePolyId + inline unsigned int decodePolyIdTile(dtPolyRef ref) const + { +-#ifdef DT_POLYREF64 +- const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1; +- return (unsigned int)((ref >> DT_POLY_BITS) & tileMask); +-#else + const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1; + return (unsigned int)((ref >> m_polyBits) & tileMask); +-#endif + } + + /// Extracts the polygon's index (within its tile) from the specified polygon reference. +@@ -580,13 +559,8 @@ public: + /// @see #encodePolyId + inline unsigned int decodePolyIdPoly(dtPolyRef ref) const + { +-#ifdef DT_POLYREF64 +- const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1; +- return (unsigned int)(ref & polyMask); +-#else + const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1; + return (unsigned int)(ref & polyMask); +-#endif + } + + /// @} +@@ -645,11 +619,9 @@ private: + dtMeshTile* m_nextFree; ///< Freelist of tiles. + dtMeshTile* m_tiles; ///< List of tiles. + +-#ifndef DT_POLYREF64 + unsigned int m_saltBits; ///< Number of salt bits in the tile ID. + unsigned int m_tileBits; ///< Number of tile bits in the tile ID. + unsigned int m_polyBits; ///< Number of poly bits in the tile ID. +-#endif + }; + + /// Allocates a navigation mesh object using the Detour allocator. +diff --git a/Detour/Source/DetourCommon.cpp b/Detour/Source/DetourCommon.cpp +index a98d8c8..b5700f5 100644 +--- a/Detour/Source/DetourCommon.cpp ++++ b/Detour/Source/DetourCommon.cpp +@@ -16,14 +16,14 @@ + // 3. This notice may not be removed or altered from any source distribution. + // + ++#include <math.h> + #include "DetourCommon.h" +-#include "DetourMath.h" + + ////////////////////////////////////////////////////////////////////////////////////////// + + float dtSqrt(float x) + { +- return dtMathSqrtf(x); ++ return sqrtf(x); + } + + void dtClosestPtPointTriangle(float* closest, const float* p, +diff --git a/Detour/Source/DetourNavMesh.cpp b/Detour/Source/DetourNavMesh.cpp +index 9d627be..5174050 100644 +--- a/Detour/Source/DetourNavMesh.cpp ++++ b/Detour/Source/DetourNavMesh.cpp +@@ -16,13 +16,13 @@ + // 3. This notice may not be removed or altered from any source distribution. + // + ++#include <math.h> + #include <float.h> + #include <string.h> + #include <stdio.h> + #include "DetourNavMesh.h" + #include "DetourNode.h" + #include "DetourCommon.h" +-#include "DetourMath.h" + #include "DetourAlloc.h" + #include "DetourAssert.h" + #include <new> +@@ -193,13 +193,11 @@ dtNavMesh::dtNavMesh() : + m_tileLutMask(0), + m_posLookup(0), + m_nextFree(0), +- m_tiles(0) ++ m_tiles(0), ++ m_saltBits(0), ++ m_tileBits(0), ++ m_polyBits(0) + { +-#ifndef DT_POLYREF64 +- m_saltBits = 0; +- m_tileBits = 0; +- m_polyBits = 0; +-#endif + memset(&m_params, 0, sizeof(dtNavMeshParams)); + m_orig[0] = 0; + m_orig[1] = 0; +@@ -250,17 +248,11 @@ dtStatus dtNavMesh::init(const dtNavMeshParams* params) + m_nextFree = &m_tiles[i]; + } + +- // Init ID generator values. +-#ifndef DT_POLYREF64 +- m_tileBits = dtIlog2(dtNextPow2((unsigned int)params->maxTiles)); +- m_polyBits = dtIlog2(dtNextPow2((unsigned int)params->maxPolys)); +- // Only allow 31 salt bits, since the salt mask is calculated using 32bit uint and it will overflow. +- m_saltBits = dtMin((unsigned int)31, 32 - m_tileBits - m_polyBits); +- +- if (m_saltBits < 10) +- return DT_FAILURE | DT_INVALID_PARAM; +-#endif +- ++ // Edited by TC ++ m_tileBits = STATIC_TILE_BITS; ++ m_polyBits = STATIC_POLY_BITS; ++ m_saltBits = STATIC_SALT_BITS; ++ + return DT_SUCCESS; + } + +@@ -1242,11 +1234,7 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz + tile->offMeshCons = 0; + + // Update salt, salt should never be zero. +-#ifdef DT_POLYREF64 +- tile->salt = (tile->salt+1) & ((1<<DT_SALT_BITS)-1); +-#else + tile->salt = (tile->salt+1) & ((1<<m_saltBits)-1); +-#endif + if (tile->salt == 0) + tile->salt++; + +diff --git a/Detour/Source/DetourNavMeshBuilder.cpp b/Detour/Source/DetourNavMeshBuilder.cpp +index 1bf271b..9d8471b 100644 +--- a/Detour/Source/DetourNavMeshBuilder.cpp ++++ b/Detour/Source/DetourNavMeshBuilder.cpp +@@ -16,13 +16,13 @@ + // 3. This notice may not be removed or altered from any source distribution. + // + ++#include <math.h> + #include <stdio.h> + #include <stdlib.h> + #include <string.h> + #include <float.h> + #include "DetourNavMesh.h" + #include "DetourCommon.h" +-#include "DetourMath.h" + #include "DetourNavMeshBuilder.h" + #include "DetourAlloc.h" + #include "DetourAssert.h" +@@ -202,8 +202,8 @@ static int createBVTree(const unsigned short* verts, const int /*nverts*/, + if (z > it.bmax[2]) it.bmax[2] = z; + } + // Remap y +- it.bmin[1] = (unsigned short)dtMathFloorf((float)it.bmin[1]*ch/cs); +- it.bmax[1] = (unsigned short)dtMathCeilf((float)it.bmax[1]*ch/cs); ++ it.bmin[1] = (unsigned short)floorf((float)it.bmin[1]*ch/cs); ++ it.bmax[1] = (unsigned short)ceilf((float)it.bmax[1]*ch/cs); + } + + int curNode = 0; +diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp +index 9debb4d..ec3a294 100644 +--- a/Detour/Source/DetourNavMeshQuery.cpp ++++ b/Detour/Source/DetourNavMeshQuery.cpp +@@ -16,13 +16,13 @@ + // 3. This notice may not be removed or altered from any source distribution. + // + ++#include <math.h> + #include <float.h> + #include <string.h> + #include "DetourNavMeshQuery.h" + #include "DetourNavMesh.h" + #include "DetourNode.h" + #include "DetourCommon.h" +-#include "DetourMath.h" + #include "DetourAlloc.h" + #include "DetourAssert.h" + #include <new> +@@ -99,8 +99,9 @@ inline float dtQueryFilter::getCost(const float* pa, const float* pb, + return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()]; + } + #endif +- +-static const float H_SCALE = 0.999f; // Search heuristic scale. ++ ++// Edited by TC ++static const float H_SCALE = 2.0f; // Search heuristic scale. + + + dtNavMeshQuery* dtAllocNavMeshQuery() +@@ -3504,7 +3505,7 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen + dtVsub(hitNormal, centerPos, hitPos); + dtVnormalize(hitNormal); + +- *hitDist = dtMathSqrtf(radiusSqr); ++ *hitDist = sqrtf(radiusSqr); + + return status; + } +diff --git a/Detour/Source/DetourNode.cpp b/Detour/Source/DetourNode.cpp +index 5cf6548..1d18977 100644 +--- a/Detour/Source/DetourNode.cpp ++++ b/Detour/Source/DetourNode.cpp +@@ -22,30 +22,17 @@ + #include "DetourCommon.h" + #include <string.h> + +-#ifdef DT_POLYREF64 +-// From Thomas Wang, https://gist.github.com/badboy/6267743 + inline unsigned int dtHashRef(dtPolyRef a) + { +- a = (~a) + (a << 18); // a = (a << 18) - a - 1; +- a = a ^ (a >> 31); +- a = a * 21; // a = (a + (a << 2)) + (a << 4); +- a = a ^ (a >> 11); +- a = a + (a << 6); +- a = a ^ (a >> 22); +- return (unsigned int)a; ++ // Edited by TC ++ a = (~a) + (a << 18); ++ a = a ^ (a >> 31); ++ a = a * 21; ++ a = a ^ (a >> 11); ++ a = a + (a << 6); ++ a = a ^ (a >> 22); ++ return (unsigned int)a; + } +-#else +-inline unsigned int dtHashRef(dtPolyRef a) +-{ +- a += ~(a<<15); +- a ^= (a>>10); +- a += (a<<3); +- a ^= (a>>6); +- a += ~(a<<11); +- a ^= (a>>16); +- return (unsigned int)a; +-} +-#endif + + ////////////////////////////////////////////////////////////////////////////////////////// + dtNodePool::dtNodePool(int maxNodes, int hashSize) : +diff --git a/DetourCrowd/Source/DetourObstacleAvoidance.cpp b/DetourCrowd/Source/DetourObstacleAvoidance.cpp +index 0fad9ef..d3f90b7 100644 +--- a/DetourCrowd/Source/DetourObstacleAvoidance.cpp ++++ b/DetourCrowd/Source/DetourObstacleAvoidance.cpp +@@ -18,10 +18,10 @@ + + #include "DetourObstacleAvoidance.h" + #include "DetourCommon.h" +-#include "DetourMath.h" + #include "DetourAlloc.h" + #include "DetourAssert.h" + #include <string.h> ++#include <math.h> + #include <float.h> + #include <new> + +@@ -58,7 +58,7 @@ static int isectRaySeg(const float* ap, const float* u, + dtVsub(v,bq,bp); + dtVsub(w,ap,bp); + float d = dtVperp2D(u,v); +- if (dtMathFabs(d) < 1e-6f) return 0; ++ if (fabsf(d) < 1e-6f) return 0; + d = 1.0f/d; + t = dtVperp2D(v,w) * d; + if (t < 0 || t > 1) return 0; +@@ -482,7 +482,7 @@ int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const flo + const int nd = dtClamp(ndivs, 1, DT_MAX_PATTERN_DIVS); + const int nr = dtClamp(nrings, 1, DT_MAX_PATTERN_RINGS); + const float da = (1.0f/nd) * DT_PI*2; +- const float dang = dtMathAtan2f(dvel[2], dvel[0]); ++ const float dang = atan2f(dvel[2], dvel[0]); + + // Always add sample at zero + pat[npat*2+0] = 0; +diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h +index 83ca606..66974cd 100644 +--- a/Recast/Include/Recast.h ++++ b/Recast/Include/Recast.h +@@ -243,7 +243,7 @@ struct rcConfig + }; + + /// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax. +-static const int RC_SPAN_HEIGHT_BITS = 13; ++static const int RC_SPAN_HEIGHT_BITS = 16; // EDITED BY TC + /// Defines the maximum value for rcSpan::smin and rcSpan::smax. + static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1; + +@@ -255,9 +255,9 @@ static const int RC_SPANS_PER_POOL = 2048; + /// @see rcHeightfield + struct rcSpan + { +- unsigned int smin : 13; ///< The lower limit of the span. [Limit: < #smax] +- unsigned int smax : 13; ///< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] +- unsigned int area : 6; ///< The area id assigned to the span. ++ unsigned int smin : 16; ///< The lower limit of the span. [Limit: < #smax] ++ unsigned int smax : 16; ///< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] ++ unsigned char area; ///< The area id assigned to the span. + rcSpan* next; ///< The next span higher up in column. + }; + +-- +1.9.0.msysgit.0 + |
