From 9d0de7be141830e2429d200ceae218abc0c208bb Mon Sep 17 00:00:00 2001 From: Max Qian Date: Thu, 19 Oct 2023 09:37:35 +0800 Subject: [PATCH] update libs --- libs/LICENSE.EMHASH8 | 21 + libs/cimg/CImg.h | 1515 +++++++++------- libs/cpp_httplib/httplib.cpp | 28 +- libs/cpp_httplib/httplib.h | 61 +- libs/emhash/hash_set8.hpp | 1524 +++++++++++++++++ libs/emhash/hash_table8.hpp | 467 ++--- libs/nlohmann/json.hpp | 93 +- libs/nlohmann/json_fwd.hpp | 176 -- .../parser/json/mapping/Deserializer.cpp | 13 +- libs/oatpp/parser/json/mapping/Serializer.cpp | 9 +- 10 files changed, 2820 insertions(+), 1087 deletions(-) create mode 100644 libs/LICENSE.EMHASH8 create mode 100644 libs/emhash/hash_set8.hpp delete mode 100644 libs/nlohmann/json_fwd.hpp diff --git a/libs/LICENSE.EMHASH8 b/libs/LICENSE.EMHASH8 new file mode 100644 index 00000000..4abcb494 --- /dev/null +++ b/libs/LICENSE.EMHASH8 @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 hyb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libs/cimg/CImg.h b/libs/cimg/CImg.h index ab754bd6..e04351c6 100644 --- a/libs/cimg/CImg.h +++ b/libs/cimg/CImg.h @@ -54,7 +54,7 @@ // Set version number of the library. #ifndef cimg_version -#define cimg_version 322 +#define cimg_version 332 /*----------------------------------------------------------- # @@ -121,6 +121,7 @@ #pragma warning(push) #pragma warning(disable:4127) #pragma warning(disable:4244) +#pragma warning(disable:4307) #pragma warning(disable:4311) #pragma warning(disable:4312) #pragma warning(disable:4319) @@ -132,6 +133,7 @@ #pragma warning(disable:4800) #pragma warning(disable:4804) #pragma warning(disable:4820) +#pragma warning(disable:4995) #pragma warning(disable:4996) #ifndef _CRT_SECURE_NO_DEPRECATE @@ -398,9 +400,9 @@ enum {FALSE_WIN = 0}; #if cimg_OS==0 #define cimg_display 0 #elif cimg_OS==1 -#define cimg_display 0 +#define cimg_display 1 #elif cimg_OS==2 -#define cimg_display 0 +#define cimg_display 2 #endif #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2) #error CImg Library: Configuration variable 'cimg_display' is badly defined. @@ -2441,9 +2443,14 @@ namespace cimg_library { } inline unsigned int openmp_mode(const unsigned int value, const bool is_set) { +#if cimg_use_openmp!=0 static unsigned int mode = 2; if (is_set) { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); } return mode; +#else + cimg::unused(value,is_set); + return 0; +#endif } //! Set current \CImg openmp mode. @@ -2693,6 +2700,7 @@ namespace cimg_library { static T min() { return ~max(); } static T max() { return (T)1<<(8*sizeof(T) - 1); } static T inf() { return max(); } + static T nan() { return inf(); } static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; } static const char* format() { return "%s"; } static const char* format_s() { return "%s"; } @@ -2711,6 +2719,7 @@ namespace cimg_library { static bool min() { return false; } static bool max() { return true; } static bool inf() { return max(); } + static bool nan() { return inf(); } static bool is_inf() { return false; } static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; } static const char* format() { return "%s"; } @@ -2727,6 +2736,7 @@ namespace cimg_library { static unsigned char min() { return 0; } static unsigned char max() { return (unsigned char)-1; } static unsigned char inf() { return max(); } + static unsigned char nan() { return inf(); } static unsigned char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } static const char* format() { return "%u"; } @@ -2744,6 +2754,7 @@ namespace cimg_library { static char min() { return 0; } static char max() { return (char)-1; } static char inf() { return max(); } + static char nan() { return inf(); } static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } static const char* format() { return "%u"; } @@ -2760,6 +2771,7 @@ namespace cimg_library { static char min() { return ~max(); } static char max() { return (char)((unsigned char)-1>>1); } static char inf() { return max(); } + static char nan() { return inf(); } static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; } static const char* format() { return "%d"; } static const char* format_s() { return "%d"; } @@ -2776,6 +2788,7 @@ namespace cimg_library { static signed char min() { return ~max(); } static signed char max() { return (signed char)((unsigned char)-1>>1); } static signed char inf() { return max(); } + static signed char nan() { return inf(); } static signed char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(signed char)val; } static const char* format() { return "%d"; } @@ -2792,6 +2805,7 @@ namespace cimg_library { static unsigned short min() { return 0; } static unsigned short max() { return (unsigned short)-1; } static unsigned short inf() { return max(); } + static unsigned short nan() { return inf(); } static unsigned short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; } static const char* format() { return "%u"; } @@ -2808,6 +2822,7 @@ namespace cimg_library { static short min() { return ~max(); } static short max() { return (short)((unsigned short)-1>>1); } static short inf() { return max(); } + static short nan() { return inf(); } static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; } static const char* format() { return "%d"; } static const char* format_s() { return "%d"; } @@ -2823,6 +2838,7 @@ namespace cimg_library { static unsigned int min() { return 0; } static unsigned int max() { return (unsigned int)-1; } static unsigned int inf() { return max(); } + static unsigned int nan() { return inf(); } static unsigned int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; } static const char* format() { return "%u"; } @@ -2839,6 +2855,7 @@ namespace cimg_library { static int min() { return ~max(); } static int max() { return (int)(~0U>>1); } static int inf() { return max(); } + static int nan() { return inf(); } static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; } static const char* format() { return "%d"; } static const char* format_s() { return "%d"; } @@ -2854,6 +2871,7 @@ namespace cimg_library { static cimg_uint64 min() { return 0; } static cimg_uint64 max() { return (cimg_uint64)-1; } static cimg_uint64 inf() { return max(); } + static cimg_uint64 nan() { return inf(); } static cimg_uint64 cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; } static const char* format() { return cimg_fuint64; } @@ -2870,6 +2888,7 @@ namespace cimg_library { static cimg_int64 min() { return ~max(); } static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); } static cimg_int64 inf() { return max(); } + static cimg_int64 nan() { return inf(); } static cimg_int64 cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val; } @@ -2878,7 +2897,8 @@ namespace cimg_library { static long format(const long val) { return (long)val; } }; -#if !(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))) +#if cimg_use_cpp11==1 && \ + (!(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX)))) template<> struct type { static const char* string() { static const char *const s = "uint64"; return s; } static bool is_float() { return false; } @@ -2888,6 +2908,7 @@ namespace cimg_library { static cimg_uint64 min() { return 0; } static cimg_uint64 max() { return (cimg_uint64)-1; } static cimg_uint64 inf() { return max(); } + static cimg_uint64 nan() { return inf(); } static cimg_uint64 cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; } static const char* format() { return cimg_fuint64; } @@ -2901,10 +2922,11 @@ namespace cimg_library { static bool is_inf(const cimg_int64) { return false; } static bool is_nan(const cimg_int64) { return false; } static bool is_finite(const cimg_int64) { return true; } - static cimg_int64 min() { return ~max(); } - static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); } - static cimg_int64 inf() { return max(); } - static cimg_int64 cut(const double val) { + static long long min() { return ~max(); } + static long long max() { return (cimg_int64)((cimg_uint64)-1>>1); } + static long long inf() { return max(); } + static long long nan() { return max(); } + static long long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val; } static const char* format() { return cimg_fint64; } @@ -3155,7 +3177,8 @@ namespace cimg_library { template<> struct superset { typedef double type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef double type; }; -#if !(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))) +#if cimg_use_cpp11==1 && \ + (!(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX)))) template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef cimg_int64 type; }; template<> struct superset { typedef cimg_int64 type; }; @@ -6358,7 +6381,10 @@ namespace cimg_library { **/ template inline T mod(const T& x, const T& m) { - if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); + if (!m) { + if (cimg::type::is_float()) return cimg::type::nan(); + else throw CImgArgumentException("cimg::mod(): Specified modulo value is 0."); + } const double dx = (double)x, dm = (double)m; if (!cimg::type::is_finite(dm)) return x; if (cimg::type::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm)); @@ -6983,7 +7009,7 @@ namespace cimg_library { //! Ellipsize a string. /** \param str C-string. - \param l Max number of characters. + \param l Max number of printed characters. \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string. **/ inline char *strellipsize(char *const str, const unsigned int l=64, @@ -7005,7 +7031,7 @@ namespace cimg_library { /** \param str C-string. \param res output C-string. - \param l Max number of characters. + \param l Max number of printed characters. String 'res' must be a size of at least 'l+1'. \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string. **/ inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64, @@ -7359,11 +7385,18 @@ namespace cimg_library { \param filename Specified filename to get size from. \return File size or '-1' if file does not exist. **/ - inline cimg_int64 fsize(const char *const filename) { - std::FILE *const file = cimg::std_fopen(filename,"rb"); + inline cimg_int64 fsize(std::FILE *const file) { if (!file) return (cimg_int64)-1; + const long pos = std::ftell(file); std::fseek(file,0,SEEK_END); const cimg_int64 siz = (cimg_int64)std::ftell(file); + std::fseek(file,pos,SEEK_SET); + return siz; + } + + inline cimg_int64 fsize(const char *const filename) { + std::FILE *const file = cimg::std_fopen(filename,"rb"); + const cimg_int64 siz = fsize(file); cimg::fclose(file); return siz; } @@ -13142,7 +13175,7 @@ namespace cimg_library { const unsigned int omode = cimg::exception_mode(); cimg::exception_mode(0); try { - _fill(expression,true,3,0,0,"operator=",0,0); + _fill(expression,true,3,0,"operator=",0,0); } catch (CImgException&) { cimg::exception_mode(omode); load(expression); @@ -13217,7 +13250,7 @@ namespace cimg_library { instead of assigning them. **/ CImg& operator+=(const char *const expression) { - return *this+=(+*this)._fill(expression,true,3,0,0,"operator+=",this,0); + return *this+=(+*this)._fill(expression,true,3,0,"operator+=",this,0); } //! In-place addition operator. @@ -13338,7 +13371,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a subtraction instead of an addition. **/ CImg& operator-=(const char *const expression) { - return *this-=(+*this)._fill(expression,true,3,0,0,"operator-=",this,0); + return *this-=(+*this)._fill(expression,true,3,0,"operator-=",this,0); } //! In-place subtraction operator. @@ -13442,7 +13475,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a multiplication instead of an addition. **/ CImg& operator*=(const char *const expression) { - return mul((+*this)._fill(expression,true,3,0,0,"operator*=",this,0)); + return mul((+*this)._fill(expression,true,3,0,"operator*=",this,0)); } //! In-place multiplication operator. @@ -13707,7 +13740,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a division instead of an addition. **/ CImg& operator/=(const char *const expression) { - return div((+*this)._fill(expression,true,3,0,0,"operator/=",this,0)); + return div((+*this)._fill(expression,true,3,0,"operator/=",this,0)); } //! In-place division operator. @@ -13771,7 +13804,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition. **/ CImg& operator%=(const char *const expression) { - return *this%=(+*this)._fill(expression,true,3,0,0,"operator%=",this,0); + return *this%=(+*this)._fill(expression,true,3,0,"operator%=",this,0); } //! In-place modulo operator. @@ -13837,7 +13870,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition. **/ CImg& operator&=(const char *const expression) { - return *this&=(+*this)._fill(expression,true,3,0,0,"operator&=",this,0); + return *this&=(+*this)._fill(expression,true,3,0,"operator&=",this,0); } //! In-place bitwise AND operator. @@ -13903,7 +13936,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition. **/ CImg& operator|=(const char *const expression) { - return *this|=(+*this)._fill(expression,true,3,0,0,"operator|=",this,0); + return *this|=(+*this)._fill(expression,true,3,0,"operator|=",this,0); } //! In-place bitwise OR operator. @@ -13973,7 +14006,7 @@ namespace cimg_library { - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead. **/ CImg& operator^=(const char *const expression) { - return *this^=(+*this)._fill(expression,true,3,0,0,"operator^=",this,0); + return *this^=(+*this)._fill(expression,true,3,0,"operator^=",this,0); } //! In-place bitwise XOR operator. @@ -14041,7 +14074,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition. **/ CImg& operator<<=(const char *const expression) { - return *this<<=(+*this)._fill(expression,true,3,0,0,"operator<<=",this,0); + return *this<<=(+*this)._fill(expression,true,3,0,"operator<<=",this,0); } //! In-place bitwise left shift operator. @@ -14108,7 +14141,7 @@ namespace cimg_library { Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition. **/ CImg& operator>>=(const char *const expression) { - return *this>>=(+*this)._fill(expression,true,3,0,0,"operator>>=",this,0); + return *this>>=(+*this)._fill(expression,true,3,0,"operator>>=",this,0); } //! In-place bitwise right shift operator. @@ -14190,7 +14223,7 @@ namespace cimg_library { \param expression Value string describing the way pixel values are compared. **/ bool operator==(const char *const expression) const { - return *this==(+*this)._fill(expression,true,3,0,0,"operator==",this,0); + return *this==(+*this)._fill(expression,true,3,0,"operator==",this,0); } //! Test if two images have the same size and values. @@ -16680,17 +16713,24 @@ namespace cimg_library { // Check instance dimension and header. if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d has invalid dimensions (%u,%u,%u,%u)", - _width,_height,_depth,_spectrum); + "CImg3d has invalid dimensions (%u,%u,%u,%u)", + _width,_height,_depth,_spectrum); return false; } const T *ptrs = _data, *const ptre = end(); if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') || !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d header not found"); + "CImg3d header not found"); + return false; + } + if (!cimg::type::is_finite(*ptrs) || !cimg::type::is_finite(ptrs[1])) { + if (error_message) cimg_snprintf(error_message,256, + "Specified numbers of vertices/primitives (%g/%g) are invalid", + (double)*ptrs,(double)ptrs[1]); return false; } + const unsigned int nb_points = cimg::float2uint((float)*(ptrs++)), nb_primitives = cimg::float2uint((float)*(ptrs++)); @@ -16700,8 +16740,9 @@ namespace cimg_library { const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives; if (_data + minimal_size>ptre) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected", - nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size); + "CImg3d (%u,%u) has only %lu values, " + "while at least %lu values were expected", + nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size); return false; } } @@ -16710,23 +16751,23 @@ namespace cimg_library { if (!nb_points) { if (nb_primitives) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) defines no vertices but %u primitives", - nb_points,nb_primitives,nb_primitives); + "CImg3d (%u,%u) defines no vertices but %u primitives", + nb_points,nb_primitives,nb_primitives); return false; } if (ptrs!=ptre) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) is an empty object but contains %u value%s " - "more than expected", - nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":""); + "CImg3d (%u,%u) is an empty object but contains %u value%s " + "more than expected", + nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":""); return false; } return true; } if (ptrs + 3*nb_points>ptre) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) defines only %u vertices data", - nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3); + "CImg3d (%u,%u) defines only %u vertices data", + nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3); return false; } ptrs+=3*nb_points; @@ -16734,8 +16775,8 @@ namespace cimg_library { // Check consistency of primitive data. if (ptrs==ptre) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) defines %u vertices but no primitive", - nb_points,nb_primitives,nb_points); + "CImg3d (%u,%u) defines %u vertices but no primitive", + nb_points,nb_primitives,nb_points); return false; } @@ -16748,8 +16789,8 @@ namespace cimg_library { const unsigned int i0 = cimg::float2uint((float)*(ptrs++)); if (i0>=nb_points) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]", - nb_points,nb_primitives,i0,p); + "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]", + nb_points,nb_primitives,i0,p); return false; } } break; @@ -16760,9 +16801,9 @@ namespace cimg_library { ptrs+=3; if (i0>=nb_points || i1>=nb_points) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " - "sphere primitive [%u]", - nb_points,nb_primitives,i0,i1,p); + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " + "sphere primitive [%u]", + nb_points,nb_primitives,i0,i1,p); return false; } } break; @@ -16773,9 +16814,9 @@ namespace cimg_library { if (nb_inds==6) ptrs+=4; if (i0>=nb_points || i1>=nb_points) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " - "segment primitive [%u]", - nb_points,nb_primitives,i0,i1,p); + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " + "segment primitive [%u]", + nb_points,nb_primitives,i0,i1,p); return false; } } break; @@ -16787,9 +16828,9 @@ namespace cimg_library { if (nb_inds==9) ptrs+=6; if (i0>=nb_points || i1>=nb_points || i2>=nb_points) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " - "triangle primitive [%u]", - nb_points,nb_primitives,i0,i1,i2,p); + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " + "triangle primitive [%u]", + nb_points,nb_primitives,i0,i1,i2,p); return false; } } break; @@ -16802,23 +16843,23 @@ namespace cimg_library { if (nb_inds==12) ptrs+=8; if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " - "quadrangle primitive [%u]", - nb_points,nb_primitives,i0,i1,i2,i3,p); + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " + "quadrangle primitive [%u]", + nb_points,nb_primitives,i0,i1,i2,i3,p); return false; } } break; default : if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u", - nb_points,nb_primitives,p,nb_inds); + "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u", + nb_points,nb_primitives,p,nb_inds); return false; } if (ptrs>ptre) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], " - "%u values missing", - nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre)); + "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], " + "%u values missing", + nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre)); return false; } } @@ -16826,8 +16867,8 @@ namespace cimg_library { // Check consistency of color data. if (ptrs==ptre) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) defines no color/texture data", - nb_points,nb_primitives); + "CImg3d (%u,%u) defines no color/texture data", + nb_points,nb_primitives); return false; } for (unsigned int c = 0; c=c) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u " - "for primitive [%u]", - nb_points,nb_primitives,w,c); + "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u " + "for primitive [%u]", + nb_points,nb_primitives,w,c); return false; } } else ptrs+=w*h*s; } if (ptrs>ptre) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], " - "%u values missing", - nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre)); + "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], " + "%u values missing", + nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre)); return false; } } @@ -16859,8 +16900,8 @@ namespace cimg_library { // Check consistency of opacity data. if (ptrs==ptre) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) defines no opacity data", - nb_points,nb_primitives); + "CImg3d (%u,%u) defines no opacity data", + nb_points,nb_primitives); return false; } for (unsigned int o = 0; o=o) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) refers to invalid shared opacity index %u " - "for primitive [%u]", - nb_points,nb_primitives,w,o); + "CImg3d (%u,%u) refers to invalid shared opacity index %u " + "for primitive [%u]", + nb_points,nb_primitives,w,o); return false; } } else ptrs+=w*h*s; } if (ptrs>ptre) { if (error_message) cimg_snprintf(error_message,256, - "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]", - nb_points,nb_primitives,o); + "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]", + nb_points,nb_primitives,o); return false; } } @@ -16890,8 +16931,8 @@ namespace cimg_library { // Check end of data. if (ptrs1?"s":""); + "CImg3d (%u,%u) contains %u value%s more than expected", + nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":""); return false; } return true; @@ -16933,7 +16974,7 @@ namespace cimg_library { unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent, result_dim, result_end_dim, break_type, constcache_size; - bool is_parallelizable, is_noncritical_run, is_end_code, is_fill, return_new_comp, need_input_copy; + bool is_parallelizable, is_noncritical_run, is_end_code, is_fill, need_input_copy, return_new_comp; double *result, *result_end; cimg_uint64 rng; const char *const calling_function, *s_op, *ss_op; @@ -17110,7 +17151,8 @@ namespace cimg_library { imgin(CImg::const_empty()),imgout(CImg::empty()),imglist(CImgList::empty()), img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0), result_dim(0),result_end_dim(0),break_type(0),constcache_size(0),is_parallelizable(true), - is_noncritical_run(false),is_fill(false),need_input_copy(false),result_end(0),rng(0),calling_function(0) { + is_noncritical_run(false),is_fill(false),need_input_copy(false), + result_end(0),rng(0),calling_function(0) { mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()() result = mem._data; } @@ -17192,7 +17234,12 @@ namespace cimg_library { // Bits of 'block_flags' tell about in which code block we currently are: // 0: critical(), 1: begin(), 2: begin_t(), 3: end(), 4: end_t(). - const bool is_inside_critical = (bool)(block_flags&1); + const bool + is_inside_critical = (bool)(block_flags&1), + is_inside_begin = (bool)(block_flags&2), + is_inside_begin_t = (bool)(block_flags&4), + is_inside_end = (bool)(block_flags&8), + is_inside_end_t = (bool)(block_flags&16); // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value // linked to the returned memory slot (reference that cannot be determined at compile time). @@ -17312,22 +17359,23 @@ namespace cimg_library { _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 21,0,0); } switch (*ss1) { - case 'm' : arg1 = 4; arg2 = 0; break; // im - case 'M' : arg1 = 5; arg2 = 1; break; // iM case 'a' : arg1 = 6; arg2 = 2; break; // ia - case 'v' : arg1 = 7; arg2 = 3; break; // iv - case 'd' : arg1 = 8; arg2 = 3; break; // id - case 's' : arg1 = 9; arg2 = 12; break; // is - case 'p' : arg1 = 10; arg2 = 13; break; // ip case 'c' : // ic if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]); if (mem_img_median==~0U) mem_img_median = imgin?const_scalar(imgin.median()):0; _cimg_mp_return(mem_img_median); break; + case 'd' : arg1 = 8; arg2 = 3; break; // id + case 'm' : arg1 = 4; arg2 = 0; break; // im + case 'M' : arg1 = 5; arg2 = 1; break; // iM case 'n' : // in if (reserved_label[12]!=~0U) _cimg_mp_return(reserved_label[12]); - if (mem_img_norm==~0U) mem_img_norm = imgin?const_scalar(imgin.magnitude()):0; + if (mem_img_norm==~0U) mem_img_norm = imgin?const_scalar(imgin.magnitude(2)):0; _cimg_mp_return(mem_img_norm); + break; + case 'p' : arg1 = 10; arg2 = 13; break; // ip + case 's' : arg1 = 9; arg2 = 12; break; // is + case 'v' : arg1 = 7; arg2 = 3; break; // iv } } else if (*ss1=='m') switch (*ss) { @@ -19265,7 +19313,6 @@ namespace cimg_library { _cimg_mp_op("Function 'begin()'"); s1 = ss6; while (s1 Error too much arguments + if (p2>4) { + *s1 = 0; s1 = s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Argument '%s' is a vector of size %u (should be <=4), " + "in expression '%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,s1,p2,s0); + } + arg1 = pos + 1; + arg2 = p2>1?pos + 2:0; + arg3 = p2>2?pos + 3:0; + arg4 = p2>3?pos + 4:0; + } else { // Coordinates specified as scalars + arg1 = pos; arg2 = arg3 = arg4 = 0; + if (s1::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); - for (s = s0; s::vector((ulongT)op,pos,0).move_to(l_opcode); break; - case 1 : op = mp_norm1; CImg::vector((ulongT)op,pos,0).move_to(l_opcode); break; - case 2 : op = mp_norm2; CImg::vector((ulongT)op,pos,0).move_to(l_opcode); break; - case ~0U : op = mp_norminf; CImg::vector((ulongT)op,pos,0).move_to(l_opcode); break; - default : op = mp_normp; CImg::vector((ulongT)op,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)). - move_to(l_opcode); - } - for ( ; s::vector(0,0,0,arg1).move_to(l_opcode); + for (++s; s::sequence(_cimg_mp_size(arg2),arg2 + 1, - arg2 + (ulongT)_cimg_mp_size(arg2)). + CImg::sequence(_cimg_mp_size(arg2),arg2 + 1,arg2 + (ulongT)_cimg_mp_size(arg2)). move_to(l_opcode); else CImg::vector(arg2).move_to(l_opcode); is_sth&=_cimg_mp_is_const_scalar(arg2); s = ns; } - (l_opcode>'y').move_to(opcode); + op = val==2?_mp_vector_norm2:val==1?_mp_vector_norm1:!val?_mp_vector_norm0: + cimg::type::is_inf(val)?_mp_vector_norminf:_mp_vector_normp; + opcode[0] = (ulongT)op; opcode[2] = opcode._height; if (is_sth) _cimg_mp_const_scalar(op(*this)); - if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1 - _cimg_mp_scalar1(mp_abs,opcode[3]); + if (opcode._height==5) { // Single argument + if (arg1) { _cimg_mp_scalar1(mp_abs,opcode[4]); } + else { _cimg_mp_scalar2(mp_neq,opcode[4],0); } + } + opcode[1] = pos = scalar(); opcode.move_to(code); return_new_comp = true; _cimg_mp_return(pos); } break; + case 'o' : + if (!std::strncmp(ss,"o2c(",4)) { // Offset to coordinates + _cimg_mp_op("Function 'o2c()'"); + if (*ss4=='#') { // Index specified + s0 = ss5; while (s0::vector((ulongT)mp_o2c,pos,p1,arg1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + case 'p' : if (!std::strncmp(ss,"permut(",7)) { // Number of permutations _cimg_mp_op("Function 'permut()'"); @@ -21814,10 +21921,7 @@ namespace cimg_library { #ifdef cimg_mp_func_run if (!std::strncmp(ss,"run(",4)) { // Run external command _cimg_mp_op("Function 'run()'"); - const bool is_inside_begin = (bool)(block_flags&2), is_inside_end = (bool)(block_flags&8); - if (!is_inside_critical && !is_inside_begin && !is_inside_end) { - is_parallelizable = false; is_noncritical_run = true; - } + if (!is_inside_critical && !is_inside_begin && !is_inside_end) is_noncritical_run = true; CImg::vector((ulongT)mp_run,0,0).move_to(l_opcode); pos = 1; for (s = ss4; s0) pos = is_comp_vector(arg1)?arg1:((return_new_comp = true), vector(p1)); + else { + pos = scalar(); + if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) { + val = mem[arg1]; + _cimg_mp_const_scalar(val?(mem[arg2]?1:val):0); + } + } + CImg::vector((ulongT)mp_vector_unitnorm,pos,arg1,p1,arg2).move_to(code); + _cimg_mp_return(pos); + } + if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable _cimg_mp_op("Function 'unref()'"); arg1=~0U; @@ -22943,26 +23067,36 @@ namespace cimg_library { if (!list_median[p1]) CImg::vector(imglist[p1].median()).move_to(list_median[p1]); _cimg_mp_const_scalar(*list_median[p1]); } - _cimg_mp_scalar1(mp_list_median,arg1); + _cimg_mp_scalar1(mp_list_id,arg1); + } + + if (*ss1=='d') { // id#ind + if (!imglist) _cimg_mp_return(0); + if (_cimg_mp_is_const_scalar(arg1)) { + if (!list_stats) list_stats.assign(imglist._width); + if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(imglist[p1].get_stats(),false); + _cimg_mp_const_scalar(std::sqrt(list_stats(p1,3))); + } + _cimg_mp_scalar1(mp_list_id,arg1); } if (*ss1=='n') { // in#ind if (!imglist) _cimg_mp_return(0); if (_cimg_mp_is_const_scalar(arg1)) { if (!list_norm) list_norm.assign(imglist._width); - if (!list_norm[p1]) CImg::vector(imglist[p1].magnitude()).move_to(list_norm[p1]); + if (!list_norm[p1]) CImg::vector(imglist[p1].magnitude(2)).move_to(list_norm[p1]); _cimg_mp_const_scalar(*list_norm[p1]); } _cimg_mp_scalar1(mp_list_norm,arg1); } switch (*ss1) { + case 'a' : arg2 = 2; break; // ia#ind case 'm' : arg2 = 0; break; // im#ind case 'M' : arg2 = 1; break; // iM#ind - case 'a' : arg2 = 2; break; // ia#ind - case 'v' : arg2 = 3; break; // iv#ind - case 's' : arg2 = 12; break; // is#ind case 'p' : arg2 = 13; break; // ip#ind + case 's' : arg2 = 12; break; // is#ind + case 'v' : arg2 = 3; break; // iv#ind } } else if (*ss1=='m') switch (*ss) { case 'x' : arg2 = 4; break; // xm#ind @@ -24097,30 +24231,18 @@ namespace cimg_library { return cimg::type::nan(); } -#ifdef cimg_mp_func_run - static double mp_run(_cimg_math_parser& mp) { - const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; - CImgList _str; - CImg it; - for (unsigned int n = 0; n string - const double *ptr = &_mp_arg(3 + 2*n) + 1; - unsigned int l = 0; - while (l(ptr,l,1,1,1,true).move_to(_str); - } else { // Scalar argument -> number - it.assign(24); - cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); - CImg::string(it,false,true).move_to(_str); - } - } - CImg(1,1,1,1,0).move_to(_str); - CImg str = _str>'x'; - cimg_mp_func_run(str._data); - return cimg::type::nan(); + static double mp_c2o(_cimg_math_parser& mp) { + mp_check_list(mp,"c2o"); + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + const CImg &img = ind==~0U?mp.imgin:mp.imglist[ind]; + const int + x = (int)_mp_arg(3), + y = (int)_mp_arg(4), + z = (int)_mp_arg(5), + c = (int)_mp_arg(6); + return (double)img.offset(x,y,z,c); } -#endif static double mp_cbrt(_cimg_math_parser& mp) { return cimg::cbrt(_mp_arg(2)); @@ -24482,7 +24604,7 @@ namespace cimg_library { mp.imgout.pixel_type(),s_op,ind); double ret = cimg::type::nan(); - if (dim<1) ret = img[siz - 1]; // Scalar element + if (dim==1) ret = img[siz - 1]; // Scalar element else cimg_forC(img,c) ptrd[c] = img(0,siz - 1,0,c); // Vector element if (is_pop) { // Remove element from array --siz; @@ -24824,10 +24946,7 @@ namespace cimg_library { mp_check_list(mp,"ellipse"); const unsigned int i_end = (unsigned int)mp.opcode[2]; unsigned int ind = (unsigned int)mp.opcode[3]; - if (ind!=~0U) { - if (!mp.imglist.width()) return cimg::type::nan(); - ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width()); - } + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width()); CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; CImg color(img._spectrum,1,1,1,0); bool is_invalid_arguments = false, is_outlined = false; @@ -24888,9 +25007,11 @@ namespace cimg_library { return (double)(_mp_arg(2)==_mp_arg(3)); } +#if cimg_use_cpp11==1 static double mp_erf(_cimg_math_parser& mp) { return std::erf(_mp_arg(2)); } +#endif static double mp_erfinv(_cimg_math_parser& mp) { return cimg::erfinv(_mp_arg(2)); @@ -25293,7 +25414,7 @@ namespace cimg_library { ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); } const CImg &img = ind==~0U?mp.imgout:mp.imglist[ind]; - return (double)img.magnitude(); + return (double)img.magnitude(2); } static double mp_image_print(_cimg_math_parser& mp) { @@ -26098,10 +26219,27 @@ namespace cimg_library { static double mp_list_norm(_cimg_math_parser& mp) { const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); if (!mp.list_norm) mp.list_norm.assign(mp.imglist._width); - if (!mp.list_norm[ind]) CImg::vector(mp.imglist[ind].magnitude()).move_to(mp.list_norm[ind]); + if (!mp.list_norm[ind]) CImg::vector(mp.imglist[ind].magnitude(2)).move_to(mp.list_norm[ind]); return *mp.list_norm[ind]; } + static double mp_list_id(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + bool get_stats = false; + cimg::mutex(13); + if (!mp.list_stats || mp.list_stats.size()!=mp.imglist._width) mp.list_stats.assign(mp.imglist._width); + if (!mp.list_stats[ind]) get_stats = true; + cimg::mutex(13,0); + + if (get_stats) { + CImg st = mp.imglist[ind].get_stats(); + cimg::mutex(13); + st.move_to(mp.list_stats[ind]); + cimg::mutex(13,0); + } + return std::sqrt(mp.list_stats(ind,3)); + } + static double mp_list_set_ioff(_cimg_math_parser& mp) { if (!mp.imglist.width()) return cimg::type::nan(); const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); @@ -26969,65 +27107,25 @@ namespace cimg_library { return (double)(_mp_arg(2)!=_mp_arg(3)); } - static double mp_norm0(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - switch (i_end - 3) { - case 1 : return _mp_arg(3)!=0; - case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0); - } - double res = 0; - for (unsigned int i = 3; ires) res = val; + static double mp_o2c(_cimg_math_parser& mp) { + mp_check_list(mp,"o2c"); + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()); + const CImg &img = ind==~0U?mp.imgin:mp.imglist[ind]; + longT offset = (longT)_mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + if (!img) + ptrd[0] = ptrd[1] = ptrd[2] = ptrd[3] = cimg::type::nan(); + else { + *(ptrd++) = (double)(offset%img.width()); + offset/=img.width(); + *(ptrd++) = (double)(offset%img.height()); + offset/=img.height(); + *(ptrd++) = (double)(offset%img.depth()); + offset/=img.depth(); + *ptrd = (double)(offset%img.spectrum()); } - return res; - } - - static double mp_normp(_cimg_math_parser& mp) { - const unsigned int i_end = (unsigned int)mp.opcode[2]; - if (i_end==4) return cimg::abs(_mp_arg(3)); - const double p = (double)mp.opcode[3]; - double res = 0; - for (unsigned int i = 4; i0?res:0.; + return cimg::type::nan(); } static double mp_permutations(_cimg_math_parser& mp) { @@ -27140,7 +27238,7 @@ namespace cimg_library { } static double mp_repeat(_cimg_math_parser& mp) { - const double nb_it = _mp_arg(2); + const double nb_it = _mp_arg(2), nb_itm1 = nb_it - 1; double *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0, *const ptrs = &_mp_arg(1); @@ -27148,13 +27246,13 @@ namespace cimg_library { *const p_body = ++mp.p_code, *const p_end = p_body + mp.opcode[4]; - if (nb_it>0) { + if (nb_it>=1) { const unsigned int _break_type = mp.break_type; mp.break_type = 0; double it = 0; if (ptrc) { // Version with loop variable (3 arguments) - while (it_data; @@ -27166,7 +27264,7 @@ namespace cimg_library { } *ptrc = it; } else // Version without loop variable (2 arguments) - while (it_data; const ulongT target = mp.opcode[1]; @@ -27218,6 +27316,36 @@ namespace cimg_library { return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4)); } +#ifdef cimg_mp_func_run + static double mp_run(_cimg_math_parser& mp) { + const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(3 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(24); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + CImg(1,1,1,1,0).move_to(_str); + CImg str = _str>'x'; +#if cimg_use_openmp==0 + const unsigned int n_thread = 0; +#else + const unsigned int n_thread = omp_get_thread_num(); +#endif + cimg_mp_func_run(str._data,n_thread && mp.is_noncritical_run); + return cimg::type::nan(); + } +#endif + static double mp_self_add(_cimg_math_parser& mp) { return _mp_arg(1)+=_mp_arg(2); } @@ -28020,6 +28148,75 @@ namespace cimg_library { return !mp_vector_eq(mp); } + static double _mp_vector_norm0(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[2]; + double res = 0; + for (unsigned int i = siz - 1; i>3; --i) res+=(double)(_mp_arg(i)?1:0); + return res; + } + + static double _mp_vector_norm1(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[2]; + double res = 0; + for (unsigned int i = siz - 1; i>3; --i) res+=(double)cimg::abs(_mp_arg(i)); + return res; + } + + static double _mp_vector_norm2(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[2]; + double res = 0; + for (unsigned int i = siz - 1; i>3; --i) res+=(double)cimg::sqr(_mp_arg(i)); + return (double)std::sqrt(res); + } + + static double _mp_vector_norminf(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[2]; + double res = 0; + for (unsigned int i = siz - 1; i>3; --i) { + const double val = (double)cimg::abs(_mp_arg(i)); + if (val>res) res = val; + } + return res; + } + + static double _mp_vector_normp(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[2]; + const double p = _mp_arg(3); + double res = 0; + for (unsigned int i = siz - 1; i>3; --i) res+=(double)std::pow(cimg::abs(_mp_arg(i)),p); + res = (double)std::pow(res,1.0/p); + return res; + } + + static double mp_vector_normp(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[3]; + const double p = _mp_arg(4); + if (siz>0) { // Vector-valued argument + const double *ptrs = &_mp_arg(2) + 1; + double res = 0; + if (p==2) { // L2 + for (unsigned int i = 0; i::is_inf(p)) { // L-inf + for (unsigned int i = 0; ires) res = val; + } + } else { // L-p + for (unsigned int i = 0; i0?res:0; + } + // Scalar-valued argument. + const double val = _mp_arg(2); + return p?cimg::abs(val):(val!=0); + } + static double mp_vector_print(_cimg_math_parser& mp) { const bool print_string = (bool)mp.opcode[4]; cimg_pragma_openmp(critical(mp_vector_print)) @@ -28124,6 +28321,23 @@ namespace cimg_library { return _mp_arg(1); } + static double mp_vector_unitnorm(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[3]; + const double p = _mp_arg(4); + if (siz>0) { // Vector-valued argument + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + if (ptrd!=ptrs) std::memcpy(ptrd,ptrs,siz*sizeof(double)); + CImg vec(ptrd,siz,1,1,1,true); + const double mag = vec.magnitude(p); + if (mag>0) vec/=mag; + return cimg::type::nan(); + } + // Scalar-valued argument. + const double val = _mp_arg(2); + return val?(_mp_arg(2)?1:val):0; + } + #define _cimg_mp_vfunc(func) \ const longT sizd = (longT)mp.opcode[2];\ const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \ @@ -28589,7 +28803,9 @@ namespace cimg_library { - The \inplace of this method statically casts the computed values to the pixel type \c T. - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. **/ +#if cimg_use_cpp11==1 _cimg_create_pointwise_functions(erf,std::erf,4096) +#endif //! Compute the logarithm of each pixel value. /** @@ -28916,7 +29132,7 @@ namespace cimg_library { Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition. **/ CImg& pow(const char *const expression) { - return pow((+*this)._fill(expression,true,3,0,0,"pow",this,0)); + return pow((+*this)._fill(expression,true,3,0,"pow",this,0)); } //! Raise each pixel value to a power, specified from an expression \newinstance. @@ -28968,7 +29184,7 @@ namespace cimg_library { Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift. **/ CImg& rol(const char *const expression) { - return rol((+*this)._fill(expression,true,3,0,0,"rol",this,0)); + return rol((+*this)._fill(expression,true,3,0,"rol",this,0)); } //! Compute the bitwise left rotation of each pixel value \newinstance. @@ -29020,7 +29236,7 @@ namespace cimg_library { Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift. **/ CImg& ror(const char *const expression) { - return ror((+*this)._fill(expression,true,3,0,0,"ror",this,0)); + return ror((+*this)._fill(expression,true,3,0,"ror",this,0)); } //! Compute the bitwise right rotation of each pixel value \newinstance. @@ -29102,7 +29318,7 @@ namespace cimg_library { \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. **/ CImg& min(const char *const expression) { - return min((+*this)._fill(expression,true,3,0,0,"min",this,0)); + return min((+*this)._fill(expression,true,3,0,"min",this,0)); } //! Pointwise min operator between an image and an expression \newinstance. @@ -29160,7 +29376,7 @@ namespace cimg_library { \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. **/ CImg& max(const char *const expression) { - return max((+*this)._fill(expression,true,3,0,0,"max",this,0)); + return max((+*this)._fill(expression,true,3,0,"max",this,0)); } //! Pointwise max operator between an image and an expression \newinstance. @@ -29219,7 +29435,7 @@ namespace cimg_library { \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. **/ CImg& minabs(const char *const expression) { - return minabs((+*this)._fill(expression,true,3,0,0,"minabs",this,0)); + return minabs((+*this)._fill(expression,true,3,0,"minabs",this,0)); } //! Pointwise minabs operator between an image and an expression \newinstance. @@ -29278,7 +29494,7 @@ namespace cimg_library { \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. **/ CImg& maxabs(const char *const expression) { - return maxabs((+*this)._fill(expression,true,3,0,0,"maxabs",this,0)); + return maxabs((+*this)._fill(expression,true,3,0,"maxabs",this,0)); } //! Pointwise maxabs operator between an image and an expression \newinstance. @@ -29873,7 +30089,7 @@ namespace cimg_library { if (!expression || !*expression) return 0; double _val = 0; if (__eval(expression,_val)) return _val; - _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='+' || *expression=='*' || *expression==':'),"eval", *this,img_output,list_images,false); mp.begin_t(); @@ -29916,7 +30132,7 @@ namespace cimg_library { if (!expression || !*expression) { output.assign(1); *output = 0; return; } double _val = 0; if (__eval(expression,_val)) { output.assign(1); *output = _val; return; } - _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='+' || *expression=='*' || *expression==':'),"eval", *this,img_output,list_images,false); output.assign(1,std::max(1U,mp.result_dim)); @@ -30049,32 +30265,37 @@ namespace cimg_library { //! Compute norm of the image, viewed as a matrix. /** - \param magnitude_type Norm type. Can be: - - \c -1: Linf-norm + \param magnitude_type Can be: - \c 0: L0-norm - \c 1: L1-norm - \c 2: L2-norm + - \c p>2 : Lp-norm + - \c ~0U: Linf-norm **/ - double magnitude(const int magnitude_type=2) const { + double magnitude(const float magnitude_type=2) const { if (is_empty()) throw CImgInstanceException(_cimg_instance "magnitude(): Empty instance.", cimg_instance); const ulongT siz = size(); double res = 0; - switch (magnitude_type) { - case -1 : { - cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; } - } break; - case 1 : { - cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) - for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]); - } break; - default : { + if (magnitude_type==2) { // L2 cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]); res = (double)std::sqrt(res); - } + } else if (magnitude_type==1) { // L1 + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]); + } else if (!magnitude_type) { // L0 + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)(_data[off]?1:0); + } else if (cimg::type::is_inf(magnitude_type)) { // L-inf + cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; } + } else { // L-p + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) + res+=(double)std::pow((double)cimg::abs(_data[off]),(double)magnitude_type); + res = (double)std::pow(res,1.0/magnitude_type); } return res; } @@ -30345,7 +30566,7 @@ namespace cimg_library { /** If the instance matrix is not square, the Moore-Penrose pseudo-inverse is computed instead. \param use_LU Choose the inverting algorithm. Can be: - - \c true: LU solver (faster but less precise). + - \c true: LU solver (faster but sometimes less precise). - \c false: SVD solver (more precise but slower). \param lambda is used only in the Moore-Penrose pseudoinverse for estimating A^t.(A^t.A + lambda.Id)^-1. **/ @@ -31284,7 +31505,7 @@ namespace cimg_library { cimg_forX(*this,x) { CImg S = get_column(x); const CImg S0 = method<2?CImg():S; - Tfloat residual = S.magnitude()/S._height; + Tfloat residual = S.magnitude(2)/S._height; const unsigned int nmax = max_iter?max_iter:D._width; for (unsigned int n = 0; nmax_residual; ++n) { @@ -31339,7 +31560,7 @@ namespace cimg_library { W(x,ind) = weight; cimg_forY(S,y) S[y]-=weight*D(ind,y); } - residual = S.magnitude()/S._height; + residual = S.magnitude(2)/S._height; is_orthoproj = true; } } @@ -32533,37 +32754,32 @@ namespace cimg_library { _cimg_abort_init_openmp; try { CImg base = provides_copy?provides_copy->get_shared():get_shared(); - _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='+' || *expression=='*' || *expression==':'), calling_function,base,this,list_images,true); - if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' && + if (!provides_copy && expression && + *expression!='>' && *expression!='<' && *expression!=':' && mp.need_input_copy) base.assign().assign(*this,false); // Needs input copy - // Determine 2nd largest image dimension (used as axis for inner loop in parallelized evaluation). - unsigned int M; + // Determine M2, smallest image dimension (used as axis for the most inner loop in parallelized iterations). + // M1 is the total number of parallelized iterations. + unsigned int M1 = 0, M2 = 0; + cimg::unused(M1,M2); if (mp.result_dim) { - M = cimg::max(_width,_height,_depth); - M = M==_width?std::max(_height,_depth):M==_height?std::max(_width,_depth):std::max(_width,_height); + M2 = cimg::min(_width,_height,_depth); + M1 = M2==_width?_height*_depth:M2==_height?_width*_depth:_width*_height; } else { - M = cimg::max(_width,_height,_depth,_spectrum); - M = M==_width?cimg::max(_height,_depth,_spectrum): - M==_height?cimg::max(_width,_depth,_spectrum): - M==_depth?cimg::max(_width,_height,_spectrum):cimg::max(_width,_height,_depth); + M2 = cimg::min(_width,_height,_depth,_spectrum); + M1 = M2==_width?_height*_depth*_spectrum:M2==_height?_width*_depth*_spectrum: + M2==_depth?_width*_height*_spectrum:_width*_height*_depth; } - bool do_in_parallel = false; -#if cimg_use_openmp!=0 - if (mp.is_noncritical_run && (*expression=='*' || *expression==':')) - throw CImgArgumentException(_cimg_instance - "%s(): Cannot evaluate expression '%s' in parallel, " - "as 'run()' is used outside a 'critical()' section.", - cimg_instance,calling_function,expression); - cimg_openmp_if(!mp.is_noncritical_run && - (*expression=='*' || *expression==':' || - (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2))) - do_in_parallel = true; -#endif + bool is_parallelizable = false; + cimg_openmp_if(*expression=='*' || *expression==':' || (mp.is_parallelizable && + (M2>=2 || M1>=4096) && M1*M2>=32)) + is_parallelizable = true; + if (mp.result_dim) { // Vector-valued expression const unsigned int N = std::min(mp.result_dim,_spectrum); const ulongT whd = (ulongT)_width*_height*_depth; @@ -32583,7 +32799,7 @@ namespace cimg_library { } mp.end_t(); - } else if (*expression=='>' || !do_in_parallel) { + } else if (*expression=='>' || *expression=='+' || !is_parallelizable) { CImg res(1,mp.result_dim); mp.begin_t(); cimg_forYZ(*this,y,z) { @@ -32628,8 +32844,8 @@ namespace cimg_library { } \ } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp - if (M==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) } - else if (M==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) } + if (M2==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) } + else if (M2==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) } else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) } lmp.end_t(); @@ -32647,7 +32863,7 @@ namespace cimg_library { else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); } mp.end_t(); - } else if (*expression=='>' || !do_in_parallel) { + } else if (*expression=='>' || *expression=='+' || !is_parallelizable) { mp.begin_t(); if (mode&4) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); } else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); } @@ -32677,9 +32893,9 @@ namespace cimg_library { } \ } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp - if (M==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) } - else if (M==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) } - else if (M==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) } + if (M2==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) } + else if (M2==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) } + else if (M2==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) } else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) } lmp.end_t(); @@ -36725,6 +36941,20 @@ namespace cimg_library { This function permutes image content regarding the specified axes permutation. **/ CImg& permute_axes(const char *const axes_order) { + if (is_empty() || !axes_order) return *this; + const unsigned uicase = _permute_axes_uicase(axes_order); + if (_permute_axes_is_optim(uicase)) { // Data layout allow to do nothing but set the new dimensions + CImg res(*this,true); + for (unsigned int i = 0; i<4; ++i) { + const unsigned int + axis = (uicase>>(4*(3 - i)))&15, + value = !axis?_width:axis==1?_height:axis==2?_depth:_spectrum; + if (!i) res._width = value; else if (i==1) res._height = value; + else if (i==2) res._depth = value; else res._spectrum = value; + } + _width = res._width; _height = res._height; _depth = res._depth; _spectrum = res._spectrum; + return *this; + } return get_permute_axes(axes_order).move_to(*this); } @@ -36734,224 +36964,273 @@ namespace cimg_library { return _permute_axes(axes_order,foo); } + unsigned int _permute_axes_uicase(const char *const axes_order) const { // Convert axes to integer case number + unsigned char s_axes[4] = { 0,1,2,3 }, n_axes[4] = { }; + bool is_error = false; + if (axes_order) for (unsigned int l = 0; axes_order[l]; ++l) { + int c = cimg::lowercase(axes_order[l]); + if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { is_error = true; break; } + else { ++n_axes[c%=4]; s_axes[l] = (unsigned char)c; } + } + is_error|=(*n_axes>1) || (n_axes[1]>1) || (n_axes[2]>1) || (n_axes[3]>1); + if (is_error) + throw CImgArgumentException(_cimg_instance + "permute_axes(): Invalid specified axes order '%s'.", + cimg_instance, + axes_order); + return (s_axes[0]<<12) | (s_axes[1]<<8) | (s_axes[2]<<4) | (s_axes[3]); + } + + bool _permute_axes_is_optim(const unsigned int uicase) const { // Determine cases where nothing has to be done + const unsigned int co = ((_width>1)<<3)|((_height>1)<<2)|((_depth>1)<<1)|(_spectrum>1); + if (co<=2 || uicase==0x0123) return true; + switch (uicase) { + case (0x0132) : if ((co>=4 && co<=6) || (co>=8 && co<=10) || (co>=12 && co<=14)) return true; break; + case (0x0213) : if ((co>=3 && co<=5) || (co>=8 && co<=13)) return true; break; + case (0x0231) : if (co==3 || co==4 || (co>=8 && co<=12)) return true; break; + case (0x0312) : if (co==4 || co==6 || co==8 || co==9 || co==10 || co==12 || co==14) return true; break; + case (0x0321) : if (co==4 || (co>=8 && co<=10) || co==12) return true; break; + case (0x1023) : if (co>=3 && co<=11) return true; break; + case (0x1032) : if ((co>=4 && co<=6) || (co>=8 && co<=10)) return true; break; + case (0x1203) : if (co>=3 && co<=9) return true; break; + case (0x1230) : if (co>=3 && co<=8) return true; break; + case (0x1302) : if ((co>=4 && co<=6) || co==8 || co==10) return true; break; + case (0x1320) : if ((co>=4 && co<=6) || co==8) return true; break; + case (0x2013) : if ((co>=3 && co<=5) || co==8 || co==9 || co==12 || co==13) return true; break; + case (0x2031) : if (co==3 || co==4 || co==8 || co==9 || co==12) return true; break; + case (0x2103) : if ((co>=3 && co<=5) || co==8 || co==9) return true; break; + case (0x2130) : if ((co>=3 && co<=5) || co==8) return true; break; + case (0x2301) : if (co==3 || co==4 || co==8 || co==12) return true; break; + case (0x2310) : if (co==3 || co==4 || co==8) return true; break; + case (0x3012) : if (co==4 || co==6 || co==8 || co==10 || co==12 || co==14) return true; break; + case (0x3021) : if (co==4 || co==8 || co==10 || co==12) return true; break; + case (0x3102) : if (co==4 || co==6 || co==8 || co==10) return true; break; + case (0x3120) : if (co==4 || co==6 || co==8) return true; break; + case (0x3201) : if (co==4 || co==8 || co==12) return true; break; + case (0x3210) : if (co==4 || co==8) return true; break; + } + return false; + } + template CImg _permute_axes(const char *const axes_order, const t&) const { if (is_empty() || !axes_order) return CImg(*this,false); CImg res; + const unsigned uicase = _permute_axes_uicase(axes_order); + + if (_permute_axes_is_optim(uicase)) { // Data layout allow to do nothing but set the new dimensions + res.assign(*this,false); + for (unsigned int i = 0; i<4; ++i) { + const unsigned int + axis = (uicase>>(4*(3 - i)))&15, + value = !axis?_width:axis==1?_height:axis==2?_depth:_spectrum; + if (!i) res._width = value; else if (i==1) res._height = value; + else if (i==2) res._depth = value; else res._spectrum = value; + } + return res; + } + const T* ptrs = _data; - unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = {}; - for (unsigned int l = 0; axes_order[l]; ++l) { - int c = cimg::lowercase(axes_order[l]); - if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; } - else { ++n_code[c%=4]; s_code[l] = (unsigned char)c; } - } - if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) { - const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]); - ulongT wh, whd; - switch (code) { - case 0x0123 : // xyzc - return +*this; - case 0x0132 : // xycz - res.assign(_width,_height,_spectrum,_depth); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++); - break; - case 0x0213 : // xzyc - res.assign(_width,_depth,_height,_spectrum); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++); - break; - case 0x0231 : // xzcy - res.assign(_width,_depth,_spectrum,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++); - break; - case 0x0312 : // xcyz - res.assign(_width,_spectrum,_height,_depth); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++); - break; - case 0x0321 : // xczy - res.assign(_width,_spectrum,_depth,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++); - break; - case 0x1023 : // yxzc - res.assign(_height,_width,_depth,_spectrum); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++); - break; - case 0x1032 : // yxcz - res.assign(_height,_width,_spectrum,_depth); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++); - break; - case 0x1203 : // yzxc - res.assign(_height,_depth,_width,_spectrum); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++); - break; - case 0x1230 : // yzcx - res.assign(_height,_depth,_spectrum,_width); - switch (_width) { - case 1 : { - t *ptr_r = res.data(0,0,0,0); - for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { - *(ptr_r++) = (t)*(ptrs++); - } - } break; - case 2 : { - t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1); - for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { - *(ptr_r++) = (t)ptrs[0]; - *(ptr_g++) = (t)ptrs[1]; - ptrs+=2; - } - } break; - case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB - t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2); - for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { - *(ptr_r++) = (t)ptrs[0]; - *(ptr_g++) = (t)ptrs[1]; - *(ptr_b++) = (t)ptrs[2]; - ptrs+=3; - } - } break; - case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA - t - *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), - *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3); - for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { - *(ptr_r++) = (t)ptrs[0]; - *(ptr_g++) = (t)ptrs[1]; - *(ptr_b++) = (t)ptrs[2]; - *(ptr_a++) = (t)ptrs[3]; - ptrs+=4; - } - } break; - default : { - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++); - return res; + ulongT wh, whd; + + switch (uicase) { + case 0x0123 : // xyzc + return +*this; + case 0x0132 : // xycz + res.assign(_width,_height,_spectrum,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++); + break; + case 0x0213 : // xzyc + res.assign(_width,_depth,_height,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++); + break; + case 0x0231 : // xzcy + res.assign(_width,_depth,_spectrum,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++); + break; + case 0x0312 : // xcyz + res.assign(_width,_spectrum,_height,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++); + break; + case 0x0321 : // xczy + res.assign(_width,_spectrum,_depth,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++); + break; + case 0x1023 : // yxzc + res.assign(_height,_width,_depth,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++); + break; + case 0x1032 : // yxcz + res.assign(_height,_width,_spectrum,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++); + break; + case 0x1203 : // yzxc + res.assign(_height,_depth,_width,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++); + break; + case 0x1230 : // yzcx + res.assign(_height,_depth,_spectrum,_width); + switch (_width) { + case 1 : { + t *ptr_r = res.data(0,0,0,0); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)*(ptrs++); } + } break; + case 2 : { + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + ptrs+=2; } - break; - case 0x1302 : // ycxz - res.assign(_height,_spectrum,_width,_depth); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++); - break; - case 0x1320 : // yczx - res.assign(_height,_spectrum,_depth,_width); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++); - break; - case 0x2013 : // zxyc - res.assign(_depth,_width,_height,_spectrum); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++); - break; - case 0x2031 : // zxcy - res.assign(_depth,_width,_spectrum,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++); - break; - case 0x2103 : // zyxc - res.assign(_depth,_height,_width,_spectrum); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++); - break; - case 0x2130 : // zycx - res.assign(_depth,_height,_spectrum,_width); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++); - break; - case 0x2301 : // zcxy - res.assign(_depth,_spectrum,_width,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++); - break; - case 0x2310 : // zcyx - res.assign(_depth,_spectrum,_height,_width); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++); - break; - case 0x3012 : // cxyz - res.assign(_spectrum,_width,_height,_depth); - switch (_spectrum) { - case 1 : { - const T *ptr_r = data(0,0,0,0); - t *ptrd = res._data; - for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++); - } break; - case 2 : { - const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1); - t *ptrd = res._data; - for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { - ptrd[0] = (t)*(ptr_r++); - ptrd[1] = (t)*(ptr_g++); - ptrd+=2; - } - } break; - case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB - const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); - t *ptrd = res._data; - for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { - ptrd[0] = (t)*(ptr_r++); - ptrd[1] = (t)*(ptr_g++); - ptrd[2] = (t)*(ptr_b++); - ptrd+=3; - } - } break; - case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA - const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); - t *ptrd = res._data; - for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { - ptrd[0] = (t)*(ptr_r++); - ptrd[1] = (t)*(ptr_g++); - ptrd[2] = (t)*(ptr_b++); - ptrd[3] = (t)*(ptr_a++); - ptrd+=4; - } - } break; - default : { - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++); + } break; + case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + *(ptr_b++) = (t)ptrs[2]; + ptrs+=3; } + } break; + case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA + t + *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), + *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + *(ptr_b++) = (t)ptrs[2]; + *(ptr_a++) = (t)ptrs[3]; + ptrs+=4; } - break; - case 0x3021 : // cxzy - res.assign(_spectrum,_width,_depth,_height); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++); - break; - case 0x3102 : // cyxz - res.assign(_spectrum,_height,_width,_depth); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++); - break; - case 0x3120 : // cyzx - res.assign(_spectrum,_height,_depth,_width); - wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++); - break; - case 0x3201 : // czxy - res.assign(_spectrum,_depth,_width,_height); + } break; + default : { wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++); - break; - case 0x3210 : // czyx - res.assign(_spectrum,_depth,_height,_width); + cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++); + return res; + } + } + break; + case 0x1302 : // ycxz + res.assign(_height,_spectrum,_width,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++); + break; + case 0x1320 : // yczx + res.assign(_height,_spectrum,_depth,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++); + break; + case 0x2013 : // zxyc + res.assign(_depth,_width,_height,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++); + break; + case 0x2031 : // zxcy + res.assign(_depth,_width,_spectrum,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++); + break; + case 0x2103 : // zyxc + res.assign(_depth,_height,_width,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++); + break; + case 0x2130 : // zycx + res.assign(_depth,_height,_spectrum,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++); + break; + case 0x2301 : // zcxy + res.assign(_depth,_spectrum,_width,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++); + break; + case 0x2310 : // zcyx + res.assign(_depth,_spectrum,_height,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++); + break; + case 0x3012 : // cxyz + res.assign(_spectrum,_width,_height,_depth); + switch (_spectrum) { + case 1 : { + const T *ptr_r = data(0,0,0,0); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++); + } break; + case 2 : { + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd+=2; + } + } break; + case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd[2] = (t)*(ptr_b++); + ptrd+=3; + } + } break; + case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd[2] = (t)*(ptr_b++); + ptrd[3] = (t)*(ptr_a++); + ptrd+=4; + } + } break; + default : { wh = (ulongT)res._width*res._height; whd = wh*res._depth; - cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++); - break; + cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++); + } } + break; + case 0x3021 : // cxzy + res.assign(_spectrum,_width,_depth,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++); + break; + case 0x3102 : // cyxz + res.assign(_spectrum,_height,_width,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++); + break; + case 0x3120 : // cyzx + res.assign(_spectrum,_height,_depth,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++); + break; + case 0x3201 : // czxy + res.assign(_spectrum,_depth,_width,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++); + break; + case 0x3210 : // czyx + res.assign(_spectrum,_depth,_height,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++); + break; } - if (!res) - throw CImgArgumentException(_cimg_instance - "permute_axes(): Invalid specified axes order '%s'.", - cimg_instance, - axes_order); return res; } @@ -47371,24 +47650,22 @@ namespace cimg_library { if (is_empty() || !opacity || !pattern || std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; - int w1 = width() - 1, h1 = height() - 1, dx01 = x1 - x0, dy01 = y1 - y0; const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); - if (pattern==~0U && y0>y1) { - cimg::swap(x0,x1,y0,y1); - dx01*=-1; dy01*=-1; - } + if (pattern==~0U && y0>y1) { cimg::swap(x0,x1,y0,y1); dx01*=-1; dy01*=-1; } static unsigned int hatch = ~0U - (~0U>>1); if (init_hatch) hatch = ~0U - (~0U>>1); cimg_init_scanline(opacity); const int - step = y0<=y1?1:-1,hdy01 = dy01*cimg::sign(dx01)/2, - cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + step = y0<=y1?1:-1, + hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), + cy1 = cimg::cut(y1,0,h1) + step; dy01+=dy01?0:1; for (int y = cy0; y!=cy1; y+=step) { @@ -47763,29 +48040,32 @@ namespace cimg_library { - This function uses several call to the single CImg::draw_line() procedure, depending on the vectors size in \p points. **/ - template - CImg& draw_line(const CImg& points, + template + CImg& draw_line(const CImg& points, const tc *const color, const float opacity=1, const unsigned int pattern=~0U, const bool init_hatch=true) { - if (is_empty() || !points || points._width<2) return *this; - bool ninit_hatch = init_hatch; - switch (points._height) { - case 0 : case 1 : + if (is_empty() || !points) return *this; + if (!color) throw CImgArgumentException(_cimg_instance - "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).", + "draw_line(): Specified color is (null).", + cimg_instance); + if (points.height()!=2) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified point set (%u,%u,%u,%u).", cimg_instance, - points._width,points._height,points._depth,points._spectrum,points._data); + points._width,points._height,points._depth,points._spectrum); + CImg ipoints; + if (cimg::type::is_float()) ipoints = points.get_round(); + else ipoints.assign(points,cimg::type::string()==cimg::type::string()); - default : { - const int x0 = (int)points(0,0), y0 = (int)points(0,1); - int ox = x0, oy = y0; - for (unsigned int i = 1; i ipoints; + if (cimg::type::is_float()) ipoints = points.get_round(); + else ipoints.assign(points,cimg::type::string()==cimg::type::string()); + + if (ipoints._width==1) return draw_point(ipoints(0,0),ipoints(0,1),color,opacity); + if (ipoints._width==2) return draw_line(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),color,opacity); + if (ipoints._width==3) return draw_triangle(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1), + ipoints(2,0),ipoints(2,1),color,opacity); cimg_init_scanline(opacity); int xmin = 0, ymin = 0, - xmax = points.get_shared_row(0).max_min(xmin), - ymax = points.get_shared_row(1).max_min(ymin); + xmax = ipoints.get_shared_row(0).max_min(xmin), + ymax = ipoints.get_shared_row(1).max_min(ymin); if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this; if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity); ymin = std::max(0,ymin); ymax = std::min(height() - 1,ymax); - CImg Xs(points._width,ymax - ymin + 1); + CImg Xs(ipoints._width,ymax - ymin + 1); CImg count(Xs._height,1,1,1,0); unsigned int n = 0, nn = 1; bool go_on = true; while (go_on) { - unsigned int an = (nn + 1)%points._width; - const int - x0 = cimg::uiround(points(n,0)), - y0 = cimg::uiround(points(n,1)); - if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; } - const int - x1 = cimg::uiround(points(nn,0)), - y1 = cimg::uiround(points(nn,1)); + unsigned int an = (nn + 1)%ipoints._width; + const int x0 = ipoints(n,0), y0 = ipoints(n,1); + if (ipoints(nn,1)==y0) while (ipoints(an,1)==y0) { nn = an; (an+=1)%=ipoints._width; } + const int x1 = ipoints(nn,0), y1 = ipoints(nn,1); unsigned int tn = an; - while (points(tn,1)==y1) (tn+=1)%=points._width; - + while (ipoints(tn,1)==y1) (tn+=1)%=ipoints._width; if (y0!=y1) { const int - y2 = cimg::uiround(points(tn,1)), + y2 = ipoints(tn,1), x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1, step = cimg::sign(y01), - tmax = std::max(1,cimg::abs(y01)), htmax = tmax*cimg::sign(x01)/2, + tmax = std::max(1,cimg::abs(y01)), + htmax = tmax*cimg::sign(x01)/2, tend = tmax - (step==cimg::sign(y12)); unsigned int y = (unsigned int)y0 - ymin; for (int t = 0; t<=tend; ++t, y+=step) @@ -49806,43 +50084,38 @@ namespace cimg_library { } //! Draw a outlined 2D or 3D polygon \overloading. - template - CImg& draw_polygon(const CImg& points, + template + CImg& draw_polygon(const CImg& points, const tc *const color, const float opacity, const unsigned int pattern) { if (is_empty() || !points) return *this; if (!color) throw CImgArgumentException(_cimg_instance "draw_polygon(): Specified color is (null).", cimg_instance); - if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity); - if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1), - (int)points(1,0),(int)points(1,1),color,opacity,pattern); - bool ninit_hatch = true; - switch (points._height) { - case 0 : case 1 : + if (points.height()!=2) throw CImgArgumentException(_cimg_instance "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).", cimg_instance, points._width,points._height,points._depth,points._spectrum); - default : { - CImg npoints(points._width,2); - int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1); - unsigned int nb_points = 1; - for (unsigned int p = 1; p ipoints; + if (cimg::type::is_float()) ipoints = points.get_round(); + else ipoints.assign(points,cimg::type::string()==cimg::type::string()); + + if (ipoints._width==1) return draw_point(ipoints(0,0),ipoints(0,1),color,opacity); + if (ipoints._width==2) return draw_line(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1), + color,opacity,pattern); + if (ipoints._width==3) return draw_triangle(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1), + ipoints(2,0),ipoints(2,1),color,opacity,pattern); + bool ninit_hatch = true; + const int x0 = ipoints(0,0), y0 = ipoints(0,1); + int ox = x0, oy = y0; + for (unsigned int i = 1; i letter = font[ch]; - if (letter) { + if (font[ch]) { + CImg letter = font[ch]; const CImg &mask = ch + 256Uletter._spectrum) - letter.assign(letter.get_resize(-100,-100,1,_spectrum,0,2),false); - const unsigned int cmin = std::min(_spectrum,letter._spectrum); - if (foreground_color) - for (unsigned int c = 0; c &screen = visu?visu:visu0; std::FILE *file; do { - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u." +#ifdef cimg_use_png + "png", +#else + "bmp", +#endif + snap_number++); if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp); @@ -53831,9 +54120,9 @@ namespace cimg_library { do { #ifdef cimg_use_zlib - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++); #else - cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); + cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++); #endif if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); } while (file); @@ -54215,7 +54504,7 @@ namespace cimg_library { "load_bmp(): Specified filename is (null).", cimg_instance); - const ulongT fsiz = file?(ulongT)cimg_max_buf_size:(ulongT)cimg::fsize(filename); + const ulongT fsiz = (ulongT)(file?cimg::fsize(file):cimg::fsize(filename)); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); CImg header(54); cimg::fread(header._data,54,nfile); @@ -54238,13 +54527,25 @@ namespace cimg_library { nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24), bpp = header[0x1C] + (header[0x1D]<<8); - if (!file_size || file_size==offset) { - cimg::fseek(nfile,0,SEEK_END); - file_size = (int)cimg::ftell(nfile); - cimg::fseek(nfile,54,SEEK_SET); - } - if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR); + if ((ulongT)file_size!=fsiz) + throw CImgIOException(_cimg_instance + "load_bmp(): Invalid file_size %d specified in filename '%s' (expected %lu).", + cimg_instance, + file_size,filename?filename:"(FILE*)",fsiz); + + if (header_size<0 || header_size>=file_size) + throw CImgIOException(_cimg_instance + "load_bmp(): Invalid header size %d specified in filename '%s'.", + cimg_instance, + header_size,filename?filename:"(FILE*)"); + + if (offset<0 || offset>=file_size) + throw CImgIOException(_cimg_instance + "load_bmp(): Invalid offset %d specified in filename '%s'.", + cimg_instance, + offset,filename?filename:"(FILE*)"); + if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR); const int dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)), align_bytes = (4 - dx_bytes%4)%4; @@ -54252,18 +54553,24 @@ namespace cimg_library { cimg_iobuffer = (ulongT)24*1024*1024, buf_size = (ulongT)cimg::abs(dy)*(dx_bytes + align_bytes); - if (buf_size>fsiz) - throw CImgIOException(_cimg_instance - "load_bmp(): File size %lu for filename '%s' does not match " - "encoded image dimensions (%d,%d).", - cimg_instance, - (long)fsiz,filename?filename:"(FILE*)",dx,dy); + if (buf_size>=fsiz) + throw CImgIOException(_cimg_instance + "load_bmp(): File size %lu for filename '%s' does not match " + "encoded image dimensions (%d,%d).", + cimg_instance, + (long)fsiz,filename?filename:"(FILE*)",dx,dy); CImg colormap; if (bpp<16) { if (!nb_colors) nb_colors = 1<0) cimg::fseek(nfile,xoffset,SEEK_CUR); + if (xoffset<0 || xoffset>=file_size) + throw CImgIOException(_cimg_instance + "load_bmp(): Malformed header in filename '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + cimg::fseek(nfile,xoffset,SEEK_CUR); CImg buffer; if (buf_size(x.headers, "Content-Length"); + auto len = get_header_value_u64(x.headers, "Content-Length", 0, 0); if (len > payload_max_length) { exceed_payload_max_length = true; skip_content_with_length(strm, len); @@ -2815,7 +2815,7 @@ bool parse_www_authenticate(const Response &res, s = s.substr(pos + 1); auto beg = std::sregex_iterator(s.begin(), s.end(), re); for (auto i = beg; i != std::sregex_iterator(); ++i) { - auto m = *i; + const auto &m = *i; auto key = s.substr(static_cast(m.position(1)), static_cast(m.length(1))); auto val = m.length(2) > 0 @@ -3510,6 +3510,12 @@ Server &Server::set_default_headers(Headers headers) { return *this; } +Server &Server::set_header_writer( + std::function const &writer) { + header_writer_ = writer; + return *this; +} + Server &Server::set_keep_alive_max_count(size_t count) { keep_alive_max_count_ = count; return *this; @@ -3704,7 +3710,7 @@ bool Server::write_response_core(Stream &strm, bool close_connection, return false; } - if (!detail::write_headers(bstrm, res.headers)) { return false; } + if (!header_writer_(bstrm, res.headers)) { return false; } // Flush buffer auto &data = bstrm.get_buffer(); @@ -4552,9 +4558,9 @@ bool ClientImpl::read_response_line(Stream &strm, const Request &req, if (!line_reader.getline()) { return false; } #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR - const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n"); -#else const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n"); +#else + const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n"); #endif std::cmatch m; @@ -4943,7 +4949,7 @@ bool ClientImpl::write_request(Stream &strm, Request &req, const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path; bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); - detail::write_headers(bstrm, req.headers); + header_writer_(bstrm, req.headers); // Flush buffer auto &data = bstrm.get_buffer(); @@ -5754,6 +5760,11 @@ void ClientImpl::set_default_headers(Headers headers) { default_headers_ = std::move(headers); } +void ClientImpl::set_header_writer( + std::function const &writer) { + header_writer_ = writer; +} + void ClientImpl::set_address_family(int family) { address_family_ = family; } @@ -6948,6 +6959,11 @@ void Client::set_default_headers(Headers headers) { cli_->set_default_headers(std::move(headers)); } +void Client::set_header_writer( + std::function const &writer) { + cli_->set_header_writer(writer); +} + void Client::set_address_family(int family) { cli_->set_address_family(family); } diff --git a/libs/cpp_httplib/httplib.h b/libs/cpp_httplib/httplib.h index 36d6d81f..abf91f52 100644 --- a/libs/cpp_httplib/httplib.h +++ b/libs/cpp_httplib/httplib.h @@ -8,7 +8,7 @@ #ifndef CPPHTTPLIB_HTTPLIB_H #define CPPHTTPLIB_HTTPLIB_H -#define CPPHTTPLIB_VERSION "0.13.3" +#define CPPHTTPLIB_VERSION "0.14.1" /* * Configuration @@ -233,7 +233,7 @@ using socket_t = int; #include #include #include -#define CPPHTTPLIB_OPENSSL_SUPPORT + #ifdef CPPHTTPLIB_OPENSSL_SUPPORT #ifdef _WIN32 #include @@ -487,8 +487,7 @@ struct Request { bool has_header(const std::string &key) const; std::string get_header_value(const std::string &key, size_t id = 0) const; - template - T get_header_value(const std::string &key, size_t id = 0) const; + uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; size_t get_header_value_count(const std::string &key) const; void set_header(const std::string &key, const std::string &val); @@ -520,8 +519,7 @@ struct Response { bool has_header(const std::string &key) const; std::string get_header_value(const std::string &key, size_t id = 0) const; - template - T get_header_value(const std::string &key, size_t id = 0) const; + uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const; size_t get_header_value_count(const std::string &key) const; void set_header(const std::string &key, const std::string &val); @@ -739,6 +737,8 @@ class RegexMatcher : public MatcherBase { std::regex regex_; }; +ssize_t write_headers(Stream &strm, const Headers &headers); + } // namespace detail class Server { @@ -802,6 +802,8 @@ class Server { Server &set_socket_options(SocketOptions socket_options); Server &set_default_headers(Headers headers); + Server & + set_header_writer(std::function const &writer); Server &set_keep_alive_max_count(size_t count); Server &set_keep_alive_timeout(time_t sec); @@ -936,6 +938,8 @@ class Server { SocketOptions socket_options_ = default_socket_options; Headers default_headers_; + std::function header_writer_ = + detail::write_headers; }; enum class Error { @@ -988,8 +992,8 @@ class Result { bool has_request_header(const std::string &key) const; std::string get_request_header_value(const std::string &key, size_t id = 0) const; - template - T get_request_header_value(const std::string &key, size_t id = 0) const; + uint64_t get_request_header_value_u64(const std::string &key, + size_t id = 0) const; size_t get_request_header_value_count(const std::string &key) const; private: @@ -1166,6 +1170,9 @@ class ClientImpl { void set_default_headers(Headers headers); + void + set_header_writer(std::function const &writer); + void set_address_family(int family); void set_tcp_nodelay(bool on); void set_socket_options(SocketOptions socket_options); @@ -1275,6 +1282,10 @@ class ClientImpl { // Default headers Headers default_headers_; + // Header writer + std::function header_writer_ = + detail::write_headers; + // Settings std::string client_cert_path_; std::string client_key_path_; @@ -1541,6 +1552,9 @@ class Client { void set_default_headers(Headers headers); + void + set_header_writer(std::function const &writer); + void set_address_family(int family); void set_tcp_nodelay(bool on); void set_socket_options(SocketOptions socket_options); @@ -1710,15 +1724,9 @@ inline void duration_to_sec_and_usec(const T &duration, U callback) { callback(static_cast(sec), static_cast(usec)); } -template -inline T get_header_value(const Headers & /*headers*/, - const std::string & /*key*/, size_t /*id*/ = 0, - uint64_t /*def*/ = 0) {} - -template <> -inline uint64_t get_header_value(const Headers &headers, - const std::string &key, size_t id, - uint64_t def) { +inline uint64_t get_header_value_u64(const Headers &headers, + const std::string &key, size_t id, + uint64_t def) { auto rng = headers.equal_range(key); auto it = rng.first; std::advance(it, static_cast(id)); @@ -1730,14 +1738,14 @@ inline uint64_t get_header_value(const Headers &headers, } // namespace detail -template -inline T Request::get_header_value(const std::string &key, size_t id) const { - return detail::get_header_value(headers, key, id, 0); +inline uint64_t Request::get_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(headers, key, id, 0); } -template -inline T Response::get_header_value(const std::string &key, size_t id) const { - return detail::get_header_value(headers, key, id, 0); +inline uint64_t Response::get_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(headers, key, id, 0); } template @@ -1906,10 +1914,9 @@ inline std::ostream &operator<<(std::ostream &os, const Error &obj) { return os; } -template -inline T Result::get_request_header_value(const std::string &key, - size_t id) const { - return detail::get_header_value(request_headers_, key, id, 0); +inline uint64_t Result::get_request_header_value_u64(const std::string &key, + size_t id) const { + return detail::get_header_value_u64(request_headers_, key, id, 0); } template diff --git a/libs/emhash/hash_set8.hpp b/libs/emhash/hash_set8.hpp new file mode 100644 index 00000000..3b6454e7 --- /dev/null +++ b/libs/emhash/hash_set8.hpp @@ -0,0 +1,1524 @@ +// emhash8::HashSet for C++11/14/17 +// version 1.6.3 +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019-2022 Huang Yuanbing & bailuzhou AT 163.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef EMH_KEY + #undef EMH_KEY + #undef EMH_BUCKET + #undef EMH_NEW + #undef EMH_EMPTY + #undef EMH_PREVET +#endif + +// likely/unlikely +#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) +# define EMH_LIKELY(condition) __builtin_expect(condition, 1) +# define EMH_UNLIKELY(condition) __builtin_expect(condition, 0) +#else +# define EMH_LIKELY(condition) (condition) +# define EMH_UNLIKELY(condition) (condition) +#endif + +#define EMH_KEY(p,n) p[n] + +#define EMH_INDEX(i,n) i[n] +#define EMH_BUCKET(i,n) i[n].bucket +#define EMH_HSLOT(i,n) i[n].slot +#define EMH_SLOT(i,n) (i[n].slot & _mask) +#define EMH_PREVET(i,n) i[n].slot + +#define EMH_KEYMASK(key, mask) ((size_type)(key >> 0) & ~mask) +#define EMH_EQHASH(n, key_hash) (EMH_KEYMASK(key_hash, _mask) == (_index[n].slot & ~_mask)) +#define EMH_NEW(key, bucket, key_hash) new(_pairs + _num_filled) value_type(key); _index[bucket] = {bucket, _num_filled++ | EMH_KEYMASK(key_hash, _mask)} + +#define EMH_EMPTY(i, n) (0 > (int)i[n].bucket) + +namespace emhash8 { + +constexpr uint32_t INACTIVE = 0xAAAAAAAA; +constexpr uint32_t END = 0-0x1u; +constexpr uint32_t EAD = 2; + +#ifndef EMH_DEFAULT_LOAD_FACTOR + constexpr static float EMH_DEFAULT_LOAD_FACTOR = 0.80f; +#endif +#if EMH_CACHE_LINE_SIZE < 32 + constexpr static uint32_t EMH_CACHE_LINE_SIZE = 64; +#endif + +/// A cache-friendly hash table with open addressing, linear/quadratic probing and power-of-two capacity +template , typename EqT = std::equal_to> +class HashSet +{ +public: + using htype = HashSet; + using value_type = KeyT; + using key_type = KeyT; + +#ifdef EMH_SMALL_TYPE + using size_type = uint16_t; +#elif EMH_SIZE_TYPE == 0 + using size_type = uint32_t; +#else + using size_type = size_t; +#endif + + using hasher = HashT; + using key_equal = EqT; + + struct Index + { + size_type bucket; + size_type slot; + }; + + class const_iterator; + class iterator + { + public: + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = typename htype::value_type; + using pointer = value_type*; + using const_pointer = const value_type* ; + using reference = value_type&; + using const_reference = const value_type&; + + iterator() : kv_(nullptr) {} + iterator(const_iterator& cit) { + kv_ = cit.kv_; + } + + iterator(const htype* hash_map, size_type bucket) { + kv_ = hash_map->_pairs + (int)bucket; + } + + iterator& operator++() + { + kv_ ++; + return *this; + } + + iterator operator++(int) + { + auto cur = *this; kv_ ++; + return cur; + } + + iterator& operator--() + { + kv_ --; + return *this; + } + + iterator operator--(int) + { + auto cur = *this; kv_ --; + return cur; + } + + reference operator*() const { return *kv_; } + pointer operator->() const { return kv_; } + + bool operator == (const iterator& rhs) const { return kv_ == rhs.kv_; } + bool operator != (const iterator& rhs) const { return kv_ != rhs.kv_; } + bool operator == (const const_iterator& rhs) const { return kv_ == rhs.kv_; } + bool operator != (const const_iterator& rhs) const { return kv_ != rhs.kv_; } + + public: + value_type* kv_; + }; + + class const_iterator + { + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename htype::value_type; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + + const_iterator(const iterator& it) { + kv_ = it.kv_; + } + + const_iterator (const htype* hash_map, size_type bucket) { + kv_ = hash_map->_pairs + (int)bucket; + } + + const_iterator& operator++() + { + kv_ ++; + return *this; + } + + const_iterator operator++(int) + { + auto cur = *this; kv_ ++; + return cur; + } + + const_iterator& operator--() + { + kv_ --; + return *this; + } + + const_iterator operator--(int) + { + auto cur = *this; kv_ --; + return cur; + } + + const_reference operator*() const { return *kv_; } + const_pointer operator->() const { return kv_; } + + bool operator == (const iterator& rhs) const { return kv_ == rhs.kv_; } + bool operator != (const iterator& rhs) const { return kv_ != rhs.kv_; } + bool operator == (const const_iterator& rhs) const { return kv_ == rhs.kv_; } + bool operator != (const const_iterator& rhs) const { return kv_ != rhs.kv_; } + public: + const value_type* kv_; + }; + + void init(size_type bucket, float mlf = EMH_DEFAULT_LOAD_FACTOR) + { + _pairs = nullptr; + _index = nullptr; + _mask = _num_buckets = 0; + _num_filled = 0; + max_load_factor(mlf); + rehash(bucket); + } + + HashSet(size_type bucket = 2, float mlf = EMH_DEFAULT_LOAD_FACTOR) + { + init(bucket, mlf); + } + + HashSet(const HashSet& rhs) + { + _pairs = alloc_bucket(rhs._num_buckets * rhs.max_load_factor() + 4); + _index = alloc_index(rhs._num_buckets); + clone(rhs); + } + + HashSet(HashSet&& rhs) + { + init(0); + *this = std::move(rhs); + } + + HashSet(std::initializer_list ilist) + { + init((size_type)ilist.size()); + for (auto it = ilist.begin(); it != ilist.end(); ++it) + do_insert(*it); + } + + template + HashSet(InputIt first, InputIt last, size_type bucket_count=4) + { + init(std::distance(first, last) + bucket_count); + for (; first != last; ++first) + emplace(*first); + } + + HashSet& operator=(const HashSet& rhs) + { + if (this == &rhs) + return *this; + + clearkv(); + + if (_num_buckets < rhs._num_buckets || _num_buckets > 2 * rhs._num_buckets) { + free(_pairs); _pairs = alloc_bucket(rhs._num_buckets * rhs.max_load_factor() + 4); + free(_index); _index = alloc_index(rhs._num_buckets); + } + + clone(rhs); + return *this; + } + + HashSet& operator=(HashSet&& rhs) + { + if (this != &rhs) { + swap(rhs); + rhs.clear(); + } + return *this; + } + + template + bool operator == (const Con& rhs) const + { + if (size() != rhs.size()) + return false; + + for (auto it = begin(), last = end(); it != last; ++it) { + auto oi = rhs.find(*it); + if (oi == rhs.end()) + return false; + } + return true; + } + + template + bool operator != (const Con& rhs) const { return !(*this == rhs); } + + ~HashSet() + { + clearkv(); + free(_pairs); + free(_index); + } + + void clone(const HashSet& rhs) + { + _hasher = rhs._hasher; +// _eq = rhs._eq; + _num_buckets = rhs._num_buckets; + _num_filled = rhs._num_filled; + _mlf = rhs._mlf; + _last = rhs._last; + _mask = rhs._mask; + + auto opairs = rhs._pairs; + memcpy((char*)_index, (char*)rhs._index, (_num_buckets + EAD) * sizeof(Index)); + + if (is_copy_trivially()) { + if (opairs) + memcpy((char*)_pairs, (char*)opairs, _num_filled * sizeof(value_type)); + } else { + for (size_type slot = 0; slot < _num_filled; slot++) + new(_pairs + slot) value_type(opairs[slot]); + } + } + + void swap(HashSet& rhs) + { + // std::swap(_eq, rhs._eq); + std::swap(_hasher, rhs._hasher); + std::swap(_pairs, rhs._pairs); + std::swap(_index, rhs._index); + std::swap(_num_buckets, rhs._num_buckets); + std::swap(_num_filled, rhs._num_filled); + std::swap(_mask, rhs._mask); + std::swap(_mlf, rhs._mlf); + std::swap(_last, rhs._last); + } + + // ------------------------------------------------------------- + inline iterator first() const { return {this, 0}; } + inline iterator last() const { return {this, _num_filled - 1}; } + + iterator begin() { return first(); } + const_iterator cbegin() const { return first(); } + const_iterator begin() const { return first(); } + + inline iterator end() { return {this, _num_filled}; } + inline const_iterator cend() const { return {this, _num_filled}; } + const_iterator end() const { return cend(); } + + size_type size() const { return _num_filled; } + bool empty() const { return _num_filled == 0; } + size_type bucket_count() const { return _num_buckets; } + + /// Returns average number of elements per bucket. + float load_factor() const { return static_cast(_num_filled) / (_mask + 1); } + + HashT& hash_function() const { return _hasher; } + EqT& key_eq() const { return _eq; } + + void max_load_factor(float mlf) + { + if (mlf < 1.0-1e-4 && mlf > 0.2f) + _mlf = (uint32_t)((1 << 27) / mlf); + } + + constexpr float max_load_factor() const { return (1 << 27) / (float)_mlf; } + constexpr size_type max_size() const { return (1ull << (sizeof(size_type) * 8 - 2)); } + constexpr size_type max_bucket_count() const { return max_size(); } + +#ifdef EMH_STATIS + //Returns the bucket number where the element with key k is located. + size_type bucket(const KeyT& key) const + { + const auto bucket = hash_bucket(key); + const auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return 0; + else if (bucket == next_bucket) + return bucket + 1; + + return hash_main(bucket) + 1; + } + + //Returns the number of elements in bucket n. + size_type bucket_size(const size_type bucket) const + { + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return 0; + + next_bucket = hash_main(bucket); + size_type ibucket_size = 1; + + //iterator each item in current main bucket + while (true) { + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == next_bucket) { + break; + } + ibucket_size ++; + next_bucket = nbucket; + } + return ibucket_size; + } + + size_type get_main_bucket(const size_type bucket) const + { + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return INACTIVE; + + return hash_main(bucket); + } + + size_type get_diss(size_type bucket, size_type next_bucket, const size_type slots) const + { + auto pbucket = reinterpret_cast(&_pairs[bucket]); + auto pnext = reinterpret_cast(&_pairs[next_bucket]); + if (pbucket / EMH_CACHE_LINE_SIZE == pnext / EMH_CACHE_LINE_SIZE) + return 0; + size_type diff = pbucket > pnext ? (pbucket - pnext) : (pnext - pbucket); + if (diff / EMH_CACHE_LINE_SIZE < slots - 1) + return diff / EMH_CACHE_LINE_SIZE + 1; + return slots - 1; + } + + int get_bucket_info(const size_type bucket, size_type steps[], const size_type slots) const + { + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return -1; + + const auto main_bucket = hash_main(bucket); + if (next_bucket == main_bucket) + return 1; + else if (main_bucket != bucket) + return 0; + + steps[get_diss(bucket, next_bucket, slots)] ++; + size_type ibucket_size = 2; + //find a new empty and linked it to tail + while (true) { + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == next_bucket) + break; + + steps[get_diss(nbucket, next_bucket, slots)] ++; + ibucket_size ++; + next_bucket = nbucket; + } + return (int)ibucket_size; + } + + void dump_statics() const + { + const uint32_t slots = 128; + size_type buckets[slots + 1] = {0}; + size_type steps[slots + 1] = {0}; + for (size_type bucket = 0; bucket < _num_buckets; ++bucket) { + auto bsize = get_bucket_info(bucket, steps, slots); + if (bsize > 0) + buckets[bsize] ++; + } + + size_type sumb = 0, collision = 0, sumc = 0, finds = 0, sumn = 0; + puts("============== buckets size ration ========="); + for (size_type i = 0; i < sizeof(buckets) / sizeof(buckets[0]); i++) { + const auto bucketsi = buckets[i]; + if (bucketsi == 0) + continue; + sumb += bucketsi; + sumn += bucketsi * i; + collision += bucketsi * (i - 1); + finds += bucketsi * i * (i + 1) / 2; + printf(" %2u %8u %2.2lf| %.2lf\n", i, bucketsi, bucketsi * 100.0 * i / _num_filled, sumn * 100.0 / _num_filled); + } + + puts("========== collision miss ration ==========="); + for (size_type i = 0; i < sizeof(steps) / sizeof(steps[0]); i++) { + sumc += steps[i]; + if (steps[i] <= 2) + continue; + printf(" %2u %8u %.2lf %.2lf\n", i, steps[i], steps[i] * 100.0 / collision, sumc * 100.0 / collision); + } + + if (sumb == 0) return; + printf(" _num_filled/bucket_size/packed collision/cache_miss/hit_find = %u/%.2lf/%zd/ %.2lf%%/%.2lf%%/%.2lf\n", + _num_filled, _num_filled * 1.0 / sumb, sizeof(value_type), (collision * 100.0 / _num_filled), (collision - steps[0]) * 100.0 / _num_filled, finds * 1.0 / _num_filled); + assert(sumn == _num_filled); + assert(sumc == collision); + puts("============== buckets size end ============="); + } +#endif + + // ------------------------------------------------------------ + template + iterator find(const KeyT& key) noexcept + { + return {this, find_filled_slot(key)}; + } + + template + const_iterator find(const K& key) const noexcept + { + return {this, find_filled_slot(key)}; + } + + template + bool contains(const K& key) const noexcept + { + return find_filled_slot(key) != _num_filled; + } + + template + size_type count(const K& key) const noexcept + { + return find_filled_slot(key) == _num_filled ? 0 : 1; + //return find_sorted_bucket(key) == END ? 0 : 1; + //return find_hash_bucket(key) == END ? 0 : 1; + } + + template + std::pair equal_range(const K& key) + { + const auto found = find(key); + if (found.second == _num_filled) + return { found, found }; + else + return { found, std::next(found) }; + } + + void merge(HashSet& rhs) + { + if (empty()) { + *this = std::move(rhs); + return; + } + + for (auto rit = rhs.begin(); rit != rhs.end(); ) { + auto fit = find(*rit); + if (fit == end()) { + insert_unique(*rit); + rit = rhs.erase(rit); + } else { + ++rit; + } + } + } + + // ----------------------------------------------------- + std::pair do_insert(const value_type& value) + { + const auto key_hash = hash_key(value); + const auto bucket = find_or_allocate(value, key_hash); + const auto empty = EMH_EMPTY(_index, bucket); + if (empty) { + EMH_NEW(value, bucket, key_hash); + } + + const auto slot = EMH_SLOT(_index, bucket); + return { {this, slot}, empty }; + } + + std::pair do_insert(value_type&& value) + { + const auto key_hash = hash_key(value); + const auto bucket = find_or_allocate(value, key_hash); + const auto empty = EMH_EMPTY(_index, bucket); + if (empty) { + EMH_NEW(std::forward(value), bucket, key_hash); + } + + const auto slot = EMH_SLOT(_index, bucket); + return { {this, slot}, empty }; + } + + template + std::pair do_insert(K&& key) + { + const auto key_hash = hash_key(key); + const auto bucket = find_or_allocate(key, key_hash); + const auto empty = EMH_EMPTY(_index, bucket); + if (empty) { + EMH_NEW(std::forward(key), bucket, key_hash); + } + + const auto slot = EMH_SLOT(_index, bucket); + return { {this, slot}, empty }; + } + + template + std::pair do_assign(K&& key) + { + check_expand_need(); + const auto key_hash = hash_key(key); + const auto bucket = find_or_allocate(key, key_hash); + const auto empty = EMH_EMPTY(_index, bucket); + if (empty) { + EMH_NEW(std::forward(key), bucket, key_hash); + } + + const auto slot = EMH_SLOT(_index, bucket); + return { {this, slot}, empty }; + } + + std::pair insert(const value_type& p) + { + check_expand_need(); + return do_insert(p); + } + + std::pair insert(value_type && p) + { + check_expand_need(); + return do_insert(std::move(p)); + } + + void insert(std::initializer_list ilist) + { + reserve(ilist.size() + _num_filled, false); + for (auto it = ilist.begin(); it != ilist.end(); ++it) + do_insert(*it); + } + + template + void insert(Iter first, Iter last) + { + reserve(std::distance(first, last) + _num_filled, false); + for (; first != last; ++first) + do_insert(*first); + } + + template + void insert_unique(Iter begin, Iter end) + { + reserve(std::distance(begin, end) + _num_filled, false); + for (; begin != end; ++begin) { + insert_unique(*begin); + } + } + + template + size_type insert_unique(K&& key) + { + check_expand_need(); + const auto key_hash = hash_key(key); + auto bucket = find_unique_bucket(key_hash); + EMH_NEW(std::forward(key), bucket, key_hash); + return bucket; + } + + size_type insert_unique(value_type&& value) + { + return insert_unique(std::move(value)); + } + + inline size_type insert_unique(const value_type& value) + { + return insert_unique(value); + } + + template + inline std::pair emplace(Args&&... args) + { + check_expand_need(); + return do_insert(std::forward(args)...); + } + + //no any optimize for position + template + iterator emplace_hint(const_iterator hint, Args&&... args) + { + (void)hint; + check_expand_need(); + return do_insert(std::forward(args)...).first; + } + + template + std::pair try_emplace(const KeyT& k, Args&&... args) + { + check_expand_need(); + return do_insert(k, std::forward(args)...); + } + + template + std::pair try_emplace(KeyT&& k, Args&&... args) + { + check_expand_need(); + return do_insert(std::move(k), std::forward(args)...); + } + + template + inline size_type emplace_unique(Args&&... args) + { + return insert_unique(std::forward(args)...); + } + + std::pair insert_or_assign(const KeyT& key) { return do_assign(key); } + std::pair insert_or_assign(KeyT&& key) { return do_assign(std::move(key)); } + + /// Erase an element from the hash table. + /// return 0 if element was not found + size_type erase(const KeyT& key) + { + const auto key_hash = hash_key(key); + const auto sbucket = find_filled_bucket(key, key_hash); + if (sbucket == END) + return 0; + + const auto main_bucket = key_hash & _mask; + erase_slot(sbucket, main_bucket); + return 1; + } + + //iterator erase(const_iterator begin_it, const_iterator end_it) + iterator erase(const const_iterator& cit) + { + const auto slot = (size_type)(cit.kv_ - _pairs); + size_type main_bucket; + const auto sbucket = find_slot_bucket(slot, main_bucket); //TODO + erase_slot(sbucket, main_bucket); + return {this, slot}; + } + + //only last >= first + iterator erase(const_iterator first, const_iterator last) + { + auto esize = long(last.kv_ - first.kv_); + auto tsize = long((_pairs + _num_filled) - last.kv_); //last to tail size + auto next = first; + while (tsize -- > 0) { + if (esize-- <= 0) + break; + next = ++erase(next); + } + + //fast erase from last + next = this->last(); + while (esize -- > 0) + next = --erase(next); + + return {this, size_type(next.kv_ - _pairs)}; + } + + template + size_type erase_if(Pred pred) + { + auto old_size = size(); + for (auto it = begin(); it != end();) { + if (pred(*it)) + it = erase(it); + else + ++it; + } + return old_size - size(); + } + + static constexpr bool is_triviall_destructable() + { +#if __cplusplus >= 201402L || _MSC_VER > 1600 + return !(std::is_trivially_destructible::value); +#else + return !(std::is_pod::value); +#endif + } + + static constexpr bool is_copy_trivially() + { +#if __cplusplus >= 201103L || _MSC_VER > 1600 + return (std::is_trivially_copyable::value); +#else + return (std::is_pod::value); +#endif + } + + void clearkv() + { + if (is_triviall_destructable()) { + while (_num_filled --) + _pairs[_num_filled].~value_type(); + } + } + + /// Remove all elements, keeping full capacity. + void clear() + { + if (_num_filled > 0) + memset((char*)_index, INACTIVE, sizeof(_index[0]) * _num_buckets); + + clearkv(); + + _last = _num_filled = 0; + } + + void shrink_to_fit(const float min_factor = EMH_DEFAULT_LOAD_FACTOR / 4) + { + if (load_factor() < min_factor && bucket_count() > 10) //safe guard + rehash(_num_filled); + } + + /// Make room for this many elements + bool reserve(uint64_t num_elems, bool force) + { + (void)force; + const auto required_buckets = (uint32_t)(num_elems * _mlf >> 27); + if (EMH_LIKELY(required_buckets < _mask)) // && !force + return false; + +#if EMH_STATIS + if (_num_filled > 1'000'000) dump_statics(); +#endif + + //assert(required_buckets < max_size()); + rehash(required_buckets + 2); + return true; + } + + static value_type* alloc_bucket(size_type num_buckets) + { +#if 0 + auto new_pairs = (char*)malloc(num_buckets * sizeof(value_type) + (EAD + num_buckets) * sizeof(Index)); +#else + auto new_pairs = (char*)malloc(num_buckets * sizeof(value_type)); +#endif + return (value_type *)(new_pairs); + } + + static Index* alloc_index(size_type num_buckets) + { + auto new_index = (char*)malloc((EAD + num_buckets) * sizeof(Index)); + return (Index *)(new_index); + } + + bool reserve(size_type required_buckets) + { + if (_num_filled != required_buckets) + return reserve(required_buckets, true); + + _last = 0; + + std::sort(_pairs, _pairs + _num_filled, [this](const value_type & l, const value_type & r) { + const auto hashl = (size_type)hash_key(l) & _mask, hashr = (size_type)hash_key(r) & _mask; + if (hashl != hashr) + return hashl < hashr; +#if 0 + return hashl < hashr; +#else + return l < r; +#endif + }); + + memset(_index, INACTIVE, sizeof(_index[0]) * _num_buckets); + for (size_type slot = 0; slot < _num_filled; slot++) { + const auto& key = EMH_KEY(_pairs, slot); + const auto key_hash = hash_key(key); + const auto bucket = size_type(key_hash & _mask); + auto& next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + EMH_INDEX(_index, bucket) = {1, slot | EMH_KEYMASK(key_hash, _mask)}; + else { + EMH_HSLOT(_index, bucket) |= EMH_KEYMASK(key_hash, _mask); + next_bucket ++; + } + } + return true; + } + + void rebuild(size_type num_buckets) + { + auto new_pairs = (value_type*)alloc_bucket(num_buckets * max_load_factor() + 4); + if (is_copy_trivially()) { + if (_pairs) + memcpy((char*)new_pairs, (char*)_pairs, _num_filled * sizeof(value_type)); + } else { + for (size_type slot = 0; slot < _num_filled; slot++) { + new(new_pairs + slot) value_type(std::move(_pairs[slot])); + if (is_triviall_destructable()) + _pairs[slot].~value_type(); + } + } + free(_pairs); _pairs = new_pairs; + } + + void rehash(uint64_t required_buckets) + { + if (required_buckets < _num_filled) + return; + + uint32_t num_buckets = _num_filled > (1u << 16) ? (1u << 16) : 4u; + while (num_buckets < required_buckets) { num_buckets *= 2; } + assert(num_buckets < max_size()); + +#if EMH_REHASH_LOG + auto last = _last; + size_type collision = 0; +#endif + + _last = 0; + _num_buckets = num_buckets; + _mask = num_buckets - 1; + + free(_index); + rebuild(num_buckets); + + _index = (Index*)alloc_index (num_buckets); + + memset((char*)_index, INACTIVE, sizeof(_index[0]) * num_buckets); + memset((char*)(_index + num_buckets), 0, sizeof(_index[0]) * EAD); + +#ifdef EMH_SORT + std::sort(_pairs, _pairs + _num_filled, [this](const value_type & l, const value_type & r) { + const auto hashl = hash_key(l), hashr = hash_key(r); + auto diff = int64_t((hashl & _mask) - (hashr & _mask)); + if (diff != 0) + return diff < 0; + return hashl < hashr; +// return l < r; + }); +#endif + + for (size_type slot = 0; slot < _num_filled; slot++) { + const auto& key = EMH_KEY(_pairs, slot); + const auto key_hash = hash_key(key); + const auto bucket = find_unique_bucket(key_hash); + EMH_INDEX(_index, bucket) = {bucket, slot | EMH_KEYMASK(key_hash, _mask)}; + +#if EMH_REHASH_LOG + if (bucket != hash_main(bucket)) + collision ++; +#endif + } + +#if EMH_REHASH_LOG + if (_num_filled > EMH_REHASH_LOG) { + auto mbucket = _num_filled - collision; + char buff[255] = {0}; + sprintf(buff, " _num_filled/aver_size/K.V/pack/collision|last = %u/%.2lf/%s.%s/%zd|%.2lf%%,%.2lf%%", + _num_filled, double (_num_filled) / mbucket, typeid(KeyT).name(), typeid(ValueT).name(), sizeof(_pairs[0]), collision * 100.0 / _num_filled, last * 100.0 / _num_buckets); +#ifdef EMH_LOG + static uint32_t ihashs = 0; EMH_LOG() << "hash_nums = " << ihashs ++ << "|" <<__FUNCTION__ << "|" << buff << endl; +#else + puts(buff); +#endif + } +#endif + } + +private: + // Can we fit another element? + inline bool check_expand_need() + { + return reserve(_num_filled, false); + } + + size_type slot_to_bucket(const size_type slot) const + { + size_type main_bucket; + return find_slot_bucket(slot, main_bucket); //TODO + } + + //very slow + void erase_slot(const size_type sbucket, const size_type main_bucket) + { + const auto slot = EMH_SLOT(_index, sbucket); + const auto ebucket = erase_bucket(sbucket, main_bucket); + const auto last_slot = --_num_filled; + if (EMH_LIKELY(slot != last_slot)) { + const auto last_bucket = slot_to_bucket(last_slot); + EMH_KEY(_pairs, slot) = std::move(EMH_KEY(_pairs, last_slot)); + EMH_HSLOT(_index, last_bucket) = slot | (EMH_HSLOT(_index, last_bucket) & ~_mask); + } + + if (is_triviall_destructable()) + _pairs[last_slot].~value_type(); + + EMH_INDEX(_index, ebucket) = {INACTIVE, END}; + } + + size_type erase_bucket(const size_type bucket, const size_type main_bucket) + { + const auto next_bucket = EMH_BUCKET(_index, bucket); + if (bucket == main_bucket) { + if (main_bucket != next_bucket) { + const auto nbucket = EMH_BUCKET(_index, next_bucket); + EMH_INDEX(_index, main_bucket) = { + (nbucket == next_bucket) ? main_bucket : nbucket, + EMH_HSLOT(_index, next_bucket) + }; + } + return next_bucket; + } + + const auto prev_bucket = find_prev_bucket(main_bucket, bucket); + EMH_BUCKET(_index, prev_bucket) = (bucket == next_bucket) ? prev_bucket : next_bucket; + return bucket; + } + + // Find the slot with this key, or return bucket size + size_type find_slot_bucket(const size_type slot, size_type& main_bucket) const + { + const auto key_hash = hash_key(EMH_KEY(_pairs, slot)); + const auto bucket = main_bucket = size_type(key_hash & _mask); +// if (EMH_EQHASH(bucket, key_hash)) { + if (slot == EMH_SLOT(_index, bucket)) + return bucket; +// } + + auto next_bucket = EMH_BUCKET(_index, bucket); + while (true) { + if (EMH_LIKELY(slot == EMH_SLOT(_index, next_bucket))) + return next_bucket; + next_bucket = EMH_BUCKET(_index, next_bucket); + } + + return 0; + } + + // Find the slot with this key, or return bucket size + size_type find_filled_bucket(const KeyT& key, uint64_t key_hash) const + { + const auto bucket = size_type(key_hash & _mask); + auto next_bucket = EMH_BUCKET(_index, bucket); + if (EMH_UNLIKELY((int)next_bucket < 0)) + return END; + + if (EMH_EQHASH(bucket, key_hash)) { + const auto slot = EMH_SLOT(_index, bucket); + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + return bucket; + } + if (next_bucket == bucket) + return END; + + while (true) { + if (EMH_EQHASH(next_bucket, key_hash)) { + const auto slot = EMH_SLOT(_index, next_bucket); + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + return next_bucket; + } + + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (EMH_UNLIKELY(nbucket == next_bucket)) + return END; + next_bucket = nbucket; + } + return 0; + } + + // Find the slot with this key, or return bucket size + size_type find_filled_slot(const KeyT& key) const + { + const auto key_hash = hash_key(key); + const auto bucket = size_type(key_hash & _mask); + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return _num_filled; + + if (EMH_EQHASH(bucket, key_hash)) { + const auto slot = EMH_SLOT(_index, bucket); + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + return slot; + } + if (next_bucket == bucket) + return _num_filled; + + while (true) { + if (EMH_EQHASH(next_bucket, key_hash)) { + const auto slot = EMH_SLOT(_index, next_bucket); + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + return slot; + } + + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (EMH_UNLIKELY(nbucket == next_bucket)) + return _num_filled; + next_bucket = nbucket; + } + + return 0; + } + + size_type find_hash_bucket(const KeyT& key) const + { + const auto key_hash = hash_key(key); + const auto bucket = size_type(key_hash & _mask); + const auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) + return END; + + auto slot = EMH_SLOT(_index, bucket); + if (_eq(key, EMH_KEY(_pairs, slot++))) + return slot; + else if (next_bucket == bucket) + return END; + + while (true) { + const auto& okey = EMH_KEY(_pairs, slot++); + if (_eq(key, okey)) + return slot; + + const auto hasho = hash_key(okey); + if ((hasho & _mask) != bucket) + break; + else if (hasho > key_hash) + break; + else if (EMH_UNLIKELY(slot >= _num_filled)) + break; + } + + return END; + } + + size_type find_sorted_bucket(const KeyT& key) const + { + const auto key_hash = hash_key(key); + const auto bucket = size_type(key_hash & _mask); + const auto slots = (int)(EMH_BUCKET(_index, bucket)); //TODO + if (slots < 0 /**|| key < EMH_KEY(_pairs, slot)*/) + return END; + + const auto slot = EMH_SLOT(_index, bucket); + auto ormask = _index[bucket].slot & ~_mask; + auto hmask = EMH_KEYMASK(key_hash, _mask); + if ((hmask | ormask) != ormask) + return END; + + if (_eq(key, EMH_KEY(_pairs, slot))) + return slot; + else if (slots == 1 || key < EMH_KEY(_pairs, slot)) + return END; + +#if 0 + if (key < EMH_KEY(_pairs, slot) || key > EMH_KEY(_pairs, slots + slot - 1)) + return END; +#endif + + for (size_type i = 1; i < slots; i++) { + const auto& okey = EMH_KEY(_pairs, slot + i); + if (_eq(key, okey)) + return slot + i; +// else if (okey > key) +// return END; + } + + return END; + } + + //kick out bucket and find empty to occpuy + //it will break the orgin link and relnik again. + //before: main_bucket-->prev_bucket --> bucket --> next_bucket + //atfer : main_bucket-->prev_bucket --> (removed)--> new_bucket--> next_bucket + size_type kickout_bucket(const size_type kmain, const size_type bucket) + { + const auto next_bucket = EMH_BUCKET(_index, bucket); + const auto new_bucket = find_empty_bucket(next_bucket); + const auto prev_bucket = find_prev_bucket(kmain, bucket); + + const auto oslot = EMH_HSLOT(_index, bucket); + if (next_bucket == bucket) + EMH_INDEX(_index, new_bucket) = {new_bucket, oslot}; + else + EMH_INDEX(_index, new_bucket) = {next_bucket, oslot}; + + EMH_BUCKET(_index, prev_bucket) = new_bucket; + EMH_BUCKET(_index, bucket) = INACTIVE; + + return bucket; + } + +/* +** inserts a new key into a hash table; first, check whether key's main +** bucket/position is free. If not, check whether colliding node/bucket is in its main +** position or not: if it is not, move colliding bucket to an empty place and +** put new key in its main position; otherwise (colliding bucket is in its main +** position), new key goes to an empty position. +*/ + size_type find_or_allocate(const KeyT& key, uint64_t key_hash) + { + const auto bucket = size_type(key_hash & _mask); + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) { + return bucket; + } + + const auto slot = EMH_SLOT(_index, bucket); + if (EMH_EQHASH(bucket, key_hash)) + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + return bucket; + + //check current bucket_key is in main bucket or not + const auto kmain = hash_bucket(EMH_KEY(_pairs, slot)); + if (kmain != bucket) + return kickout_bucket(kmain, bucket); + else if (next_bucket == bucket) + return EMH_BUCKET(_index, next_bucket) = find_empty_bucket(next_bucket); + + //find next linked bucket and check key + while (true) { + const auto slot2 = EMH_SLOT(_index, next_bucket); + if (EMH_UNLIKELY(EMH_EQHASH(next_bucket, key_hash))) { + if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot2)))) + return next_bucket; + } + + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == next_bucket) + break; + next_bucket = nbucket; + } + + //find a new empty and link it to tail + const auto new_bucket = find_empty_bucket(next_bucket); + return EMH_BUCKET(_index, next_bucket) = new_bucket; + } + + size_type find_unique_bucket(uint64_t key_hash) + { + const auto bucket = size_type(key_hash & _mask); + auto next_bucket = EMH_BUCKET(_index, bucket); + if ((int)next_bucket < 0) { + return bucket; + } + + //check current bucket_key is in main bucket or not + const auto kmain = hash_main(bucket); + if (EMH_UNLIKELY(kmain != bucket)) + return kickout_bucket(kmain, bucket); + else if (EMH_UNLIKELY(next_bucket != bucket)) + next_bucket = find_last_bucket(next_bucket); + + //find a new empty and link it to tail + return EMH_BUCKET(_index, next_bucket) = find_empty_bucket(next_bucket); + } + +/*** + Different probing techniques usually provide a trade-off between memory locality and avoidance of clustering. +Since Robin Hood hashing is relatively resilient to clustering (both primary and secondary), linear probing is the most cache friendly alternativeis typically used. + + It's the core algorithm of this hash map with highly optimization/benchmark. +normaly linear probing is inefficient with high load factor, it use a new 3-way linear +probing strategy to search empty slot. from benchmark even the load factor > 0.9, it's more 2-3 timer fast than +one-way search strategy. + +1. linear or quadratic probing a few cache line for less cache miss from input slot "bucket_from". +2. the first search slot from member variant "_last", init with 0 +3. the second search slot from calculated pos "(_num_filled + _last) & _mask", it's like a rand value +*/ + // key is not in this mavalue. Find a place to put it. + size_type find_empty_bucket(const size_type bucket_from) + { + auto bucket = bucket_from; + if (EMH_EMPTY(_index, ++bucket) || EMH_EMPTY(_index, ++bucket)) + return bucket; + + auto offset = 2u; + +#ifndef EMH_QUADRATIC + constexpr auto linear_probe_length = 2 + EMH_CACHE_LINE_SIZE / 16;//2 4 6 8 + for (; offset < linear_probe_length; offset += 2) { + auto bucket1 = (bucket + offset) & _mask; + if (EMH_EMPTY(_index, bucket1) || EMH_EMPTY(_index, ++bucket1)) + return bucket1; + } +#else + constexpr auto linear_probe_length = 10;//2 4 7 11 + for (auto step = offset; offset < linear_probe_length; offset += ++step) { + auto bucket1 = (bucket + offset) & _mask; + if (EMH_EMPTY(_index, bucket1) || EMH_EMPTY(_index, ++bucket1)) + return bucket1; + } +#endif + +#if 0 + while (true) { + _last &= _mask; + if (EMH_EMPTY(_index, _last++) || EMH_EMPTY(_index, _last++)) + return _last++ - 1; + +#if 1 + auto tail = _mask - (_last & _mask); + if (EMH_EMPTY(_index, tail) || EMH_EMPTY(_index, ++tail)) + return tail; +#endif +#if 0 + auto medium = (_num_filled + _last) & _mask; + if (EMH_EMPTY(_index, medium) || EMH_EMPTY(_index, ++medium)) + return medium; +#endif + } +#else + //for (auto slot = bucket + offset; ;slot += offset++) { + for (auto slot = bucket + offset; ; slot++) { +// if (EMH_EMPTY(_index, ++_last))// || EMH_EMPTY(_index, ++_last)) +// return _last ++; + + auto bucket1 = slot++ & _mask; + if (EMH_UNLIKELY(EMH_EMPTY(_index, bucket1)))// || EMH_UNLIKELY(EMH_EMPTY(_index, ++bucket1)))) + return bucket1; + + auto medium = (_num_filled + _last++) & _mask; + if (EMH_EMPTY(_index, medium) || EMH_EMPTY(_index, ++medium)) + return medium; + + ++_last &= _mask; + } +#endif + return 0; + } + + size_type find_last_bucket(size_type main_bucket) const + { + auto next_bucket = EMH_BUCKET(_index, main_bucket); + if (next_bucket == main_bucket) + return main_bucket; + + while (true) { + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == next_bucket) + return next_bucket; + next_bucket = nbucket; + } + } + + size_type find_prev_bucket(const size_type main_bucket, const size_type bucket) const + { + auto next_bucket = EMH_BUCKET(_index, main_bucket); + if (next_bucket == bucket) + return main_bucket; + + while (true) { + const auto nbucket = EMH_BUCKET(_index, next_bucket); + if (nbucket == bucket) + return next_bucket; + next_bucket = nbucket; + } + } + + inline size_type hash_bucket(const KeyT& key) const + { + return (size_type)hash_key(key) & _mask; + } + + inline size_type hash_main(const size_type bucket) const + { + const auto slot = EMH_SLOT(_index, bucket); + return (size_type)hash_key(EMH_KEY(_pairs, slot)) & _mask; + } + +#ifdef EMH_INT_HASH + static constexpr uint64_t KC = UINT64_C(11400714819323198485); + static uint64_t hash64(uint64_t key) + { +#if __SIZEOF_INT128__ + __uint128_t r = key; r *= KC; + return (uint64_t)(r >> 64) + (uint64_t)r; +#elif _WIN64 + uint64_t high; + return _umul128(key, KC, &high) + high; +#elif 1 + auto low = key; + auto high = (key >> 32) | (key << 32); + auto mix = (0x94d049bb133111ebull * low + 0xbf58476d1ce4e5b9ull * high); + return mix >> 32; +#elif 1 + uint64_t r = key * UINT64_C(0xca4bcaa75ec3f625); + return (r >> 32) + r; +#elif 1 + //MurmurHash3Mixer + uint64_t h = key; + h ^= h >> 33; + h *= 0xff51afd7ed558ccd; + h ^= h >> 33; + h *= 0xc4ceb9fe1a85ec53; + h ^= h >> 33; + return h; +#elif 1 + uint64_t x = key; + x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); + x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb); + x = x ^ (x >> 31); + return x; +#endif + } +#endif + +#if EMH_WYHASH_HASH + //#define WYHASH_CONDOM 1 + static inline uint64_t wymix(uint64_t A, uint64_t B) + { +#if defined(__SIZEOF_INT128__) + __uint128_t r = A; r *= B; +#if WYHASH_CONDOM + A ^= (uint64_t)r; B ^= (uint64_t)(r >> 64); +#else + A = (uint64_t)r; B = (uint64_t)(r >> 64); +#endif + +#elif defined(_MSC_VER) && defined(_M_X64) +#if WYHASH_CONDOM + uint64_t a, b; + a = _umul128(A, B, &b); + A ^= a; B ^= b; +#else + A = _umul128(A, B, &B); +#endif +#else + uint64_t ha = A >> 32, hb = B >> 32, la = (uint32_t)A, lb = (uint32_t)B, hi, lo; + uint64_t rh = ha * hb, rm0 = ha * lb, rm1 = hb * la, rl = la * lb, t = rl + (rm0 << 32), c = t < rl; + lo = t + (rm1 << 32); c += lo < t; hi = rh + (rm0 >> 32) + (rm1 >> 32) + c; +#if WYHASH_CONDOM + A ^= lo; B ^= hi; +#else + A = lo; B = hi; +#endif +#endif + return A ^ B; + } + + //multiply and xor mix function, aka MUM + static inline uint64_t wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v; } + static inline uint64_t wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return v; } + static inline uint64_t wyr3(const uint8_t *p, size_t k) { + return (((uint64_t)p[0]) << 16) | (((uint64_t)p[k >> 1]) << 8) | p[k - 1]; + } + + static constexpr uint64_t secret[4] = { + 0xa0761d6478bd642full, 0xe7037ed1a0b428dbull, + 0x8ebc6af09c88c6e3ull, 0x589965cc75374cc3ull}; + + //wyhash main function https://github.com/wangyi-fudan/wyhash + static uint64_t wyhashstr(const void *key, const size_t len) + { + uint64_t a = 0, b = 0, seed = secret[0]; + const uint8_t *p = (const uint8_t*)key; + if (EMH_LIKELY(len <= 16)) { + if (EMH_LIKELY(len >= 4)) { + const auto half = (len >> 3) << 2; + a = (wyr4(p) << 32U) | wyr4(p + half); p += len - 4; + b = (wyr4(p) << 32U) | wyr4(p - half); + } else if (len) { + a = wyr3(p, len); + } + } else { + size_t i = len; + if (EMH_UNLIKELY(i > 48)) { + uint64_t see1 = seed, see2 = seed; + do { + seed = wymix(wyr8(p + 0) ^ secret[1], wyr8(p + 8) ^ seed); + see1 = wymix(wyr8(p + 16) ^ secret[2], wyr8(p + 24) ^ see1); + see2 = wymix(wyr8(p + 32) ^ secret[3], wyr8(p + 40) ^ see2); + p += 48; i -= 48; + } while (EMH_LIKELY(i > 48)); + seed ^= see1 ^ see2; + } + while (i > 16) { + seed = wymix(wyr8(p) ^ secret[1], wyr8(p + 8) ^ seed); + i -= 16; p += 16; + } + a = wyr8(p + i - 16); + b = wyr8(p + i - 8); + } + + return wymix(secret[1] ^ len, wymix(a ^ secret[1], b ^ seed)); + } +#endif + + template::value, uint32_t>::type = 0> + inline uint64_t hash_key(const UType key) const + { +#ifdef EMH_INT_HASH + return hash64(key); +#elif EMH_IDENTITY_HASH + return (key + (key >> (sizeof(UType) * 4))); +#elif EMH_WYHASH64 + return wyhash64(key, KC); +#else + return _hasher(key); +#endif + } + +template::value, uint32_t>::type = 0> + inline uint64_t hash_key(const UType& key) const + { +#if EMH_WYHASH_HASH + return wyhashstr(key.data(), key.size()); +#elif WYHASH_LITTLE_ENDIAN + return wyhash(key.data(), key.size(), 0); +#else + return _hasher(key); +#endif + } + + template::value && !std::is_same::value, uint32_t>::type = 0> + inline uint64_t hash_key(const UType& key) const + { +#ifdef EMH_INT_HASH + return _hasher(key) * KC; +#else + return _hasher(key); +#endif + } + +private: + value_type*_pairs; + Index* _index; + + HashT _hasher; + EqT _eq; + uint32_t _mlf; + size_type _mask; + size_type _num_buckets; + size_type _num_filled; + size_type _last; +}; +} // namespace emhash diff --git a/libs/emhash/hash_table8.hpp b/libs/emhash/hash_table8.hpp index c971f980..5d78f2e1 100644 --- a/libs/emhash/hash_table8.hpp +++ b/libs/emhash/hash_table8.hpp @@ -1,5 +1,6 @@ // emhash8::HashMap for C++14/17 -// version 1.6.4 +// version 1.6.5 +// https://github.com/ktprime/emhash/blob/master/hash_table8.hpp // // Licensed under the MIT License . // SPDX-License-Identifier: MIT @@ -36,45 +37,25 @@ #include #include -#ifdef EMH_KEY - #undef EMH_KEY - #undef EMH_VAL - #undef EMH_KV - #undef EMH_BUCKET - #undef EMH_NEW - #undef EMH_EMPTY - #undef EMH_PREVET - #undef EMH_LIKELY - #undef EMH_UNLIKELY -#endif +#undef EMH_NEW +#undef EMH_EMPTY // likely/unlikely #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) # define EMH_LIKELY(condition) __builtin_expect(condition, 1) # define EMH_UNLIKELY(condition) __builtin_expect(condition, 0) #else -# define EMH_LIKELY(condition) (condition) -# define EMH_UNLIKELY(condition) (condition) +# define EMH_LIKELY(condition) condition +# define EMH_UNLIKELY(condition) condition #endif -#define EMH_KEY(p, n) p[n].first -#define EMH_VAL(p, n) p[n].second -#define EMH_KV(p, n) p[n] - -#define EMH_INDEX(i, n) i[n] -#define EMH_BUCKET(i, n) i[n].bucket -#define EMH_HSLOT(i, n) i[n].slot -#define EMH_SLOT(i, n) (i[n].slot & _mask) -#define EMH_PREVET(i, n) i[n].slot - -#define EMH_KEYMASK(key, mask) ((size_type)(key) & ~mask) -#define EMH_EQHASH(n, key_hash) (EMH_KEYMASK(key_hash, _mask) == (_index[n].slot & ~_mask)) +#define EMH_EMPTY(n) (0 > (int)(_index[n].next)) +#define EMH_EQHASH(n, key_hash) (((size_type)(key_hash) & ~_mask) == (_index[n].slot & ~_mask)) +//#define EMH_EQHASH(n, key_hash) ((size_type)(key_hash - _index[n].slot) & ~_mask) == 0 #define EMH_NEW(key, val, bucket, key_hash) \ new(_pairs + _num_filled) value_type(key, val); \ _etail = bucket; \ - _index[bucket] = {bucket, _num_filled++ | EMH_KEYMASK(key_hash, _mask)} - -#define EMH_EMPTY(i, n) (0 > (int)i[n].bucket) + _index[bucket] = {bucket, _num_filled++ | ((size_type)(key_hash) & ~_mask)} namespace emhash8 { @@ -112,7 +93,7 @@ class HashMap struct Index { - size_type bucket; + size_type next; size_type slot; }; @@ -233,6 +214,7 @@ class HashMap _index = nullptr; _mask = _num_buckets = 0; _num_filled = 0; + _mlf = (uint32_t)((1 << 27) / EMH_DEFAULT_LOAD_FACTOR); max_load_factor(mlf); rehash(bucket); } @@ -332,6 +314,8 @@ class HashMap clearkv(); free(_pairs); free(_index); + _index = nullptr; + _pairs = nullptr; } void clone(const HashMap& rhs) @@ -403,7 +387,7 @@ class HashMap void max_load_factor(float mlf) { - if (mlf < 0.991 && mlf > EMH_MIN_LOAD_FACTOR) { + if (mlf < 0.992 && mlf > EMH_MIN_LOAD_FACTOR) { _mlf = (uint32_t)((1 << 27) / mlf); if (_num_buckets > 0) rehash(_num_buckets); } @@ -418,7 +402,7 @@ class HashMap size_type bucket(const KeyT& key) const { const auto bucket = hash_bucket(key); - const auto next_bucket = EMH_BUCKET(_index, bucket); + const auto next_bucket = _index[bucket].next; if ((int)next_bucket < 0) return 0; else if (bucket == next_bucket) @@ -430,7 +414,7 @@ class HashMap //Returns the number of elements in bucket n. size_type bucket_size(const size_type bucket) const { - auto next_bucket = EMH_BUCKET(_index, bucket); + auto next_bucket = _index[bucket].next; if ((int)next_bucket < 0) return 0; @@ -439,7 +423,7 @@ class HashMap //iterator each item in current main bucket while (true) { - const auto nbucket = EMH_BUCKET(_index, next_bucket); + const auto nbucket = _index[next_bucket].next; if (nbucket == next_bucket) { break; } @@ -451,7 +435,7 @@ class HashMap size_type get_main_bucket(const size_type bucket) const { - auto next_bucket = EMH_BUCKET(_index, bucket); + auto next_bucket = _index[bucket].next; if ((int)next_bucket < 0) return INACTIVE; @@ -472,7 +456,7 @@ class HashMap int get_bucket_info(const size_type bucket, size_type steps[], const size_type slots) const { - auto next_bucket = EMH_BUCKET(_index, bucket); + auto next_bucket = _index[bucket].next; if ((int)next_bucket < 0) return -1; @@ -486,7 +470,7 @@ class HashMap size_type ibucket_size = 2; //find a empty and linked it to tail while (true) { - const auto nbucket = EMH_BUCKET(_index, next_bucket); + const auto nbucket = _index[next_bucket].next; if (nbucket == next_bucket) break; @@ -538,6 +522,11 @@ class HashMap } #endif + void pack_zero(ValueT zero) + { + _pairs[_num_filled] = {KeyT(), zero}; + } + // ------------------------------------------------------------ template inline iterator find(const K& key) noexcept @@ -556,7 +545,7 @@ class HashMap { const auto slot = find_filled_slot(key); //throw - return EMH_VAL(_pairs, slot); + return _pairs[slot].second; } template @@ -564,7 +553,17 @@ class HashMap { const auto slot = find_filled_slot(key); //throw - return EMH_VAL(_pairs, slot); + return _pairs[slot].second; + } + + const ValueT& index(const uint32_t index) const + { + return _pairs[index].second; + } + + ValueT& index(const uint32_t index) + { + return _pairs[index].second; } template @@ -615,7 +614,7 @@ class HashMap const auto slot = find_filled_slot(key); const auto found = slot != _num_filled; if (found) { - val = EMH_VAL(_pairs, slot); + val = _pairs[slot].second; } return found; } @@ -624,14 +623,14 @@ class HashMap ValueT* try_get(const KeyT& key) noexcept { const auto slot = find_filled_slot(key); - return slot != _num_filled ? &EMH_VAL(_pairs, slot) : nullptr; + return slot != _num_filled ? &_pairs[slot].second : nullptr; } /// Const version of the above ValueT* try_get(const KeyT& key) const noexcept { const auto slot = find_filled_slot(key); - return slot != _num_filled ? &EMH_VAL(_pairs, slot) : nullptr; + return slot != _num_filled ? &_pairs[slot].second : nullptr; } /// set value if key exist @@ -641,7 +640,7 @@ class HashMap if (slot == _num_filled) return false; - EMH_VAL(_pairs, slot) = val; + _pairs[slot].second = val; return true; } @@ -652,7 +651,7 @@ class HashMap if (slot == _num_filled) return false; - EMH_VAL(_pairs, slot) = std::move(val); + _pairs[slot].second = std::move(val); return true; } @@ -660,7 +659,7 @@ class HashMap ValueT get_or_return_default(const KeyT& key) const noexcept { const auto slot = find_filled_slot(key); - return slot == _num_filled ? ValueT() : EMH_VAL(_pairs, slot); + return slot == _num_filled ? ValueT() : _pairs[slot].second; } // ----------------------------------------------------- @@ -668,12 +667,12 @@ class HashMap { const auto key_hash = hash_key(value.first); const auto bucket = find_or_allocate(value.first, key_hash); - const auto bempty = EMH_EMPTY(_index, bucket); + const auto bempty = EMH_EMPTY(bucket); if (bempty) { EMH_NEW(value.first, value.second, bucket, key_hash); } - const auto slot = EMH_SLOT(_index, bucket); + const auto slot = _index[bucket].slot & _mask; return { {this, slot}, bempty }; } @@ -681,12 +680,12 @@ class HashMap { const auto key_hash = hash_key(value.first); const auto bucket = find_or_allocate(value.first, key_hash); - const auto bempty = EMH_EMPTY(_index, bucket); + const auto bempty = EMH_EMPTY(bucket); if (bempty) { EMH_NEW(std::move(value.first), std::move(value.second), bucket, key_hash); } - const auto slot = EMH_SLOT(_index, bucket); + const auto slot = _index[bucket].slot & _mask; return { {this, slot}, bempty }; } @@ -695,12 +694,12 @@ class HashMap { const auto key_hash = hash_key(key); const auto bucket = find_or_allocate(key, key_hash); - const auto bempty = EMH_EMPTY(_index, bucket); + const auto bempty = EMH_EMPTY(bucket); if (bempty) { EMH_NEW(std::forward(key), std::forward(val), bucket, key_hash); } - const auto slot = EMH_SLOT(_index, bucket); + const auto slot = _index[bucket].slot & _mask; return { {this, slot}, bempty }; } @@ -710,14 +709,14 @@ class HashMap check_expand_need(); const auto key_hash = hash_key(key); const auto bucket = find_or_allocate(key, key_hash); - const auto bempty = EMH_EMPTY(_index, bucket); + const auto bempty = EMH_EMPTY(bucket); if (bempty) { EMH_NEW(std::forward(key), std::forward(val), bucket, key_hash); } else { - EMH_VAL(_pairs, EMH_SLOT(_index, bucket)) = std::move(val); + _pairs[_index[bucket].slot & _mask].second = std::move(val); } - const auto slot = EMH_SLOT(_index, bucket); + const auto slot = _index[bucket].slot & _mask; return { {this, slot}, bempty }; } @@ -824,13 +823,13 @@ class HashMap check_expand_need(); const auto key_hash = hash_key(key); const auto bucket = find_or_allocate(key, key_hash); - if (EMH_EMPTY(_index, bucket)) { + if (EMH_EMPTY(bucket)) { EMH_NEW(key, val, bucket, key_hash); return ValueT(); } else { - const auto slot = EMH_SLOT(_index, bucket); + const auto slot = _index[bucket].slot & _mask; ValueT old_value(val); - std::swap(EMH_VAL(_pairs, slot), old_value); + std::swap(_pairs[slot].second, old_value); return old_value; } } @@ -841,13 +840,13 @@ class HashMap check_expand_need(); const auto key_hash = hash_key(key); const auto bucket = find_or_allocate(key, key_hash); - if (EMH_EMPTY(_index, bucket)) { + if (EMH_EMPTY(bucket)) { /* Check if inserting a value rather than overwriting an old entry */ EMH_NEW(key, std::move(ValueT()), bucket, key_hash); } - const auto slot = EMH_SLOT(_index, bucket); - return EMH_VAL(_pairs, slot); + const auto slot = _index[bucket].slot & _mask; + return _pairs[slot].second; } ValueT& operator[](KeyT&& key) noexcept @@ -855,12 +854,12 @@ class HashMap check_expand_need(); const auto key_hash = hash_key(key); const auto bucket = find_or_allocate(key, key_hash); - if (EMH_EMPTY(_index, bucket)) { + if (EMH_EMPTY(bucket)) { EMH_NEW(std::move(key), std::move(ValueT()), bucket, key_hash); } - const auto slot = EMH_SLOT(_index, bucket); - return EMH_VAL(_pairs, slot); + const auto slot = _index[bucket].slot & _mask; + return _pairs[slot].second; } /// Erase an element from the hash table. @@ -969,14 +968,15 @@ class HashMap } #if EMH_HIGH_LOAD + #define EMH_PREVET(i, n) i[n].slot void set_empty() { auto prev = 0; for (int32_t bucket = 1; bucket < _num_buckets; ++bucket) { - if (EMH_EMPTY(_index, bucket)) { + if (EMH_EMPTY(bucket)) { if (prev != 0) { EMH_PREVET(_index, bucket) = prev; - EMH_BUCKET(_index, prev) = -bucket; + _index[_prev].next = -bucket; } else _ehead = bucket; @@ -985,18 +985,18 @@ class HashMap } EMH_PREVET(_index, _ehead) = prev; - EMH_BUCKET(_index, prev) = 0-_ehead; - _ehead = 0-EMH_BUCKET(_index, _ehead); + _index[_prev].next = 0-_ehead; + _ehead = 0-_index[_ehead].next; } void clear_empty() { auto prev = EMH_PREVET(_index, _ehead); while (prev != _ehead) { - EMH_BUCKET(_index, prev) = INACTIVE; + _index[_prev].next = INACTIVE; prev = EMH_PREVET(_index, prev); } - EMH_BUCKET(_index, _ehead) = INACTIVE; + _index[_ehead].next = INACTIVE; _ehead = 0; } @@ -1004,10 +1004,10 @@ class HashMap size_type pop_empty(const size_type bucket) { const auto prev_bucket = EMH_PREVET(_index, bucket); - const int next_bucket = 0-EMH_BUCKET(_index, bucket); + const int next_bucket = 0-_index[bucket].next; EMH_PREVET(_index, next_bucket) = prev_bucket; - EMH_BUCKET(_index, prev_bucket) = -next_bucket; + _index[prev_bucket].next = -next_bucket; _ehead = next_bucket; return bucket; @@ -1016,14 +1016,14 @@ class HashMap //ehead->bucket->next void push_empty(const int32_t bucket) { - const int next_bucket = 0-EMH_BUCKET(_index, _ehead); + const int next_bucket = 0-_index[_ehead].next; assert(next_bucket > 0); EMH_PREVET(_index, bucket) = _ehead; - EMH_BUCKET(_index, bucket) = -next_bucket; + _index[bucket].next = -next_bucket; EMH_PREVET(_index, next_bucket) = bucket; - EMH_BUCKET(_index, _ehead) = -bucket; + _index[_ehead].next = -bucket; // _ehead = bucket; } #endif @@ -1049,7 +1049,7 @@ class HashMap if (_ehead == 0) { set_empty(); return false; - } else if (/*_num_filled + 100 < _num_buckets && */EMH_BUCKET(_index, _ehead) != 0-_ehead) { + } else if (/*_num_filled + 100 < _num_buckets && */_index[_ehead].next != 0-_ehead) { return false; } } @@ -1099,14 +1099,14 @@ class HashMap memset((char*)_index, INACTIVE, sizeof(_index[0]) * _num_buckets); for (size_type slot = 0; slot < _num_filled; slot++) { - const auto& key = EMH_KEY(_pairs, slot); + const auto& key = _pairs[slot].first; const auto key_hash = hash_key(key); const auto bucket = size_type(key_hash & _mask); - auto& next_bucket = EMH_BUCKET(_index, bucket); + auto& next_bucket = _index[bucket].next; if ((int)next_bucket < 0) - EMH_INDEX(_index, bucket) = {1, slot | EMH_KEYMASK(key_hash, _mask)}; + _index[bucket] = {1, slot | ((size_type)(key_hash) & ~_mask)}; else { - EMH_HSLOT(_index, bucket) |= EMH_KEYMASK(key_hash, _mask); + _index[bucket].slot |= (size_type)(key_hash) & ~_mask; next_bucket ++; } } @@ -1180,10 +1180,10 @@ class HashMap _etail = INACTIVE; for (size_type slot = 0; slot < _num_filled; ++slot) { - const auto& key = EMH_KEY(_pairs, slot); + const auto& key = _pairs[slot].first; const auto key_hash = hash_key(key); const auto bucket = find_unique_bucket(key_hash); - EMH_INDEX(_index, bucket) = {bucket, slot | EMH_KEYMASK(key_hash, _mask)}; + _index[bucket] = { bucket, slot | ((size_type)(key_hash) & ~_mask) }; #if EMH_REHASH_LOG if (bucket != hash_main(bucket)) @@ -1222,22 +1222,22 @@ class HashMap //very slow void erase_slot(const size_type sbucket, const size_type main_bucket) noexcept { - const auto slot = EMH_SLOT(_index, sbucket); + const auto slot = _index[sbucket].slot & _mask; const auto ebucket = erase_bucket(sbucket, main_bucket); const auto last_slot = --_num_filled; if (EMH_LIKELY(slot != last_slot)) { const auto last_bucket = (_etail == INACTIVE || ebucket == _etail) ? slot_to_bucket(last_slot) : _etail; - EMH_KV(_pairs, slot) = std::move(EMH_KV(_pairs, last_slot)); - EMH_HSLOT(_index, last_bucket) = slot | (EMH_HSLOT(_index, last_bucket) & ~_mask); + _pairs[slot] = std::move(_pairs[last_slot]); + _index[last_bucket].slot = slot | (_index[last_bucket].slot & ~_mask); } if (is_triviall_destructable()) _pairs[last_slot].~value_type(); _etail = INACTIVE; - EMH_INDEX(_index, ebucket) = {INACTIVE, 0}; + _index[ebucket] = {INACTIVE, 0}; #if EMH_HIGH_LOAD if (_ehead) { if (10 * _num_filled < 8 * _num_buckets) @@ -1250,36 +1250,36 @@ class HashMap size_type erase_bucket(const size_type bucket, const size_type main_bucket) noexcept { - const auto next_bucket = EMH_BUCKET(_index, bucket); + const auto next_bucket = _index[bucket].next; if (bucket == main_bucket) { if (main_bucket != next_bucket) { - const auto nbucket = EMH_BUCKET(_index, next_bucket); - EMH_INDEX(_index, main_bucket) = { + const auto nbucket = _index[next_bucket].next; + _index[main_bucket] = { (nbucket == next_bucket) ? main_bucket : nbucket, - EMH_HSLOT(_index, next_bucket) + _index[next_bucket].slot }; } return next_bucket; } const auto prev_bucket = find_prev_bucket(main_bucket, bucket); - EMH_BUCKET(_index, prev_bucket) = (bucket == next_bucket) ? prev_bucket : next_bucket; + _index[prev_bucket].next = (bucket == next_bucket) ? prev_bucket : next_bucket; return bucket; } // Find the slot with this key, or return bucket size size_type find_slot_bucket(const size_type slot, size_type& main_bucket) const { - const auto key_hash = hash_key(EMH_KEY(_pairs, slot)); + const auto key_hash = hash_key(_pairs[slot].first); const auto bucket = main_bucket = size_type(key_hash & _mask); - if (slot == EMH_SLOT(_index, bucket)) + if (slot == (_index[bucket].slot & _mask)) return bucket; - auto next_bucket = EMH_BUCKET(_index, bucket); + auto next_bucket = _index[bucket].next; while (true) { - if (EMH_LIKELY(slot == EMH_SLOT(_index, next_bucket))) + if (EMH_LIKELY(slot == (_index[next_bucket].slot & _mask))) return next_bucket; - next_bucket = EMH_BUCKET(_index, next_bucket); + next_bucket = _index[next_bucket].next; } return INACTIVE; @@ -1289,13 +1289,13 @@ class HashMap size_type find_filled_bucket(const KeyT& key, uint64_t key_hash) const noexcept { const auto bucket = size_type(key_hash & _mask); - auto next_bucket = EMH_BUCKET(_index, bucket); + auto next_bucket = _index[bucket].next; if (EMH_UNLIKELY((int)next_bucket < 0)) return INACTIVE; if (EMH_EQHASH(bucket, key_hash)) { - const auto slot = EMH_SLOT(_index, bucket); - if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + const auto slot = _index[bucket].slot & _mask; + if (EMH_LIKELY(_eq(key, _pairs[slot].first))) return bucket; } if (next_bucket == bucket) @@ -1303,12 +1303,12 @@ class HashMap while (true) { if (EMH_EQHASH(next_bucket, key_hash)) { - const auto slot = EMH_SLOT(_index, next_bucket); - if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + const auto slot = _index[next_bucket].slot & _mask; + if (EMH_LIKELY(_eq(key, _pairs[slot].first))) return next_bucket; } - const auto nbucket = EMH_BUCKET(_index, next_bucket); + const auto nbucket = _index[next_bucket].next; if (nbucket == next_bucket) return INACTIVE; next_bucket = nbucket; @@ -1319,55 +1319,55 @@ class HashMap // Find the slot with this key, or return bucket size template - size_type find_filled_slot(const K& key) const noexcept - { - const auto key_hash = hash_key(key); - const auto bucket = size_type(key_hash & _mask); - auto next_bucket = EMH_BUCKET(_index, bucket); - if ((int)next_bucket < 0) - return _num_filled; - - if (EMH_EQHASH(bucket, key_hash)) { - const auto slot = EMH_SLOT(_index, bucket); - if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) - return slot; - } - if (next_bucket == bucket) - return _num_filled; + size_type find_filled_slot(const K& key) const noexcept + { + const auto key_hash = hash_key(key); + const auto bucket = size_type(key_hash & _mask); + auto next_bucket = _index[bucket].next; + if ((int)next_bucket < 0) + return _num_filled; - while (true) { - if (EMH_EQHASH(next_bucket, key_hash)) { - const auto slot = EMH_SLOT(_index, next_bucket); - if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) + if (EMH_EQHASH(bucket, key_hash)) { + const auto slot = _index[bucket].slot & _mask; + if (EMH_LIKELY(_eq(key, _pairs[slot].first))) return slot; } - - const auto nbucket = EMH_BUCKET(_index, next_bucket); - if (nbucket == next_bucket) + if (next_bucket == bucket) return _num_filled; - next_bucket = nbucket; - } - return _num_filled; - } + while (true) { + if (EMH_EQHASH(next_bucket, key_hash)) { + const auto slot = _index[next_bucket].slot & _mask; + if (EMH_LIKELY(_eq(key, _pairs[slot].first))) + return slot; + } + + const auto nbucket = _index[next_bucket].next; + if (nbucket == next_bucket) + return _num_filled; + next_bucket = nbucket; + } + + return _num_filled; + } #if EMH_SORT size_type find_hash_bucket(const KeyT& key) const noexcept { const auto key_hash = hash_key(key); const auto bucket = size_type(key_hash & _mask); - const auto next_bucket = EMH_BUCKET(_index, bucket); + const auto next_bucket = _index[bucket].next; if ((int)next_bucket < 0) return END; - auto slot = EMH_SLOT(_index, bucket); - if (_eq(key, EMH_KEY(_pairs, slot++))) + auto slot = _index[bucket].slot & _mask; + if (_eq(key, _pairs[slot++].first)) return slot; else if (next_bucket == bucket) return END; while (true) { - const auto& okey = EMH_KEY(_pairs, slot++); + const auto& okey = _pairs[slot++].first; if (_eq(key, okey)) return slot; @@ -1388,32 +1388,32 @@ class HashMap { const auto key_hash = hash_key(key); const auto bucket = size_type(key_hash & _mask); - const auto slots = (int)(EMH_BUCKET(_index, bucket)); //TODO - if (slots < 0 /**|| key < EMH_KEY(_pairs, slot)*/) + const auto slots = (int)(_index[bucket].next); //TODO + if (slots < 0 /**|| key < _pairs[slot].first*/) return END; - const auto slot = EMH_SLOT(_index, bucket); + const auto slot = _index[bucket].slot & _mask; auto ormask = _index[bucket].slot & ~_mask; - auto hmask = EMH_KEYMASK(key_hash, _mask); + auto hmask = (size_type)(key_hash) & ~_mask; if ((hmask | ormask) != ormask) return END; - if (_eq(key, EMH_KEY(_pairs, slot))) + if (_eq(key, _pairs[slot].first)) return slot; - else if (slots == 1 || key < EMH_KEY(_pairs, slot)) + else if (slots == 1 || key < _pairs[slot].first) return END; #if EMH_SORT - if (key < EMH_KEY(_pairs, slot) || key > EMH_KEY(_pairs, slots + slot - 1)) + if (key < _pairs[slot].first || key > _pairs[slots + slot - 1].first) return END; #endif for (size_type i = 1; i < slots; ++i) { - const auto& okey = EMH_KEY(_pairs, slot + i); + const auto& okey = _pairs[slot + i].first; if (_eq(key, okey)) return slot + i; -// else if (okey > key) -// return END; + // else if (okey > key) + // return END; } return END; @@ -1426,76 +1426,76 @@ class HashMap //atfer : main_bucket-->prev_bucket --> (removed)--> new_bucket--> next_bucket size_type kickout_bucket(const size_type kmain, const size_type bucket) noexcept { - const auto next_bucket = EMH_BUCKET(_index, bucket); + const auto next_bucket = _index[bucket].next; const auto new_bucket = find_empty_bucket(next_bucket, 2); const auto prev_bucket = find_prev_bucket(kmain, bucket); const auto last = next_bucket == bucket ? new_bucket : next_bucket; - EMH_INDEX(_index, new_bucket) = {last, EMH_HSLOT(_index, bucket)}; + _index[new_bucket] = {last, _index[bucket].slot}; - EMH_BUCKET(_index, prev_bucket) = new_bucket; - EMH_BUCKET(_index, bucket) = INACTIVE; + _index[prev_bucket].next = new_bucket; + _index[bucket].next = INACTIVE; return bucket; } -/* -** inserts a new key into a hash table; first, check whether key's main -** bucket/position is free. If not, check whether colliding node/bucket is in its main -** position or not: if it is not, move colliding bucket to an empty place and -** put new key in its main position; otherwise (colliding bucket is in its main -** position), new key goes to an empty position. -*/ + /* + ** inserts a new key into a hash table; first, check whether key's main + ** bucket/position is free. If not, check whether colliding node/bucket is in its main + ** position or not: if it is not, move colliding bucket to an empty place and + ** put new key in its main position; otherwise (colliding bucket is in its main + ** position), new key goes to an empty position. + */ template - size_type find_or_allocate(const K& key, uint64_t key_hash) noexcept - { - const auto bucket = size_type(key_hash & _mask); - auto next_bucket = EMH_BUCKET(_index, bucket); - if ((int)next_bucket < 0) { + size_type find_or_allocate(const K& key, uint64_t key_hash) noexcept + { + const auto bucket = size_type(key_hash & _mask); + auto next_bucket = _index[bucket].next; + if ((int)next_bucket < 0) { #if EMH_HIGH_LOAD - if (next_bucket != INACTIVE) - pop_empty(bucket); + if (next_bucket != INACTIVE) + pop_empty(bucket); #endif - return bucket; - } - - const auto slot = EMH_SLOT(_index, bucket); - if (EMH_EQHASH(bucket, key_hash)) - if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, slot)))) - return bucket; + return bucket; + } - //check current bucket_key is in main bucket or not - const auto kmain = hash_bucket(EMH_KEY(_pairs, slot)); - if (kmain != bucket) - return kickout_bucket(kmain, bucket); - else if (next_bucket == bucket) - return EMH_BUCKET(_index, next_bucket) = find_empty_bucket(next_bucket, 1); + const auto slot = _index[bucket].slot & _mask; + if (EMH_EQHASH(bucket, key_hash)) + if (EMH_LIKELY(_eq(key, _pairs[slot].first))) + return bucket; + + //check current bucket_key is in main bucket or not + const auto kmain = hash_bucket(_pairs[slot].first); + if (kmain != bucket) + return kickout_bucket(kmain, bucket); + else if (next_bucket == bucket) + return _index[next_bucket].next = find_empty_bucket(next_bucket, 1); + + uint32_t csize = 1; + //find next linked bucket and check key + while (true) { + const auto eslot = _index[next_bucket].slot & _mask; + if (EMH_EQHASH(next_bucket, key_hash)) { + if (EMH_LIKELY(_eq(key, _pairs[eslot].first))) + return next_bucket; + } - uint32_t csize = 1; - //find next linked bucket and check key - while (true) { - const auto eslot = EMH_SLOT(_index, next_bucket); - if (EMH_EQHASH(next_bucket, key_hash)) { - if (EMH_LIKELY(_eq(key, EMH_KEY(_pairs, eslot)))) - return next_bucket; + csize += 1; + const auto nbucket = _index[next_bucket].next; + if (nbucket == next_bucket) + break; + next_bucket = nbucket; } - csize += 1; - const auto nbucket = EMH_BUCKET(_index, next_bucket); - if (nbucket == next_bucket) - break; - next_bucket = nbucket; + //find a empty and link it to tail + const auto new_bucket = find_empty_bucket(next_bucket, csize); + return _index[next_bucket].next = new_bucket; } - //find a empty and link it to tail - const auto new_bucket = find_empty_bucket(next_bucket, csize); - return EMH_BUCKET(_index, next_bucket) = new_bucket; - } - size_type find_unique_bucket(uint64_t key_hash) noexcept { const auto bucket = size_type(key_hash & _mask); - auto next_bucket = EMH_BUCKET(_index, bucket); + auto next_bucket = _index[bucket].next; if ((int)next_bucket < 0) { #if EMH_HIGH_LOAD if (next_bucket != INACTIVE) @@ -1511,39 +1511,40 @@ class HashMap else if (EMH_UNLIKELY(next_bucket != bucket)) next_bucket = find_last_bucket(next_bucket); - return EMH_BUCKET(_index, next_bucket) = find_empty_bucket(next_bucket, 2); + return _index[next_bucket].next = find_empty_bucket(next_bucket, 2); } -/*** - Different probing techniques usually provide a trade-off between memory locality and avoidance of clustering. -Since Robin Hood hashing is relatively resilient to clustering (both primary and secondary), linear probing is the most cache friendly alternativeis typically used. + /*** + Different probing techniques usually provide a trade-off between memory locality and avoidance of clustering. + Since Robin Hood hashing is relatively resilient to clustering (both primary and secondary), linear probing is the most cache friendly alternativeis typically used. - It's the core algorithm of this hash map with highly optimization/benchmark. -normaly linear probing is inefficient with high load factor, it use a new 3-way linear -probing strategy to search empty slot. from benchmark even the load factor > 0.9, it's more 2-3 timer fast than -one-way search strategy. + It's the core algorithm of this hash map with highly optimization/benchmark. + normaly linear probing is inefficient with high load factor, it use a new 3-way linear + probing strategy to search empty slot. from benchmark even the load factor > 0.9, it's more 2-3 timer fast than + one-way search strategy. -1. linear or quadratic probing a few cache line for less cache miss from input slot "bucket_from". -2. the first search slot from member variant "_last", init with 0 -3. the second search slot from calculated pos "(_num_filled + _last) & _mask", it's like a rand value -*/ + 1. linear or quadratic probing a few cache line for less cache miss from input slot "bucket_from". + 2. the first search slot from member variant "_last", init with 0 + 3. the second search slot from calculated pos "(_num_filled + _last) & _mask", it's like a rand value + */ // key is not in this mavalue. Find a place to put it. size_type find_empty_bucket(const size_type bucket_from, uint32_t csize) noexcept { + (void)csize; #if EMH_HIGH_LOAD if (_ehead) return pop_empty(_ehead); #endif auto bucket = bucket_from; - if (EMH_EMPTY(_index, ++bucket) || EMH_EMPTY(_index, ++bucket)) + if (EMH_EMPTY(++bucket) || EMH_EMPTY(++bucket)) return bucket; #ifdef EMH_QUADRATIC constexpr size_type linear_probe_length = 2 * EMH_CACHE_LINE_SIZE / sizeof(Index);//16 for (size_type offset = csize + 2, step = 4; offset <= linear_probe_length; ) { bucket = (bucket_from + offset) & _mask; - if (EMH_EMPTY(_index, bucket) || EMH_EMPTY(_index, ++bucket)) + if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket)) return bucket; offset += step; //7/8. 12. 16 } @@ -1551,9 +1552,9 @@ one-way search strategy. constexpr size_type quadratic_probe_length = 6u; for (size_type offset = 4u, step = 3u; step < quadratic_probe_length; ) { bucket = (bucket_from + offset) & _mask; - if (EMH_EMPTY(_index, bucket) || EMH_EMPTY(_index, ++bucket)) + if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket)) return bucket; - offset += step++;//3.4.5 + offset += step++; } #endif @@ -1564,23 +1565,23 @@ one-way search strategy. for (;;) { #if EMH_PACK_TAIL //find empty bucket and skip next - if (EMH_EMPTY(_index, _last++))// || EMH_EMPTY(_index, _last++)) + if (EMH_EMPTY(_last++))// || EMH_EMPTY(_last++)) return _last++ - 1; if (EMH_UNLIKELY(_last >= _num_buckets)) _last = 0; auto medium = (_mask / 4 + _last++) & _mask; - if (EMH_EMPTY(_index, medium)) + if (EMH_EMPTY(medium)) return medium; #else - if (EMH_EMPTY(_index, ++_last))// || EMH_EMPTY(_index, ++_last)) - return _last++; + if (EMH_EMPTY(++_last))// || EMH_EMPTY(++_last)) + return _last; _last &= _mask; auto medium = (_num_buckets / 2 + _last) & _mask; - if (EMH_EMPTY(_index, medium))// || EMH_EMPTY(_index, ++medium)) - return _last = medium; + if (EMH_EMPTY(medium))// || EMH_EMPTY(++medium)) + return medium; #endif } @@ -1589,12 +1590,12 @@ one-way search strategy. size_type find_last_bucket(size_type main_bucket) const { - auto next_bucket = EMH_BUCKET(_index, main_bucket); + auto next_bucket = _index[main_bucket].next; if (next_bucket == main_bucket) return main_bucket; while (true) { - const auto nbucket = EMH_BUCKET(_index, next_bucket); + const auto nbucket = _index[next_bucket].next; if (nbucket == next_bucket) return next_bucket; next_bucket = nbucket; @@ -1603,12 +1604,12 @@ one-way search strategy. size_type find_prev_bucket(const size_type main_bucket, const size_type bucket) const { - auto next_bucket = EMH_BUCKET(_index, main_bucket); + auto next_bucket = _index[main_bucket].next; if (next_bucket == bucket) return main_bucket; while (true) { - const auto nbucket = EMH_BUCKET(_index, next_bucket); + const auto nbucket = _index[next_bucket].next; if (nbucket == bucket) return next_bucket; next_bucket = nbucket; @@ -1622,8 +1623,8 @@ one-way search strategy. inline size_type hash_main(const size_type bucket) const noexcept { - const auto slot = EMH_SLOT(_index, bucket); - return (size_type)hash_key(EMH_KEY(_pairs, slot)) & _mask; + const auto slot = _index[bucket].slot & _mask; + return (size_type)hash_key(_pairs[slot].first) & _mask; } #if EMH_INT_HASH @@ -1750,32 +1751,32 @@ one-way search strategy. private: template::value, uint32_t>::type = 0> - inline uint64_t hash_key(const UType key) const - { + inline uint64_t hash_key(const UType key) const + { #if EMH_INT_HASH - return hash64(key); + return hash64(key); #elif EMH_IDENTITY_HASH - return key + (key >> 24); + return key + (key >> 24); #else - return _hasher(key); + return _hasher(key); #endif - } + } template::value, uint32_t>::type = 0> - inline uint64_t hash_key(const UType& key) const - { + inline uint64_t hash_key(const UType& key) const + { #if EMH_WYHASH_HASH - return wyhashstr(key.data(), key.size()); + return wyhashstr(key.data(), key.size()); #else - return _hasher(key); + return _hasher(key); #endif - } + } template::value && !std::is_same::value, uint32_t>::type = 0> - inline uint64_t hash_key(const UType& key) const - { - return _hasher(key); - } + inline uint64_t hash_key(const UType& key) const + { + return _hasher(key); + } private: Index* _index; diff --git a/libs/nlohmann/json.hpp b/libs/nlohmann/json.hpp index ddd3131d..14bc07d5 100644 --- a/libs/nlohmann/json.hpp +++ b/libs/nlohmann/json.hpp @@ -2485,6 +2485,14 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif #endif +#ifndef JSON_HAS_STATIC_RTTI + #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 + #define JSON_HAS_STATIC_RTTI 1 + #else + #define JSON_HAS_STATIC_RTTI 0 + #endif +#endif + #ifdef JSON_HAS_CPP_17 #define JSON_INLINE_VARIABLE inline #else @@ -2763,7 +2771,6 @@ JSON_HEDLEY_DIAGNOSTIC_POP inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - // inspired from https://stackoverflow.com/a/26745591 // allows to call any std function as if (e.g. with begin): // using std::begin; begin(x); @@ -3617,7 +3624,6 @@ template struct is_default_constructible> : conjunction...> {}; - template struct is_constructible : std::is_constructible {}; @@ -3633,7 +3639,6 @@ struct is_constructible> : is_default_constructible struct is_constructible> : is_default_constructible> {}; - template struct is_iterator_traits : std::false_type {}; @@ -4043,7 +4048,6 @@ struct value_in_range_of_impl2 } }; - template struct value_in_range_of_impl2 { @@ -4290,7 +4294,6 @@ inline OutStringType concat(Args && ... args) NLOHMANN_JSON_NAMESPACE_END - NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -5151,10 +5154,10 @@ template class iteration_proxy_value // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions iteration_proxy_value(iteration_proxy_value&&) noexcept(std::is_nothrow_move_constructible::value - && std::is_nothrow_move_constructible::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + && std::is_nothrow_move_constructible::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations) iteration_proxy_value& operator=(iteration_proxy_value&&) noexcept(std::is_nothrow_move_assignable::value - && std::is_nothrow_move_assignable::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + && std::is_nothrow_move_assignable::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations) ~iteration_proxy_value() = default; /// dereference operator (needed for range-based for) @@ -6144,7 +6147,6 @@ class file_input_adapter std::FILE* m_file; }; - /*! Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at beginning of input. Does not support changing the underlying std::streambuf @@ -6243,7 +6245,6 @@ class iterator_input_adapter } }; - template struct wide_string_input_helper; @@ -6367,7 +6368,7 @@ struct wide_string_input_helper } }; -// Wraps another input apdater to convert wide character types into individual bytes. +// Wraps another input adapter to convert wide character types into individual bytes. template class wide_string_input_adapter { @@ -6412,7 +6413,6 @@ class wide_string_input_adapter std::size_t utf8_bytes_filled = 0; }; - template struct iterator_input_adapter_factory { @@ -6714,7 +6714,6 @@ struct json_sax virtual ~json_sax() = default; }; - namespace detail { /*! @@ -7163,7 +7162,7 @@ class json_sax_dom_callback_parser if (ref_stack.empty()) { root = std::move(value); - return {true, &root}; + return {true, & root}; } // skip this value if we already decided to skip the parent @@ -7180,7 +7179,7 @@ class json_sax_dom_callback_parser if (ref_stack.back()->is_array()) { ref_stack.back()->m_data.m_value.array->emplace_back(std::move(value)); - return {true, &(ref_stack.back()->m_data.m_value.array->back())}; + return {true, & (ref_stack.back()->m_data.m_value.array->back())}; } // object @@ -7527,7 +7526,7 @@ class lexer : public lexer_base for (auto range = ranges.begin(); range != ranges.end(); ++range) { get(); - if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) // NOLINT(bugprone-inc-dec-in-conditions) { add(current); } @@ -9133,7 +9132,6 @@ static inline bool little_endianness(int num = 1) noexcept return *reinterpret_cast(&num) == 1; } - /////////////////// // binary reader // /////////////////// @@ -19278,7 +19276,9 @@ NLOHMANN_JSON_NAMESPACE_END #if defined(JSON_HAS_CPP_17) - #include + #if JSON_HAS_STATIC_RTTI + #include + #endif #include #endif @@ -19408,7 +19408,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} - ///////////////////// // container types // ///////////////////// @@ -19450,7 +19449,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} - /// @brief returns the allocator associated with the container /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/ static allocator_type get_allocator() @@ -19513,7 +19511,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; #endif - #if defined(_MSVC_LANG) result["compiler"]["c++"] = std::to_string(_MSVC_LANG); #elif defined(__cplusplus) @@ -19524,7 +19521,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return result; } - /////////////////////////// // JSON value data types // /////////////////////////// @@ -20126,7 +20122,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec bool is_an_object = std::all_of(init.begin(), init.end(), [](const detail::json_ref& element_ref) { - return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); + // The cast is to ensure op[size_type] is called, bearing in mind size_type may not be int; + // (many string types can be constructed from 0 via its null-pointer guise, so we get a + // broken call to op[key_type], the wrong semantics and a 4804 warning on Windows) + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[static_cast(0)].is_string(); }); // adjust type if type deduction is not wanted @@ -20346,7 +20345,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec assert_invariant(); } - /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// @@ -21104,7 +21102,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) detail::negation>, #endif -#if defined(JSON_HAS_CPP_17) +#if defined(JSON_HAS_CPP_17) && JSON_HAS_STATIC_RTTI detail::negation>, #endif detail::is_detected_lazy @@ -21141,7 +21139,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} - //////////////////// // element access // //////////////////// @@ -21856,7 +21853,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} - //////////// // lookup // //////////// @@ -21974,7 +21970,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} - /////////////// // iterators // /////////////// @@ -22113,7 +22108,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} - ////////////// // capacity // ////////////// @@ -22235,7 +22229,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @} - /////////////// // modifiers // /////////////// @@ -22683,7 +22676,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec void swap(reference other) noexcept ( std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value&& - std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_constructible::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap) std::is_nothrow_move_assignable::value ) { @@ -22700,7 +22693,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec friend void swap(reference left, reference right) noexcept ( std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value&& - std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_constructible::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap) std::is_nothrow_move_assignable::value ) { @@ -22709,7 +22702,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ - void swap(array_t& other) // NOLINT(bugprone-exception-escape) + void swap(array_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { // swap only works for arrays if (JSON_HEDLEY_LIKELY(is_array())) @@ -22725,7 +22718,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ - void swap(object_t& other) // NOLINT(bugprone-exception-escape) + void swap(object_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { // swap only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -22741,7 +22734,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ - void swap(string_t& other) // NOLINT(bugprone-exception-escape) + void swap(string_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { // swap only works for strings if (JSON_HEDLEY_LIKELY(is_string())) @@ -22757,7 +22750,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief exchanges the values /// @sa https://json.nlohmann.me/api/basic_json/swap/ - void swap(binary_t& other) // NOLINT(bugprone-exception-escape) + void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) { // swap only works for strings if (JSON_HEDLEY_LIKELY(is_binary())) @@ -23222,7 +23215,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec #endif // JSON_NO_IO /// @} - ///////////////////// // deserialization // ///////////////////// @@ -23403,7 +23395,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } } - JSON_PRIVATE_UNLESS_TESTED: ////////////////////// // member variables // @@ -23621,7 +23612,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); } - JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) static basic_json from_cbor(detail::span_input_adapter&& i, @@ -23745,7 +23735,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec return res ? result : basic_json(value_t::discarded); } - /// @brief create a JSON value from an input in BJData format /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/ template @@ -24026,7 +24015,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec }; // wrapper for "remove" operation; remove value at ptr - const auto operation_remove = [this, &result](json_pointer & ptr) + const auto operation_remove = [this, & result](json_pointer & ptr) { // get reference to parent of JSON pointer ptr const auto last_path = ptr.back(); @@ -24389,7 +24378,11 @@ inline namespace json_literals /// @brief user-defined string literal for JSON values /// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ JSON_HEDLEY_NON_NULL(1) -inline nlohmann::json operator "" _json(const char* s, std::size_t n) +#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + inline nlohmann::json operator ""_json(const char* s, std::size_t n) +#else + inline nlohmann::json operator "" _json(const char* s, std::size_t n) +#endif { return nlohmann::json::parse(s, s + n); } @@ -24397,7 +24390,11 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t n) /// @brief user-defined string literal for JSON pointer /// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ JSON_HEDLEY_NON_NULL(1) -inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + inline nlohmann::json::json_pointer operator ""_json_pointer(const char* s, std::size_t n) +#else + inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +#endif { return nlohmann::json::json_pointer(std::string(s, n)); } @@ -24450,7 +24447,7 @@ struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', /// @sa https://json.nlohmann.me/api/basic_json/std_swap/ NLOHMANN_BASIC_JSON_TPL_DECLARATION inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp) - is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression) + is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) is_nothrow_move_assignable::value) { j1.swap(j2); @@ -24461,8 +24458,13 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC } // namespace std #if JSON_USE_GLOBAL_UDLS - using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) - using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + using nlohmann::literals::json_literals::operator ""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator ""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + #else + using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + #endif #endif // #include @@ -24506,6 +24508,7 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #undef JSON_HAS_THREE_WAY_COMPARISON #undef JSON_HAS_RANGES + #undef JSON_HAS_STATIC_RTTI #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON #endif diff --git a/libs/nlohmann/json_fwd.hpp b/libs/nlohmann/json_fwd.hpp deleted file mode 100644 index 98070dfc..00000000 --- a/libs/nlohmann/json_fwd.hpp +++ /dev/null @@ -1,176 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ -#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ - -#include // int64_t, uint64_t -#include // map -#include // allocator -#include // string -#include // vector - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// This file contains all macro definitions affecting or depending on the ABI - -#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK - #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) - #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2 - #warning "Already included a different version of the library!" - #endif - #endif -#endif - -#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum) - -#ifndef JSON_DIAGNOSTICS - #define JSON_DIAGNOSTICS 0 -#endif - -#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON - #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 -#endif - -#if JSON_DIAGNOSTICS - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag -#else - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS -#endif - -#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON - #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp -#else - #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION - #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 -#endif - -// Construct the namespace ABI tags component -#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b -#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ - NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) - -#define NLOHMANN_JSON_ABI_TAGS \ - NLOHMANN_JSON_ABI_TAGS_CONCAT( \ - NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ - NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) - -// Construct the namespace version component -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ - _v ## major ## _ ## minor ## _ ## patch -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ - NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) - -#if NLOHMANN_JSON_NAMESPACE_NO_VERSION -#define NLOHMANN_JSON_NAMESPACE_VERSION -#else -#define NLOHMANN_JSON_NAMESPACE_VERSION \ - NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ - NLOHMANN_JSON_VERSION_MINOR, \ - NLOHMANN_JSON_VERSION_PATCH) -#endif - -// Combine namespace components -#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b -#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ - NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) - -#ifndef NLOHMANN_JSON_NAMESPACE -#define NLOHMANN_JSON_NAMESPACE \ - nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN -#define NLOHMANN_JSON_NAMESPACE_BEGIN \ - namespace nlohmann \ - { \ - inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) \ - { -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_END -#define NLOHMANN_JSON_NAMESPACE_END \ - } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ - } // namespace nlohmann -#endif - - -/*! -@brief namespace for Niels Lohmann -@see https://github.com/nlohmann -@since version 1.0.0 -*/ -NLOHMANN_JSON_NAMESPACE_BEGIN - -/*! -@brief default JSONSerializer template argument - -This serializer ignores the template arguments and uses ADL -([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) -for serialization. -*/ -template -struct adl_serializer; - -/// a class to store JSON values -/// @sa https://json.nlohmann.me/api/basic_json/ -template class ObjectType = - std::map, - template class ArrayType = std::vector, - class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator, - template class JSONSerializer = - adl_serializer, - class BinaryType = std::vector, // cppcheck-suppress syntaxError - class CustomBaseClass = void> -class basic_json; - -/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document -/// @sa https://json.nlohmann.me/api/json_pointer/ -template -class json_pointer; - -/*! -@brief default specialization -@sa https://json.nlohmann.me/api/json/ -*/ -using json = basic_json<>; - -/// @brief a minimal map-like container that preserves insertion order -/// @sa https://json.nlohmann.me/api/ordered_map/ -template -struct ordered_map; - -/// @brief specialization that maintains the insertion order of object keys -/// @sa https://json.nlohmann.me/api/ordered_json/ -using ordered_json = basic_json; - -NLOHMANN_JSON_NAMESPACE_END - -#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ diff --git a/libs/oatpp/parser/json/mapping/Deserializer.cpp b/libs/oatpp/parser/json/mapping/Deserializer.cpp index 180625c3..073d7b12 100644 --- a/libs/oatpp/parser/json/mapping/Deserializer.cpp +++ b/libs/oatpp/parser/json/mapping/Deserializer.cpp @@ -447,7 +447,13 @@ oatpp::Void Deserializer::deserializeObject(Deserializer* deserializer, parser:: skipValue(caret); polymorphs.emplace_back(field, label.toString()); // store polymorphs for later processing. } else { - field->set(static_cast(object.get()), deserializer->deserialize(caret, field->type)); + auto value = deserializer->deserialize(caret, field->type); + if(field->info.required && value == nullptr) { + throw std::runtime_error("[oatpp::parser::json::mapping::Deserializer::deserialize()]: " + "Error. " + std::string(type->nameQualifier) + "::" + + std::string(field->name) + " is required!"); + } + field->set(static_cast(object.get()), value); } } else if (deserializer->getConfig()->allowUnknownFields) { @@ -479,6 +485,11 @@ oatpp::Void Deserializer::deserializeObject(Deserializer* deserializer, parser:: parser::Caret polyCaret(p.second); auto selectedType = p.first->info.typeSelector->selectType(static_cast(object.get())); auto value = deserializer->deserialize(polyCaret, selectedType); + if(p.first->info.required && value == nullptr) { + throw std::runtime_error("[oatpp::parser::json::mapping::Deserializer::deserialize()]: " + "Error. " + std::string(type->nameQualifier) + "::" + + std::string(p.first->name) + " is required!"); + } oatpp::Any any(value); p.first->set(static_cast(object.get()), oatpp::Void(any.getPtr(), p.first->type)); } diff --git a/libs/oatpp/parser/json/mapping/Serializer.cpp b/libs/oatpp/parser/json/mapping/Serializer.cpp index c0df709c..69524c76 100644 --- a/libs/oatpp/parser/json/mapping/Serializer.cpp +++ b/libs/oatpp/parser/json/mapping/Serializer.cpp @@ -226,8 +226,9 @@ void Serializer::serializeObject(Serializer* serializer, stream->writeCharSimple('{'); bool first = true; + auto type = polymorph.getValueType(); auto dispatcher = static_cast( - polymorph.getValueType()->polymorphicDispatcher + type->polymorphicDispatcher ); auto fields = dispatcher->getProperties()->getList(); auto object = static_cast(polymorph.get()); @@ -243,6 +244,12 @@ void Serializer::serializeObject(Serializer* serializer, value = field->get(object); } + if(field->info.required && value == nullptr) { + throw std::runtime_error("[oatpp::parser::json::mapping::Serializer::serialize()]: " + "Error. " + std::string(type->nameQualifier) + "::" + + std::string(field->name) + " is required!"); + } + if (value || config->includeNullFields || (field->info.required && config->alwaysIncludeRequired)) { (first) ? first = false : stream->writeSimple(",", 1); serializeString(stream, field->name, static_cast(std::strlen(field->name)), serializer->m_config->escapeFlags);