diff --git a/3rdlib/DeviceManage/DeviceManage.pri b/3rdlib/DeviceManage/DeviceManage.pri new file mode 100644 index 0000000..3a2a784 --- /dev/null +++ b/3rdlib/DeviceManage/DeviceManage.pri @@ -0,0 +1,6 @@ +INCLUDEPATH += \ + $${PWD}/include + +win32 { + LIBS += -L$${PWD}/lib -lDeviceManage +} diff --git a/3rdlib/DeviceManage/bin/x64/DeviceManage.dll b/3rdlib/DeviceManage/bin/x64/DeviceManage.dll new file mode 100644 index 0000000..6ca2ae6 Binary files /dev/null and b/3rdlib/DeviceManage/bin/x64/DeviceManage.dll differ diff --git a/3rdlib/DeviceManage/bin/x64/FTD3XX.dll b/3rdlib/DeviceManage/bin/x64/FTD3XX.dll new file mode 100644 index 0000000..f3b21ce Binary files /dev/null and b/3rdlib/DeviceManage/bin/x64/FTD3XX.dll differ diff --git a/3rdlib/DeviceManage/bin/x64/ScriptProcess.dll b/3rdlib/DeviceManage/bin/x64/ScriptProcess.dll new file mode 100644 index 0000000..840e477 Binary files /dev/null and b/3rdlib/DeviceManage/bin/x64/ScriptProcess.dll differ diff --git a/3rdlib/DeviceManage/bin/x64/UAVTransport.dll b/3rdlib/DeviceManage/bin/x64/UAVTransport.dll new file mode 100644 index 0000000..1f6d1a3 Binary files /dev/null and b/3rdlib/DeviceManage/bin/x64/UAVTransport.dll differ diff --git a/3rdlib/DeviceManage/bin/x64/ftd2xx64.dll b/3rdlib/DeviceManage/bin/x64/ftd2xx64.dll new file mode 100644 index 0000000..4c8ab6c Binary files /dev/null and b/3rdlib/DeviceManage/bin/x64/ftd2xx64.dll differ diff --git a/3rdlib/DeviceManage/bin/x64/libgsl-dll.dll b/3rdlib/DeviceManage/bin/x64/libgsl-dll.dll new file mode 100644 index 0000000..59ce132 Binary files /dev/null and b/3rdlib/DeviceManage/bin/x64/libgsl-dll.dll differ diff --git a/3rdlib/DeviceManage/bin/x86/DeviceManage.dll b/3rdlib/DeviceManage/bin/x86/DeviceManage.dll new file mode 100644 index 0000000..65224d1 Binary files /dev/null and b/3rdlib/DeviceManage/bin/x86/DeviceManage.dll differ diff --git a/3rdlib/DeviceManage/bin/x86/FTD3XX.dll b/3rdlib/DeviceManage/bin/x86/FTD3XX.dll new file mode 100644 index 0000000..51c58a4 Binary files /dev/null and b/3rdlib/DeviceManage/bin/x86/FTD3XX.dll differ diff --git a/3rdlib/DeviceManage/bin/x86/ScriptProcess.dll b/3rdlib/DeviceManage/bin/x86/ScriptProcess.dll new file mode 100644 index 0000000..b464306 Binary files /dev/null and b/3rdlib/DeviceManage/bin/x86/ScriptProcess.dll differ diff --git a/3rdlib/DeviceManage/bin/x86/UAVTransport.dll b/3rdlib/DeviceManage/bin/x86/UAVTransport.dll new file mode 100644 index 0000000..0400cc3 Binary files /dev/null and b/3rdlib/DeviceManage/bin/x86/UAVTransport.dll differ diff --git a/3rdlib/DeviceManage/bin/x86/ftd2xx.dll b/3rdlib/DeviceManage/bin/x86/ftd2xx.dll new file mode 100644 index 0000000..ecafeb7 Binary files /dev/null and b/3rdlib/DeviceManage/bin/x86/ftd2xx.dll differ diff --git a/3rdlib/DeviceManage/bin/x86/libgsl-dll.dll b/3rdlib/DeviceManage/bin/x86/libgsl-dll.dll new file mode 100644 index 0000000..9ab9286 Binary files /dev/null and b/3rdlib/DeviceManage/bin/x86/libgsl-dll.dll differ diff --git a/3rdlib/DeviceManage/include/AnalyzeSpecturmExport.h b/3rdlib/DeviceManage/include/AnalyzeSpecturmExport.h new file mode 100644 index 0000000..a968ed9 --- /dev/null +++ b/3rdlib/DeviceManage/include/AnalyzeSpecturmExport.h @@ -0,0 +1,582 @@ +#ifndef _ANALYZESPECTURMEXPORT_H_ +#define _ANALYZESPECTURMEXPORT_H_ + +#ifdef ANALYZESPECTURM_EXPORTS +#define ANALYZESPECTURM_API(type) __declspec(dllexport) type __stdcall +#else +#define ANALYZESPECTURM_API(type) __declspec(dllimport) type __stdcall +#endif + +#include "NuclideLibraryDefine.h" +#include "PeakInfoDefine.h" + +#define MSS_SPECTURMDATA_MAXSIZE 65536 + +#pragma pack(push,1) + +//s - Peak fails shape tests. +//D - Peak area deconvoluted. +//M - Peak is close to a library peak. + + +enum ePeakUseFlag +{ + ePUF_PEAK_FAILED_SHAPE_TEST, + ePUF_PEAK_DECONVOLUTED, + ePUF_PEAK_FIT_NUCLIDE, + ePUF_PEAK_SMALL_POS, + + + eNF_ACTIVITY_AVG_CALC = 16, + eNF_PEAK_TOO_WIDE, + eNF_PEAK_MULT_AREA_NEG, + eNF_PEAK_TOO_NARROW, + eNF_PEAK_FWH25M_WIDE, + eNF_PEAK_FAILED_SENS_TEST, + eNF_PEAK_IDTY_FIRST_PEAK_FIALED, + eNF_LARGE_INCALC_ACTIVITY, + eNF_SMALL_INCALC_ACTIVITY, + eNF_PEAK_OUTSIDE_ANS_ENERGY_RANGE, + eNF_PEAK_CENTROID_IS_NOT_CLOSE_TO_LIB, + eNF_PEAK_BACKGROUND_COR, +}; + + +#define IS_POS_CHECKED(flag,pos) ( (flag & (1 << pos)) == (1 << pos) ) + +#define SET_SHAPE_CHECK_FAILED(flag) (flag |= (1 << ePUF_PEAK_FAILED_SHAPE_TEST)) +#define SET_SHAPE_CHECK_SUCCESS(flag) (flag &= ~(1 << ePUF_PEAK_FAILED_SHAPE_TEST)) + +#define SET_PEAK_DECONVOLUTED(flag) (flag |= (1 << ePUF_PEAK_DECONVOLUTED)) +#define RESET_PEAK_DECONVOLUTED(flag) (flag &=~(1 << ePUF_PEAK_DECONVOLUTED)) + +#define SET_PEAK_FIT_NUCLIDE(flag) (flag |= (1 << ePUF_PEAK_FIT_NUCLIDE)) +#define RESET_PEAK_FIT_NUCLIDE(flag) (flag &=~(1 << ePUF_PEAK_FIT_NUCLIDE)) +#define IS_PEAK_FIT_NUCLIDE(flag) IS_POS_CHECKED( flag,ePUF_PEAK_FIT_NUCLIDE) + +#define SET_PEAK_SMALL(flag) (flag |= (1 << ePUF_PEAK_SMALL_POS)) +#define RESET_PEAK_SMALL(flag) (flag &=~(1 << ePUF_PEAK_SMALL_POS)) +#define IS_PEAK_SMALL(flag) IS_POS_CHECKED( flag,ePUF_PEAK_SMALL_POS) + + +#define IS_NI_FLAG_CHECKED(flag,v) ( (flag &( 1 << v)) == (1 << v)) +#define SET_NI_FLAG(flag,v) (flag |= (1 << v)) +#define RESET_NI_FLAG(flag,v) (flag &= ~(1 << v)) + +#define IS_NI_KEY_LINE(flag) IS_NI_FLAG_CHECKED(flag,eNI_K) +#define IS_NI_NOT_IN_AVG(flag) IS_NI_FLAG_CHECKED(flag,eNI_A) + +#define SET_ACTIVITY_AVG_CALC(flag) SET_NI_FLAG(flag,eNF_ACTIVITY_AVG_CALC) +#define IS_ACTIVITY_AVG_CALC_SET(flag) IS_NI_FLAG_CHECKED(flag,eNF_ACTIVITY_AVG_CALC) + +#define SET_LARGE_INCALC_ACTIVITY(flag) SET_NI_FLAG(flag,eNF_LARGE_INCALC_ACTIVITY) + +#define SET_SMALL_INCALC_ACTIVITY(flag) SET_NI_FLAG(flag,eNF_SMALL_INCALC_ACTIVITY) + +#define SET_PEAK_FAILED_SENS_TEST(flag) SET_NI_FLAG(flag,eNF_PEAK_FAILED_SENS_TEST) + + +enum eLinearFitType +{ + eLFTNone, + // 线性插值 + eInterpolative, + // 一次函数,2个未知数 + eLine2, + // 二次函数,3个未知数 + eLine3, + // 多次函数 + eEFTPloy, + // 一次函数,2个未知数,有权重拟合 + eLine2WithW, + // 二次函数,3个未知数,有权重拟合 + eLine3WithW, + eLine4,// 三次函数拟合 +}; + +typedef struct sLinearFitResult +{ + eLinearFitType type; + double a; + double b; + double c; + double d; + double e; + double f; + double sa; + double sb; + double sc; + double sd; + double se; + double sf; + double chisq; +}sLinearFitResult; + +class NuclideLibrary; +struct sDataInfo +{ + int *pOrgData; + int Orglen; + + double dRealTime; + double dLiveTime; + + int iPeakBeginCh; // 峰开始道址 + + NuclideLibrary *pNuclideLibaray; +}; + +typedef struct sSmoothConfig +{ + int bUseGaussSmoothNum; + int bUseGaussSmoothTimes; +}sSmoothConfig; + +typedef struct sPeakSearchConfig +{ + double dSerachSensity; + int iChNumOnEachSide; + + double dSfwhmRadioFwhmMaxValue; + double dSheightRadioHeightValue; +}sPeakSearchConfig; + +typedef struct sResolvePeakConfig +{ + double dnofwhmofpeak; + double dnofwhmofSideBaseLine; +}sResolvePeakConfig; + +typedef struct sMatchPeaksConfig +{ + double toler; + double dprob; +}sMatchPeaksConfig; + +typedef struct sPeakInfoConfig +{ + BOOL isroi; + int leftch; + int righch; + int n; + int cursorpeak; +}sPeakInfoConfig; + +typedef struct sCursorOnePeakInfo +{ + double dChannel; // 峰所在道址 + double dEnergy; // 峰所在的能量 + int iNIChannel; + double dNIEnergy; + int iLeftChannel; + int iRightChannel; + char cNName[32]; + double fwhm; + double fwh5m; + double dGrossArea; + double dGrossAreaCountRate; + double dNetArea; + double dNetAreaCountRate; + double dNetAreaUncertain; + double dcA; // counting activity, + +}sCursorOnePeakInfo; + +typedef struct sPeakInfoResult +{ + double* pSmoothData; + int iSmoothDataLen; + + double* pSecDiffData; + int iSecDiffDataLen; + + sCursorOnePeakInfo peakinfo; +}sPeakInfoResult; + +typedef struct sConfigInfo +{ + sSmoothConfig smoothconfig; + sPeakSearchConfig peaksearchconfig; + sResolvePeakConfig resolvepeakconfig; + sLinearFitResult energycal; // 能量刻度 + sMatchPeaksConfig matchpeakconfig; + sPeakInfoConfig peakInfoConfig; +}sConfigInfo; + + +typedef struct OneValue +{ + double v; + double sv; +}OneValue; + +typedef struct sOneBaseLineInfo +{ + eLinearFitType type; + int leftch; + int rightch; + OneValue a; + OneValue b; + OneValue c; + OneValue d; + double chisq; + double leftsmoothpoint; + double rightsmoothpoint; + double leftvalue; + double rightvalue; + double peakpos; + + double grossarea; + double bdarea; + OneValue netarea; + + double directfitnetarea; +}sOneBaseLineInfo; + +typedef struct sOnePeakInfo +{ + OneValue height; + OneValue centor; + OneValue width; + // 半高宽,道址 + OneValue fwhm; + ////y = a*exp(-b*(x-c)^2)); fwhmwidth = b + double fwhmwidth; + // 半高宽,道址 + OneValue fittedfwhm; + // //y = a*exp(-b*(x-c)^2)); fittedwidth = b + OneValue fittedwidth; + OneValue area; + OneValue bdarea; + + double leftch; + double rightch; + + double energy; + + int flag; + + double leftsmoothpoint; + double rightsmoothpoint; + double leftvalue; + double rightvalue; + + // 活度 校正到开始测量时的活度 + OneValue activity; + // MDA + OneValue mda; + sOneNuclideInfo oni; + sOneEnergyLine onl; + sOneBaseLineInfo obli; +}sOnePeakInfo; + +typedef struct sMulPeakInfo +{ + int MulPeakNum; + sOnePeakInfo peakinfo; +}sMulPeakInfo; + + +typedef struct sBaseLineInfo +{ + sOneBaseLineInfo leftbaseline; + sOneBaseLineInfo rightbaseline; + sOneBaseLineInfo peakbaseline; +}sBaseLineInfo; + +class _sOneNuclideActivity; +typedef struct sResultInfo +{ + char measureDate[32]; + double dRealTime; + double dLiveTime; + int sprtLen; + double* pSmoothData; + int iSmoothDataLen; + + double* pSecDiffData; + int iSecDiffDataLen; + + double* pCandidatePeaks; + int iCandidatePeakNum; + + int iFinalPeakNum; // 两个作用 inout 传入内存大小,字节单位,传出峰数量 + int iNotEnoughMemery; + sOnePeakInfo* pSearchPeakResult; + + sLinearFitResult linFitResult; + + int iBaseLineNum; // 两个作用 inout 传入内存大小,字节单位,传出数量 + int iNotEnoughMemeryofBaseLine; + sBaseLineInfo *pBaselineInfo; + + int iMulPeakNum; // 两个作用 inout 传入内存大小,字节单位,传出重叠峰和单峰的数量 + int iNotEnoughMemeryofMulPeak; + sMulPeakInfo *pMulPeakInfo; + + int iNucResultNum; // 两个作用 inout 传入内存大小,字节单位,传出核素的数量 + int iNotEnoughMemeryofNucResult; + _sOneNuclideActivity *pNucResultInfo; +}sResultInfo; + + +struct sFitData +{ + eLinearFitType type; + int size; + double *px; + double *py; + double *pw; +}; + +typedef struct ASDataInfo +{ + int* pHandle; // 设备句柄 + int bdidx; + int chidx; + double beginchannel; + double endchannel; + eDisplaSprtInfoType calcType; + double energy; + eCalcSprtSubInfoType sprtSubType; + unsigned char reserve[20]; +}ASDataInfo; + +enum eSearchPeakSensityLevel +{ + eSPS_1, + eSPS_2, + eSPS_3, + eSPS_4, + eSPS_5 +}; + +typedef struct OneDefaultConfig +{ + BOOL enalbe; + BOOL useInternal; + char path[MAX_PATH]; +}OneDefaultConfig; + +enum eBackgroundType +{ + eBTAuto, + eBTOne, + eBTThree, + eBTFive, +}; + +typedef struct AnalysisSampleConfig +{ + // 描述 + char Description[128]; + // 分析范围 + double beginchannel; + double endchannel; + // 随机相加 + double RandomSum; + // 本底类型 + eBackgroundType BGType; + // 核素库 + OneDefaultConfig NuclideLib; + // 校正文件,能量刻度,效率刻度 + OneDefaultConfig Clb; +}AnalysisSampleConfig; + +enum eMDAType +{ + eMDATP_1 +}; + +enum eASUnit +{ + eASUBq, + eASUuCi, +}; + +typedef struct AnalysisSystemConfig +{ + // 实验室名 + char LaboratoryName[128]; + // 操作员名 + char OperatorName[128]; + // MDA类型 + eMDAType mdaType; + // 寻峰参数 + eSearchPeakSensityLevel eSPSLevel; + // 匹配配置 + double dlibMatchWidth; + // 分支比限制 100 + double dlibFractionLimit; + // 可疑核素库 + OneDefaultConfig libSuspect; + // 单位 + eASUnit asuUit; + double asuMultiplier; + double asuDivisor; + char asuActivity[32]; + double asuSize; + char asuSizeUnit[32]; + double asuSizeUncertain; +}AnalysisSystemConfig; + +typedef struct AnalysisDecayConfig +{ + // 衰减校正 + BOOL bDecayCorrection; + // 衰减校正 yyyy-MM-dd HH:mm:ss + char bDCDateTime[32]; + // 谱线采集校正 + BOOL bAcquisitionCorr; + + // 样品采集时间校正 + BOOL bSampleCorrect; + // 样品采集时间校正 yyyy-MM-dd HH:mm:ss + char bSCStartDateTime[32]; + // 样品采集时间校正yyyy-MM-dd HH:mm:ss + char bSCStopDateTime[32]; +}AnalysisDecayConfig; + +enum eUncertainReportUnitType +{ + eUPUTPercent, + eUPUTActivity +}; + +enum eUncertainReportValueType +{ + eUPVTCouting, + eUPTVTotal +}; + +enum eUncertainLevel +{ + eULOne, + eULTwo, + eULThree, +}; + +enum eOutputType +{ + eOTPrinter, + eFile, + eProgram, +}; + +typedef struct AnalysisReportConfig +{ + // 报告峰信息 + int dReportOption; + // 不确定度报告单位 + eUncertainReportUnitType URUTType; + // 不确定度报告值类型 + eUncertainReportUnitType URVTType; + // 不确定度水平 + eUncertainLevel UncertainLevel; + // 输出类型 + eOutputType OTType; + // 显示解谱结果 + BOOL bDisplayAnalysisResult; + // 输出文件配置 + char FileProgramInfo[260]; + // 输出程序配置 + char ProgramInfo[260]; + // 报告器配置 + char ReporterInfo[260]; +}AnalysisReportConfig; + +typedef struct AnalysisMethodConfig +{ + // 额外误差 % + double dSystematic; + // 随机误差 + double dRandom; +}AnalysisMethodConfig; + +typedef struct AnalysisCorrectionConfig +{ + // 峰本底校正 + OneDefaultConfig PBC; + // 几何校正 + OneDefaultConfig GeometryC; + // 衰减校正 + OneDefaultConfig AttenuationC; +}AnalysisCorrectionConfig; + +typedef struct AnalysisDataDealConfig +{ + int bUseGaussSmoothNum; + int bUseGaussSmoothTimes; + // 峰形测试 + double dSfwhmRadioFwhmMaxValue; + double dSheightRadioHeightValue; + // 解谱 + double dnofwhmofpeak; + double dnofwhmofSideBaseLine; + + unsigned char reserve[128]; +}AnalysisDataDealConfig; + +typedef struct AnalysisSpecturmConfig +{ + AnalysisSampleConfig ascSample; + AnalysisSystemConfig ascSystem; + AnalysisDecayConfig ascDecay; + AnalysisReportConfig ascReport; + AnalysisMethodConfig ascMethod; + AnalysisCorrectionConfig ascCorrection; + AnalysisDataDealConfig ascDataDeal; + + unsigned char reserve[128]; +}AnalysisSpecturmConfig; + +class _sOneNuclideActivity +{ +public: + sOneNuclideInfo info; + OneValue activity; + OneValue totalActivityUncertain; + OneValue mda; +}; + +typedef struct AnalyzeSpecturmConfigInfo +{ + int head; + int version; + char CreateTime[32]; + char LastModifyTime[32]; + char Desc[128]; + unsigned char reserve[256]; +}AnalyzeSpecturmConfigInfo; + +#pragma pack(pop) + + +#ifdef __cplusplus +extern "C" { +#endif + ANALYZESPECTURM_API(int) NuclideIndentifaction(int*pInst, sDataInfo* pSpecturmData, sConfigInfo *pConfig, sResultInfo *pResultInfo, char *ErrorInfo); + + ANALYZESPECTURM_API(int) NuclideIndentifactionV2(int*pInst, ASDataInfo* pASDI, sConfigInfo *pConfig, sResultInfo *pResultInfo, char *ErrorInfo); + + ANALYZESPECTURM_API(int) NuclideIndentifactionV3(int*pInst, ASDataInfo* pASDI, sResultInfo *pResultInfo, char *ErrorInfo); + + ANALYZESPECTURM_API(int) FitLinear(int*pInst, sFitData* pSpecturmData, sLinearFitResult *pResultInfo, char *ErrorInfo); + + ANALYZESPECTURM_API(int*) GetInst(char *ErrorInfo); + ANALYZESPECTURM_API(int) FreeInst(int*pInst); + + ANALYZESPECTURM_API(int) GetAnalyzeSpecturmConfig(AnalyzeSpecturmConfigInfo *pInfo, AnalysisSpecturmConfig *pconfig, char *ErrMsg); + + ANALYZESPECTURM_API(int) SetAnalyzeSpecturmConfig(AnalyzeSpecturmConfigInfo *pInfo, AnalysisSpecturmConfig *pconfig, char *ErrMsg); + + ANALYZESPECTURM_API(int) SaveAnalyzeSpecturmConfig(const char *path, char *ErrMsg); + + ANALYZESPECTURM_API(int) ReadAnalyzeSpecturmConfig(const char *path, char *ErrMsg); + + ANALYZESPECTURM_API(int) GetCusorNuclideInfo(int*pInst, ASDataInfo* pASDI, sResultInfo *pResultInfo, char *ErrorInfo); +#ifdef __cplusplus +} +#endif + +#endif // _ANALYZESPECTURMEXPORT_H_ \ No newline at end of file diff --git a/3rdlib/DeviceManage/include/DeviceManage.h b/3rdlib/DeviceManage/include/DeviceManage.h new file mode 100644 index 0000000..7136db5 --- /dev/null +++ b/3rdlib/DeviceManage/include/DeviceManage.h @@ -0,0 +1,371 @@ +#pragma once +#include "UAVrtp.h" +// #ifdef DEVICEMANAGE_EXPORTS +// #include "UserConfigInfo.h" +// #include "OneChannelConfigInfo.h" +// #include "OneDeviceComptonSuppressSetting.h" +// #endif + +#ifdef DEVICEMANAGE_EXPORTS +#define DEVICEMANAGE_API(type) extern "C" _declspec(dllexport) type _stdcall +#else +#define DEVICEMANAGE_API(type) extern "C" _declspec(dllexport) type _stdcall +#endif +// #include "OneComptonResultDataExport.h" + +#ifdef __cplusplus +extern "C" { +#endif + + enum eMCSystemType + { + eReserve, + eHPGEDEFAULE = 1, + eCombineDevice = 2, + eProxyDevice = 3, + eRoundMC = 10, + eRoundMCV1 = 11, // 绗簩閫氶亾璁℃暟 + eRoundMCHightThroughtput = 12, // 鍦嗙瓛澶氶亾楂樿鏁扮巼 + eFP5XMC = 13, + eDMCA2023 = 14, + eHPGEMC = 30, + eMulParamMC = 50, + eDP3402PLUSMC = 51, + eMulParam863MC = 60, + eNeutronCount = 70, + eAnalogMC = 80, + eAnalogDigtalMC = 81, + eHightThroughtMC=90, + eHightThroughtMCCh2 = 91, + eGeneralMC = 92, + eAlphaMC = 93, + eHVDevice = 100, +}; + +// 娣诲姞鏂扮殑閲囨牱鐜囧悗锛岄渶瑕佹敞鎰忓湪DeviceInfo.cpp涓坊鍔犲搴旂殑绱㈠紩 +// 娣诲姞鏂扮殑閲囨牱鐜囧悗锛岄渶瑕佹敞鎰忓湪DeviceInfo.h 涓慨鏀筍ampleRateInMbpsToSampleRate淇敼 +enum eSampleRate +{ + eNone, + e80M, + e250M, + e500M, + e50M, + e200M, + e1G, + e125M, +}; + +#pragma pack(push,1) + +struct sDeviceInfo +{ + char GUID[64]; + char SystemVersion[16]; + char CommVersion[16]; + eMCSystemType type; + char MacAddr[32]; + char FilePath[MAX_PATH]; + unsigned char reserve[60]; +}; + +class ChannelOpInfo +{ +public: + BOOL canop; + double adcperiod; + eSampleRate samplerate; + double lmperiod; + int adcmaxvalue; + double adcvoltageinmv; +}; +typedef struct sDeviceDataPtr +{ + int totalNum; + void *pData[64]; +}sDeviceDataPtr; + +class DeviceData +{ +public: + int len = 1024*1024; + BYTE pdata[1024*1024]; +}; +enum eDeviceManageOperate +{ + eAddDevice, + eRemoveDevice, +}; + +enum eDeviceType +{ + // 纭欢璁惧 + eHardwareDevice, + // 鍥炴斁璁惧 + eReplayDevice, + // 鏂囦欢璁惧 + eFileDevice, + // 纭欢缁勫悎璁惧 + eHardwareCombineDevice, + // 鍥炴斁缁勫悎璁惧 + eReplayCombineDevice, + // 浠g悊璁惧 + eDTProxyDevice, +}; + +enum eSprtOptType +{ + eAdd, + eDec, +}; + +enum eDisplaSprtInfoType; + + +typedef struct sOneSprtOptInfo +{ + void *pdev; + int bdidx; + int chidx; + eSprtOptType opttype; + eDisplaSprtInfoType sprttype; +}sOneSprtOptInfo; + +class OneNeutronData +{ +public: + int chnum; + __int64 measuretime; + __int64 lowercount; + __int64 betweencount; + __int64 ttlcount; + unsigned char reserve[32]; +}; + +#ifndef DEVICEMANAGE_EXPORTS +#define ALIAS_NALE_LEN 64 + +enum eTransferMode +{ + eSpecturmMode, + eFormWaveMode, + eADCMode, + eListMode, + eABMode, +}; + +enum eAutoSteadyStatus +{ + a, // 鏈紑鍚, + b, // 绋宠氨涓, + c, // 绋宠氨瀹屾垚, + d, // 涓嶆敮鎸, + e, // 鏍″噯涓, + f, // 绋宠氨澶辫触, + g, // 鏍″噯澶辫触, + h, // 绛夊緟涓, +}; + +typedef struct sAutoSteadyParam +{ + BOOL bEnable; + eAutoSteadyStatus status; + float offset; // 10 + float a; + float b; + float c; + unsigned short maxchanneldivfactor; + unsigned short peakchannel; // 26 + unsigned short steadyPeriodInSec; // 28 + unsigned short peakhalfwidth; // 30 + double dc; +}sAutoSteadyParam; + +class UserConfigInfo +{ +public: + char m_cAliasName[ALIAS_NALE_LEN]; + char _strMCName[32]; + int _iBDNum; + int _iChNum; + BOOL _bDisplayOrgCptSprt; + BOOL _bDisplayComptonCptSprt; + BOOL _bDisplayAntiComptonCptSprt; + BOOL _bDisplayLMSprt; + BOOL _bDisplayRiseTimeSprt; + BOOL _bDisplayFallTimeSprt; + __int64 _i64AdjustTickCount; + __int64 _i64MinAllowTickCount; + __int64 _i64MaxAllowTickCount; + BOOL _bDisplayConfig; + BOOL _bSaveLMData; + BOOL _bWork; + sAutoSteadyParam asp; + char m_cReserve[16]; +}; + +class OneChannelConfigInfo +{ +public: + void *__vfp; + /// + /// 鏁版嵁浼犺緭妯″紡 + /// + eTransferMode m_eTransferModel = eSpecturmMode; + /// + /// 纭欢澧炵泭 + /// + int m_iDeviceGain = 1; + /// + /// 纭欢澧炵泭 + /// + int m_iDeviceGainSelectIndex = 1; + /// + /// 杞欢澧炵泭 + /// + int m_iSoftGain = 3000; + /// + /// 鏃堕棿甯告暟 + /// + double m_dConstTime = 45; + /// + /// 鎴愬舰鏃堕棿 + /// + int m_iFormTime = 3; + /// + /// 蹇氶亾瑙﹀彂闃堝 + /// + double m_iFastChannelTrigerValue = 10; + /// + /// 澶氶亾鍒嗚鲸鐜 + /// + int m_iChannelNum = 1024; + + int m_iHighVoltage = 0; + + // sParamConfig ParamConfig; + /// + /// 杈撳叆12V淇″彿琛板噺鎺у埗锛1涓狝SCii瀛楃琛ㄧず锛夈傗0鈥欒〃绀哄叧闂紝鈥1鈥欒〃绀哄惎鐢ㄨ“鍑忥紝鍋忕Щ閲2 + /// + int m_iInputVoltageDesc = 0; + /// + /// CR寰垎妯″紡鍚敤锛1涓狝SCii瀛楃琛ㄧず锛夈傗0鈥欒〃绀哄叧闂紝鈥1鈥欒〃绀哄惎鐢–R寰垎锛屽亸绉婚噺3銆傚浜庡浣嶅瀷鍓嶆斁鍏惰緭鍑虹殑涓洪樁姊俊鍙凤紝姝ゆ椂搴斿惎鐢–R寰垎锛屽惁鍒欏彲涓嶅惎鐢 + /// + int m_iCRDivMode = 0; + /// + /// 杈撳叆淇″彿姝h礋鏋佹ч夋嫨锛1涓狝SCii瀛楃琛ㄧず锛夈傗0鈥欒〃绀鸿礋淇″彿锛屸1鈥欒〃绀烘淇″彿锛屽亸绉婚噺4 + /// + int m_iInputSignalPostive = 0; + /// + /// 绗竴浣嶄负绗﹀彿浣嶏紝鍚庡洓浣嶄负鏁板硷紝浠V涓哄崟浣,璀鈥+0200鈥欒〃绀虹洿娴佸亸绉200mV, 鍦板潃鍋忕Щ閲5-9 + /// + int m_iCurrentOffset = 0; + /// + /// 鏈澶ц兘閲忚寖鍥达紝浠eV涓哄崟浣嶏紙5涓狝SCII瀛楃锛 璀鈥04000鈥欙紝琛ㄧず鏈澶ц兘閲忚寖鍥翠负4MeV銆傝缃负鍏ㄩ浂鈥00000鈥欏垯琛ㄧず鍏抽棴鑷姩绋宠氨鍔熻兘銆 + /// + int m_iMaxEnergy = 0; + /// + /// Am宄伴潰绉宄版硶鐨勯潰绉瘮渚 渚嬪锛氣15鈥欒〃绀烘埅鍙栧嘲椤堕珮搴15%鐨勯潰绉敤浜庨潰绉硶瀵诲嘲 + /// + int m_iAMPeakDiv = 0; + /// + /// K-40涓嶢m-241鐨勫嘲浣嶆牎姝g郴鏁 渚嬪锛氣24513鈥欒〃绀篕-40涓嶢m-241鐨勫嘲浣嶆崲绠楃郴鏁颁负24.513 + /// + short m_iHVDelt = 0; + short m_iHVCtrl = 0; + + int m_iGetSpecturmPeirod = 1; + + __int64 m_iTotalMeasureTime = 10; + int m_iTotalMeasureCount = 1; + + int m_iTrapeTopShitBit = 0; + + int m_iRiseTime = 2; + int m_iTopTime = 2; + + BOOL m_bICRCorrect = FALSE; + + int m_iCRZAValue = 0; + + int m_iZAEnable = 30; + + BYTE reserve[128]; + + UserConfigInfo m_pUserConfigInfo; +}; +#endif + +typedef void(__stdcall *ONFINDDEVICEFINISHED)(int ret, sDeviceList* pDeviceList); + +typedef void(__stdcall *ONUPDATEONEDEVICEBOARDDATA)(void* pHandle, sDeviceDataPtr *pdata); + +typedef void(__stdcall *ONUPDATEONEDEVICECOMPTOMSUPRESSDATA)(void* pHandle,void *pComptonSet, sDeviceDataPtr *pdata); + +typedef void(__stdcall *ONDEVICEMANAGECHANGED)(eDeviceManageOperate op, int ret, sDeviceList *pDeviceList); + +typedef void(__stdcall *ONUPDATE_NEUTRONDATA)(void* pHandle, int ret, OneNeutronData* pData, int iLen); + +typedef void(__stdcall *ONUPDATE_DEVICE_CONFIG)(void* pHandle, int bdnum,int chnum, void* pData, int iLen,void *pData2, int iLen2); + +typedef void(__stdcall *ONUPDATE_PRESENT_CONFIG)(void* pHandle, int bdnum, int chnum); + +// 鏇存柊璁惧閰嶇疆缁撴瀯淇℃伅 +typedef void(__stdcall *ONUPDATE_DEVICE_STRUCT)(void* pHandle,void *pParent,int maxParentNum,void *pChild,int MaxChildNum, void *reserve); + + +DEVICEMANAGE_API(int) FindDeviceAsync(sFindDeviceConfig *fdc, ONFINDDEVICEFINISHED OnFindFinished); +DEVICEMANAGE_API(int) GetDeviceInfo(void *pHandle, sDeviceInfo *spDi, int budnum, int chnum); +DEVICEMANAGE_API(int) GetDeviceBoardTotalNum(void *pHandle); +DEVICEMANAGE_API(int) GetDeviceChTotalNum(void *pHandle, int bdidx); + +// DEVICEMANAGE_API(int) GetDeviceUserInfo(void *pHandle, UserConfigInfo *pUCI); +// DEVICEMANAGE_API(int) SetDeviceUserInfo(void *pHandle, UserConfigInfo *pUCI); +// DEVICEMANAGE_API(int) GetDeviceBoardUserInfo(void *pHandle, UserConfigInfo *pUCI, int bdidx); +// DEVICEMANAGE_API(int) GetDeviceBoardChannelUserInfo(void *pHandle, UserConfigInfo *pUCI, int bdidx, int chidx); +// DEVICEMANAGE_API(int) SetDeviceBoardChannelUserInfo(void *pHandle, UserConfigInfo *pUCI, int bdidx, int chidx); +DEVICEMANAGE_API(int) GetDeviceBoardChannelConfiginfo(void *pHandle, OneChannelConfigInfo *pUCI, int bdidx, int chidx); + +DEVICEMANAGE_API(int) DBCSetHightVoltage(void *pHandle, OneChannelConfigInfo *pUCI, int bdidx, int chidx); +DEVICEMANAGE_API(int) DBCSoftParamConfig(void *pHandle, OneChannelConfigInfo *pUCI, int bdidx, int chidx); +DEVICEMANAGE_API(int) DBCHardDeviceParamConfig(void *pHandle, OneChannelConfigInfo *pUCI, int bdidx, int chidx); + +DEVICEMANAGE_API(int) DBCStartMeasure(void *pHandle, int bdidx, int chidx); +DEVICEMANAGE_API(int) DBCStopMeasure(void *pHandle, int bdidx, int chidx); +DEVICEMANAGE_API(int) DBCGetData(void *pHandle, int totalnum, DeviceData *pDevData, int bdidx, int chidx); +DEVICEMANAGE_API(int) DBCCombineGetData(void *pHandle, int totalnum, DeviceData *pDevData, int bdidx, int chidx); +DEVICEMANAGE_API(int) DBCSetUpdateDeviceBoardDataCallBack(void *pHandle, ONUPDATEONEDEVICEBOARDDATA d); +DEVICEMANAGE_API(int) DBCSetUpdateDeviceBoardLMPointDataCallBack(void *pHandle, ONUPDATEONEDEVICEBOARDDATA d); +DEVICEMANAGE_API(int) DBCClearData(void *pHandle, int bdidx, int chidx); +DEVICEMANAGE_API(int) DBCSaveConfig(void *pHandle); + + +// DEVICEMANAGE_API(int) DBCSetUpdateDeviceComptonSupressResultCallBack(void *pHandle, ONUPDATEONEDEVICECOMPTOMSUPRESSDATA d); + +// DEVICEMANAGE_API(int) DBCGetDeviceAntiComptonSetting(void *pHandle, OneDeviceComptonSuppressSetting *pDevSet); +// DEVICEMANAGE_API(int) DBCGetDeviceOneMainAntiComptonSetting(void *pHandle, OneComptonSuppressSetting *pOneSet, int mainidx); +// DEVICEMANAGE_API(int) DBCGetDeviceOneSubAntiComptonSetting(void *pHandle, OneComptonSuppressSetting *pOneSet, int mainidx, int subidx); +// DEVICEMANAGE_API(int) DBCSetDeviceOneMainAntiComptonSetting(void *pHandle, OneComptonSuppressSetting *pOneSet, int mainidx); +// DEVICEMANAGE_API(int) DBCSetDeviceOneSubAntiComptonSetting(void *pHandle, OneComptonSuppressSetting *pOneSet, int mainidx, int subidx); + +DEVICEMANAGE_API(void) DM_Con_Flushout(); +DEVICEMANAGE_API(void) DM_Con_KeyPressEvent(char c, int codetype); +DEVICEMANAGE_API(void) DM_Con_Init(char *pStdOutBuff, int pStdOutBuffLen, char *pStdErrBuff, int pStdErrBuffLen); +DEVICEMANAGE_API(void) DM_Con_Exit(); +DEVICEMANAGE_API(void) DM_Con_Run(); +DEVICEMANAGE_API(void) DM_Con_Free(void *buf); +DEVICEMANAGE_API(int) CloseDevice(void *pHandle); + +// 鑾峰彇鏉垮崱閫氶亾瀵瑰簲鐨勮澶 +DEVICEMANAGE_API(void*) DMGetOperateDevice(void *pHandle, int bdidx, int chidx); +// 璁剧疆鏌ヨ淇濆瓨lmaa鏍煎紡 +// set 鍙傛暟涓篎ALSE琛ㄧず璇诲彇褰撳墠淇℃伅锛屽鏋滀负TRUE琛ㄧず璁剧疆淇℃伅 +// DEVICEMANAGE_API(int) DBCSaveAntiLMDataToFile(void *pHandle, int bdidx, int chidx, BOOL set, BOOL *bEnable, char *filename); + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif + + diff --git a/3rdlib/DeviceManage/include/DeviceManageDefine.h b/3rdlib/DeviceManage/include/DeviceManageDefine.h new file mode 100644 index 0000000..ea18715 --- /dev/null +++ b/3rdlib/DeviceManage/include/DeviceManageDefine.h @@ -0,0 +1,34 @@ +#pragma once +#ifdef DEVICEMANAGE_EXPORTS +#define DEVICEMANAGE_API_DM_C(type) extern "C" _declspec(dllexport) type _stdcall +#else +#define DEVICEMANAGE_API_DM_C(type) extern "C" _declspec(dllexport) type _stdcall +#endif + +#include "OneChannelDataDefine.h" + + +#ifdef __cplusplus +extern "C" { +#endif + DEVICEMANAGE_API_DM_C(void*) DMCreateOneFileDevice(); + DEVICEMANAGE_API_DM_C(int) DBCSetOneChannelSprtData(sCursorInfo *pci, OneSpecturmDataInfo *osdi); + + // 保存读取文件 + DEVICEMANAGE_API_DM_C(int) DBCSaveDeviceDataToDdf(int *pHandle, const char *path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCSaveDeviceDataToGID(int *pHandle, const char *path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCSaveDeviceDataToSpe(int *pHandle, const char *path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCSaveDeviceDataToChn(int* pHandle, const char* path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCSaveDeviceDataToCnf(int* pHandle, const char* path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCSaveDeviceDataToSpc(int* pHandle, const char* path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCSaveDeviceDataToPHD(int *pHandle, const char *path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCReadDeviceDataFromDdf(const char *path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCReadDeviceDataFromChn(const char* path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCReadDeviceDataFromCnf(const char* path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCReadDeviceDataFromSpc(const char* path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCReadDeviceDataFromSpe(const char *path, char* szErrMsg); + DEVICEMANAGE_API_DM_C(int) DBCReadDeviceDataFromPHD(const char *path, char* szErrMsg); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/3rdlib/DeviceManage/include/NuclideLibraryDefine.h b/3rdlib/DeviceManage/include/NuclideLibraryDefine.h new file mode 100644 index 0000000..157d6dd --- /dev/null +++ b/3rdlib/DeviceManage/include/NuclideLibraryDefine.h @@ -0,0 +1,116 @@ +#pragma once +#pragma pack(push,1) + +#ifdef DEVICEMANAGE_EXPORTS +#define DEVICEMANAGE_API_NUCLIB_C(type) extern "C" _declspec(dllexport) type _stdcall +#else +#define DEVICEMANAGE_API_NUCLIB_C(type) extern "C" _declspec(dllexport) type _stdcall +#endif + +#define NI_NAME_LEN 32 + +enum eEnergyType +{ + GammaRay, + XRay, + PositronDecay, + SingleEscape, + DoubleEscape +}; + +enum eHalfLifeUnit +{ + Year, + Days, + Hours, + Min, +}; + +enum eEnergyFlay +{ + // KeyLine + eNI_K, + // Not In Average + eNI_A, +}; + + +typedef struct sOneEnergyLine +{ + int rank; + double energy; + double percent; + eEnergyType energytype; + int flag; +}sOneEnergyLine; + +enum eNuclideFlag +{ + eNF_T, + eNF_F, + eNF_I, + eNF_N, + eNF_P, + eNF_C, + eNF_M, + eNF_A +}; + +typedef struct sOneNuclideInfo +{ + char scname[64]; + int flag; + double halflife; + eHalfLifeUnit halflifeunit; + double nuclidecertainty; + int energylinecount; +}sOneNuclideInfo; + +typedef struct sOneNuclide +{ + sOneNuclideInfo info; + sOneEnergyLine *pEnergyLine; +public: + sOneEnergyLine* GetEnergyLineByEnergy(double energy) + { + for (int i = 0; i < info.energylinecount; i++) + { + if (pEnergyLine[i].energy == energy) + { + return pEnergyLine + i; + } + } + return NULL; + } +}sOneNuclide; + +typedef struct sNuclideLibraryInfo +{ + int head; + int version; + char createtime[NI_NAME_LEN]; + char lastmodifytime[NI_NAME_LEN]; + char nuclidelibname[NI_NAME_LEN]; + int totalnuclidecount; + int totalenergycount; + unsigned char reserve[32]; +}sNuclideLibraryInfo; + +typedef void(__stdcall *ON_UPDATE_NUCLIDE_CALLBACK)(void *pHandle, sNuclideLibraryInfo* pnli, sOneNuclide *pNuclide, void *reserve); + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + DEVICEMANAGE_API_NUCLIB_C(int) NLGetCurNuclideInfo(ON_UPDATE_NUCLIDE_CALLBACK pnli, char *ErrMsg); + DEVICEMANAGE_API_NUCLIB_C(void) NLFreeNuclideLibrary(); + DEVICEMANAGE_API_NUCLIB_C(int) NLUpdateCurNuclideToDriver(void *pBuf, int len, char *ErrMsg); + DEVICEMANAGE_API_NUCLIB_C(int) NLSaveNuclideLibraryToFile(void* phandle, char *ErrMsg); + DEVICEMANAGE_API_NUCLIB_C(int) NLSaveAsNuclideLibraryToFile(void* phandle, const char *path, char *ErrMsg); + DEVICEMANAGE_API_NUCLIB_C(void*) NLReadNuclideFromFile(const char *path, char *ErrMsg); + DEVICEMANAGE_API_NUCLIB_C(int) NLGetNuclideInfo(void* phandle, ON_UPDATE_NUCLIDE_CALLBACK pnli, char *ErrMsg); + DEVICEMANAGE_API_NUCLIB_C(int) NLSetCurNuclideLibrary(void* phandle, char *ErrMsg); +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/3rdlib/DeviceManage/include/OneChannelDataDefine.h b/3rdlib/DeviceManage/include/OneChannelDataDefine.h new file mode 100644 index 0000000..29944aa --- /dev/null +++ b/3rdlib/DeviceManage/include/OneChannelDataDefine.h @@ -0,0 +1,69 @@ +#pragma once +#include +#ifdef DEVICEMANAGE_EXPORTS +#define DEVICEMANAGE_API_DATADEAL_C(type) extern "C" _declspec(dllexport) type _stdcall +#else +#define DEVICEMANAGE_API_DATADEAL_C(type) extern "C" _declspec(dllexport) type _stdcall +#endif +#pragma pack(push,1) + +typedef struct sPeriodMeasureInfo +{ + BOOL bEnable; + int iTotalMsrCount; + int iCurMsrCount; + int iOneTotalMsrTimeInMs; + unsigned char reserve[16]; +}sPeriodMeasureInfo; +#ifndef DEVICEMANAGE_EXPORTS + + +// 修改该结构后,一定要修改OneAntiSatisfyMainAndSubChCountStatistic结构 +class OneDataInfo +{ +public: + void *virtualptr; + __int64 * m_pData; + int m_iDataLen; + void *m_pDataMutex; + double m_dRealTime; + __int64 m_i64TotalCount; + double m_dCountRate; + int m_LastUpdateTickCount; + __int64 m_BeginTickCount; + __int64 m_TotalTickCount; + double m_DeadTimeInSec;; + // 测量时间 + SYSTEMTIME m_sDateTime; + double dreserve; + void *m_pPriveOnfo; +}; + +class OneSpecturmDataInfo :public OneDataInfo +{ +public: + __int64 m_i64ICR; + double m_dLiveTimeSec; + double m_dLiveTimePercent; + sPeriodMeasureInfo m_pmi; +}; + +class OneRiseTimeStatisticInfo :public OneDataInfo +{ + +}; + +class OneFallTimeStatisticInfo :public OneRiseTimeStatisticInfo +{ + +}; + +class OneLMDeltTimeStatisticInfo :public OneDataInfo +{ + +}; + +#endif + + +#pragma pack(pop) diff --git a/3rdlib/DeviceManage/include/OneChannelDataExport.h b/3rdlib/DeviceManage/include/OneChannelDataExport.h new file mode 100644 index 0000000..4750c11 --- /dev/null +++ b/3rdlib/DeviceManage/include/OneChannelDataExport.h @@ -0,0 +1,72 @@ +#pragma once + +#ifndef DEVICEMANAGE_EXPORTS + +#pragma pack(push,1) + +typedef struct sPeriodMeasureInfo +{ + BOOL bEnable; + int iTotalMsrCount; + int iCurMsrCount; + int iOneTotalMsrTimeInMs; + unsigned char reserve[16]; +}sPeriodMeasureInfo; + +class OneDataInfo +{ +public: + void* __vfp; + __int64 * m_pData; + int m_iDataLen; + void *m_pDataMutex; + double m_dRealTime; + __int64 m_i64TotalCount; + double m_dCountRate; + int m_LastUpdateTickCount; + __int64 m_BeginTickCount; + __int64 m_TotalTickCount; + double m_DeadTimeInSec;; + // 测量时间 + SYSTEMTIME m_sDateTime; + double dreserve; + void *m_pPriveOnfo; +}; + +class OneSpecturmDataInfo :public OneDataInfo +{ +public: + __int64 m_i64ICR; + double m_dLiveTimeSec; + double m_dLiveTimePercent; + sPeriodMeasureInfo m_pmi; +}; + +typedef struct sLMPointData +{ + __int64 time; + int amp; +}sLMPointData; + +class OneChannelLMPointData +{ +public: + void* __vfp; + sLMPointData * m_pData; + int m_iDataLen; +}; +// 谱线数据 +class OneChannelData +{ +public: + double m_LastUpdatePresentTime = 0; + OneSpecturmDataInfo m_OrgSprtData; + OneDataInfo m_RiseTimeInfo; + OneDataInfo m_FallTimeInfo; + OneChannelLMPointData m_LMPointData; + OneDataInfo m_LMDeltTimeInfo; +}; + +#pragma pack(pop) + +#endif \ No newline at end of file diff --git a/3rdlib/DeviceManage/include/OneChannelRoiConfigDefine.h b/3rdlib/DeviceManage/include/OneChannelRoiConfigDefine.h new file mode 100644 index 0000000..e31f25c --- /dev/null +++ b/3rdlib/DeviceManage/include/OneChannelRoiConfigDefine.h @@ -0,0 +1,85 @@ +#pragma once + +#ifdef DEVICEMANAGE_EXPORTS +#define DEVICEMANAGE_API_ROI_C(type) extern "C" _declspec(dllexport) type _stdcall +#else +#define DEVICEMANAGE_API_ROI_C(type) extern "C" _declspec(dllexport) type _stdcall +#endif + +#pragma pack(push,1) +struct OneRoiDetailInfo +{ + int left; + int right; + + unsigned char reserve[8]; + + void SetTop(int top) + { + int *t = (int*)reserve; + *t = top; + } + void SetBottom(int bot) + { + int *t = (int*)(reserve + 4); + *t = bot; + } + int GetTop() + { + int *t = (int*)reserve; + return *t; + } + int GetBottom() + { + int *t = (int*)(reserve + 4); + return *t; + } +}; + +enum eROICalcDataType +{ + eOrgSprt, + eCoinSprt, + eAntiSprt, + e2DMapSprt, + e2DMapMainCmbSubCmb, + e2DMapMainMulSubCmb, +}; + +struct OneRoiDatalInfo +{ + int version; + eROICalcDataType dataType; + + __int64 i64TotalCount; + double dcps; + unsigned char reserve[32]; +}; + +struct OneChannelROIInfo +{ + int head; + int version; + + int iTotalNum; + unsigned char reserve[32]; +}; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + DEVICEMANAGE_API_ROI_C(int) DBCAddROI(int *pHandle, int bdidx, int chidx, OneRoiDetailInfo *proi); + DEVICEMANAGE_API_ROI_C(int) DBCRemoveROI(int *pHandle, int bdidx, int chidx, OneRoiDetailInfo *proi); + DEVICEMANAGE_API_ROI_C(int) DBCRemoveAllROI(int *pHandle, int bdidx, int chidx); + DEVICEMANAGE_API_ROI_C(int) DBCGetROIDataInfo(void * pHandle, int bdidx, int chidx, OneRoiDetailInfo *proi, OneRoiDatalInfo *pdata); + DEVICEMANAGE_API_ROI_C(int) DBCSaveROIDataInfoToFile(void * pHandle, int bdidx, int chidx, OneRoiDetailInfo *proi, OneRoiDatalInfo *pdata, char *pDir); + DEVICEMANAGE_API_ROI_C(int) DBCGetROIInfo(int *pHandle, int bdidx, int chidx, OneChannelROIInfo *pinfo); + DEVICEMANAGE_API_ROI_C(int) DBCGetROIDetailInfo(int *pHandle, int bdidx, int chidx, int roiidx, OneRoiDetailInfo *pdetail); + DEVICEMANAGE_API_ROI_C(int) DBCSaveROIToFile(int *pHandle, int bdidx, int chidx, const char *path, char *ErrMsg); + DEVICEMANAGE_API_ROI_C(int) DBCReadROIFromFile(int *pHandle, int bdidx, int chidx, const char *path, char *ErrMsg); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/3rdlib/DeviceManage/include/OneComptonResultDataExport.h b/3rdlib/DeviceManage/include/OneComptonResultDataExport.h new file mode 100644 index 0000000..dcbe983 --- /dev/null +++ b/3rdlib/DeviceManage/include/OneComptonResultDataExport.h @@ -0,0 +1,145 @@ +#pragma once + +#ifndef DEVICEMANAGE_EXPORTS + +#include +#include "OneChannelDataExport.h" +#include +using namespace std; + +#define ANTI_COMPTON_MAX_NAME_LEN 32 + +#pragma pack(push,1) + +enum eComptonFuntion +{ + 空闲, + 主探测器通道, + 符合通道, + 反符合通道, +}; + +class OneComptonSuppressSetting +{ +public: + char m_strMCName[ANTI_COMPTON_MAX_NAME_LEN]; + char m_strMethodName[ANTI_COMPTON_MAX_NAME_LEN]; + eComptonFuntion m_eFunction; + int m_iBoardNum; + int m_iChannelNum; + BOOL m_bDisplayOrgCptSprt; + BOOL m_bDisplayNowCptSprt; + BOOL m_bDisplayLMSprt; + BOOL m_bDisplayRiseTimeSprt; + BOOL m_bDisplayFallTimeSprt; + __int64 m_i64AdjustTickCount; + __int64 m_i64MinAllowTickCount; + __int64 m_i64MaxAllowTickCount; + BOOL m_bContainAmpValue; + + unsigned char reserve[32]; + + int m_iSubSettingNum; + OneComptonSuppressSetting *m_pSubComptonSetting; +}; + +class OneDeviceComptonSuppressSetting +{ +public: + int m_iTotalComptonNum; + int m_iTotalChannelNum; + unsigned char reserve[32]; + OneComptonSuppressSetting *m_pComptonSetting; +}; + +struct OneMainAndChCountStatistic +{ + __int64 ch; + __int64 count; +}; + +class OneAntiSatisfyMainAndSubChCountStatistic +{ +public: + void* __vfp; + map<__int64, int> *m_pmapData; + OneMainAndChCountStatistic * m_pData; + void* m_mutex; + int m_iMaxDataLen; + int m_iCurDataLen; + + double m_dRealTime; + __int64 m_i64TotalCount; + double m_dCountRate; + int m_LastUpdateTickCount; + __int64 m_BeginTickCount; + __int64 m_TotalTickCount; + double m_DeadTimeInSec; + SYSTEMTIME m_sDateTime; + double dreserve; + void *m_pPriveOnfo; +}; + + +class OneAntiComptonStatisticInfoExt +{ +public: + OneComptonSuppressSetting *m_pCptSetting; + OneAntiComptonStatisticInfoExt *m_pSub; + OneSpecturmDataInfo m_CoinSprtInfo; + OneSpecturmDataInfo m_AntiCoinSprtInfo; + OneAntiSatisfyMainAndSubChCountStatistic m_MainAndSubChStatistic; +}; + +class OneAntiComptonSatisfyTimeInfo :public OneDataInfo +{ +protected: + int m_iMinTime; + int m_iMaxTime; +}; + +class AntiSatisfyDatas +{ +public: + void* __vfp; + int m_iBufferLen; + int m_iCurLen; + void *m_pAntiSatisfyData; + int m_iBufferLenExt; + int m_iCurLenExt; + void *m_pAntiSatisfyDataExt; +}; + +class OneAntiComptonStatisticInfoExtForAll +{ +public: + OneComptonSuppressSetting *m_pCptSetting; + OneAntiComptonStatisticInfoExt *m_pSub; + OneSpecturmDataInfo m_CoinCmbSprtInfo; + OneSpecturmDataInfo m_AntiCoinSprtInfo; + OneSpecturmDataInfo m_CoinSingleSprtInfo; + OneSpecturmDataInfo m_CoinMulSprtInfo; + + // 主多-符合合成 + OneSpecturmDataInfo m_MainMulSubCmbSprtInfo; + // 主多-符合无 + OneSpecturmDataInfo m_MainMulSubNoneSprtInfo; + + OneSpecturmDataInfo m_SubCoinMainCmbSubCmbSprtInfo; + // 符合通道所有,主探测器通道多个 + OneSpecturmDataInfo m_SubCoinMainMulSubCmbSprtInfo; + + OneAntiComptonSatisfyTimeInfo m_SatisfyTimeInfo; + OneAntiComptonSatisfyTimeInfo m_SatisfyTimeInfoMin; + OneAntiComptonSatisfyTimeInfo m_SatisfyTimeInfoMax; + + AntiSatisfyDatas m_SatisfyDatas; + + OneAntiSatisfyMainAndSubChCountStatistic m_MainAndSubChStatisticForCmb; + OneAntiSatisfyMainAndSubChCountStatistic m_MainMulAndSubCmbChStatistic; + +}; + +#pragma pack(pop) + +#endif \ No newline at end of file diff --git a/3rdlib/DeviceManage/include/OneEfficientCalibarationDefine.h b/3rdlib/DeviceManage/include/OneEfficientCalibarationDefine.h new file mode 100644 index 0000000..6023c28 --- /dev/null +++ b/3rdlib/DeviceManage/include/OneEfficientCalibarationDefine.h @@ -0,0 +1,92 @@ +#pragma once + +#ifdef DEVICEMANAGE_EXPORTS +#define DEVICEMANAGE_API_EFFIC_C(type) extern "C" _declspec(dllexport) type _stdcall +#else +#define DEVICEMANAGE_API_EFFIC_C(type) extern "C" _declspec(dllexport) type _stdcall +#endif + + +#include "NuclideLibraryDefine.h" +#include "AnalyzeSpecturmExport.h" +#pragma pack(push,1) + +enum eActivityUnit +{ + eGPS, + eBq, + euCi, +}; + +struct Activity +{ + double v; + eActivityUnit unit; + double uncertainty; +}; + +struct OneEFTPoint +{ + char isotope[32]; + double energy; + double efficency; + double fitefficency; + double deltefficency; + Activity activity; + char ertificateTime[32]; + double halfLife; + eHalfLifeUnit halfLifeUnit; + double efficencyuncertain; + double percent; + unsigned char reserve[16]; +}; + +struct OneKneePoint +{ + double KneeEnergy; + sLinearFitResult BlowFitResult; + sLinearFitResult AboveFitResult; + + unsigned char reserve[64]; +}; + +struct OneEfficienCalibarationInfo +{ + int head; + int version; + int curEFTPoint; + int curKneePoint; + unsigned char reserve[32]; +}; + +struct EfficOnePeakInfo +{ + OneValue area; +}; +class OneEfficientCalibaration; + +#ifndef DEVICEMANAGE_EXPORTS +class OneEfficientCalibaration +{ +public: + OneEfficienCalibarationInfo info; + OneKneePoint kneepoint[16]; + OneEFTPoint eftpoint[256]; +}; +#endif +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + DEVICEMANAGE_API_EFFIC_C(int) DBCGetDeviceBoardChannelEfficCaliinfo(int *pHandle, OneEfficientCalibaration *pUCI, int bdidx, int chidx); + DEVICEMANAGE_API_EFFIC_C(int) DBCAddEFTCaliPoint(int *pHandle, int bdidx, int chidx, OneEFTPoint *point); + DEVICEMANAGE_API_EFFIC_C(int) DBCRemoveEFTCaliPoint(int *pHandle, int bdidx, int chidx, OneEFTPoint *point); + DEVICEMANAGE_API_EFFIC_C(int) DBCSaveEFTCaliToFile(int *pHandle, int bdidx, int chidx, const char *path, char *ErrMsg); + DEVICEMANAGE_API_EFFIC_C(int) DBCReadEFTCaliFromFile(int *pHandle, int bdidx, int chidx, const char *path, char *ErrMsg); + DEVICEMANAGE_API_EFFIC_C(int) DBCGetEFTCaliinfo(int *pHandle, OneEfficientCalibaration *pUCI, int bdidx, int chidx); + DEVICEMANAGE_API_EFFIC_C(int) DBCChangeKneePointInfo(int *pHandle, int bdidx, int chidx, OneKneePoint *proi); + DEVICEMANAGE_API_EFFIC_C(int) DBCCalcEfficient(int *pHandle, int bdidx, int chidx, ASDataInfo* pASDI, EfficOnePeakInfo *popi, OneEFTPoint *poeft); +#ifdef __cplusplus +} +#endif diff --git a/3rdlib/DeviceManage/include/OneEnergyCalibarationDefine.h b/3rdlib/DeviceManage/include/OneEnergyCalibarationDefine.h new file mode 100644 index 0000000..6a410fb --- /dev/null +++ b/3rdlib/DeviceManage/include/OneEnergyCalibarationDefine.h @@ -0,0 +1,66 @@ +#pragma once +#ifdef DEVICEMANAGE_EXPORTS +#define DEVICEMANAGE_API_ENG_C(type) extern "C" _declspec(dllexport) type _stdcall +#else +#define DEVICEMANAGE_API_ENG_C(type) extern "C" _declspec(dllexport) type _stdcall +#endif + +#include "AnalyzeSpecturmExport.h" + +#pragma pack(push,1) + +struct OneECPoint +{ + double channel; + double energy; + double fitenergy; + double deltaenergy; + int energyfitflag; + double fwhm; + double fitfwhm; + double deltafwhm; + int fwhmfitflag; +}; + +struct OneEnergyCalibarationInfo +{ + int head; + int version; + int curPoint; + int MaxChannel; + sLinearFitResult energylfr; + sLinearFitResult fwhmlfr; + eLinearFitType energyFitType; + unsigned char reserve[28]; +}; + +class OneEnergyCalibaration; + +#define MAX_ECPINT_NUM 64 + +#ifndef DEVICEMANAGE_EXPORTS +class OneEnergyCalibaration +{ +public: + OneEnergyCalibarationInfo info; + OneECPoint point[MAX_ECPINT_NUM]; +}; +#endif + +#pragma pack(pop) + + +#ifdef __cplusplus +extern "C" { +#endif + DEVICEMANAGE_API_ENG_C(int) DBCGetEnergyCaliinfo(int *pHandle, OneEnergyCalibaration *pUCI, int bdidx, int chidx); + DEVICEMANAGE_API_ENG_C(int) DBCAddEnergyCaliPoint(int *pHandle, int bdidx, int chidx, OneECPoint *point); + DEVICEMANAGE_API_ENG_C(int) DBCRemoveEnergyCaliPoint(int *pHandle, int bdidx, int chidx, OneECPoint *point); + DEVICEMANAGE_API_ENG_C(int) DBCChangeEnergyFitType(int *pHandle, int bdidx, int chidx, eLinearFitType lft); + DEVICEMANAGE_API_ENG_C(int) DBCSaveEnergyCaliToFile(int *pHandle, int bdidx, int chidx, const char *path, char *ErrMsg); + DEVICEMANAGE_API_ENG_C(int) DBCReadEnergyCaliFromFile(int *pHandle, int bdidx, int chidx, const char *path, char *ErrMsg); + DEVICEMANAGE_API_ENG_C(int) DBCSaveCaliToFile(int *pHandle, int bdidx, int chidx, const char *path, char *ErrMsg); + DEVICEMANAGE_API_ENG_C(int) DBCReadCaliFromFile(int *pHandle, int bdidx, int chidx, const char *path, char *ErrMsg); +#ifdef __cplusplus +} +#endif diff --git a/3rdlib/DeviceManage/include/PeakInfoDefine.h b/3rdlib/DeviceManage/include/PeakInfoDefine.h new file mode 100644 index 0000000..11a2292 --- /dev/null +++ b/3rdlib/DeviceManage/include/PeakInfoDefine.h @@ -0,0 +1,70 @@ +#pragma once +#include +#pragma pack(push,1) + +typedef struct sOnePeakInfoV2 +{ + double dCursorChannel; + double dCursorCount; + double dCursorEnergy; + BOOL bFindPeak; + double dPeakChannel; // 峰所在道址 + double dPeakEnergy; // 峰所在的能量 + double dLeftChannel; + double dRightChannel; + char cNName[64]; + double iNIChannel; + double dNIEnergy; + double fwhmpercent; + double fwhmenergy; + double fwhxmpercent; + double fwhxmenergy; + // 总计数 + double dGrossArea; + // 总计数率 + double dGrossAreaCountRate; + // 净计数 + double dNetArea; + // 净计数率 + double dNetAreaCountRate; + double dNetAreaUncertain; + double dcA; // counting activity + unsigned char reserve[32]; +}sOnePeakInfoV2; + +enum eDisplaSprtInfoType +{ + eOrg, + eCoin, + eAnitCoin, +}; +enum eCalcSprtSubInfoType +{ + 主单多合成, + 主单合成, + 主多累加, + 主多合成_符合单多, + 主多合成, + 符合单多合成_主单多, + 符合单多合成_主多, +}; + +typedef struct sCursorInfo +{ + int* pHandle; // 设备句柄 + int bdidx; + int chidx; + double channel; + eDisplaSprtInfoType calcType; + eCalcSprtSubInfoType sprtSubType; + unsigned char reserve[28]; +}sCursorInfo; + +typedef struct sROICalcConfig +{ + int bdCalclen; + double bdCalcUncertainPercent; +}sROICalcConfig; + + +#pragma pack(pop) \ No newline at end of file diff --git a/3rdlib/DeviceManage/include/UAVrtp.h b/3rdlib/DeviceManage/include/UAVrtp.h new file mode 100644 index 0000000..ca385d6 --- /dev/null +++ b/3rdlib/DeviceManage/include/UAVrtp.h @@ -0,0 +1,448 @@ +#ifndef _UAV_RTP_H_ +#define _UAV_RTP_H_ + +#include + +#ifdef UAV_EXPORTS +#define UAV_EXPORTS_DLL(type) extern "C" _declspec(dllexport) type _stdcall +#else +#define UAV_EXPORTS_DLL(type) extern "C" _declspec(dllimport) type _stdcall +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef UAV_EXPORTS +#define MSSCOMMUNICATION_API(t) _declspec(dllexport) t __stdcall +#else +#define MSSCOMMUNICATION_API(t) _declspec(dllimport) t __stdcall +#endif + +#define MSS_MAX_DEVICE_NUM 64 + +#pragma pack(push,1) + + typedef struct sDeviceList + { + int totalNum; + int *pHandle[MSS_MAX_DEVICE_NUM]; + }sDeviceList; + + typedef struct sServerInfo + { + unsigned char comType; + unsigned short port; + unsigned char serverIP[4]; + unsigned char reserve[28]; + }sServerInfo; + + typedef struct sFindDeviceConfig + { + BOOL ETHEnable; + char macaddr[128]; + char dhcpcfgfile[256]; + int useDHCP; + BOOL COMEnable; + BOOL USB20Enable; + BOOL USB30Enable; + }sFindDeviceConfig; + + typedef enum _eDataTransferType + { + eDTT_Unkown, + eDTT_TCP, + eDTT_UDP, + eDTT_USB20, + eDTT_USB30, + eDTT_COM, + }eDataTransferType; +#pragma pack(pop) + + /***************************************************************** + ** 鍑 鏁 鍚: PMSSPROTCOLCMDCALLBACK + ** 鍔熻兘鎻忚堪: 閫氱煡鐢ㄦ埛鍥炶皟 + ** 杈撱 鍏: + pBuf 鍖呭惈鍛戒护鐨勫唴瀛 + iLlen 鍛戒护闀垮害 + lpBuf 鏌ヨ鍛戒护鐨勯檮鍔犲唴瀛 + ilpLen 鏌ヨ鍛戒护鐨勯暱搴 + iCode 閿欒鐮 + msg 閿欒淇℃伅 + ** 杩 鍥 鍊: 0 鎴愬姛 + + ** 浣溿 鑰: yl + ** 鍒涘缓鏃ユ湡: 2015-7-11 + ** 鐗 鏈細v1.0.0.0 + ** 淇敼浣滆: + ** 淇敼鏃ユ湡: + ** 淇敼鎻忚堪: + ****************************************************************/ + typedef void(__stdcall *PMSSPROTCOLCMDCALLBACK)(void* pHandle,void *pBuf, int iLen, void *lpBuf, int ilpLen, int iCode, const char *msg); + + /***************************************************************** + ** 鍑 鏁 鍚: Mss_SetCmdCallBack(void *pbuf,int len) + ** 鍔熻兘鎻忚堪: 璁剧疆鍛戒护鍙戦佸洖璋冨嚱鏁 + ** 杈撱 鍏: + pMssCmdCallBack 鍥炶皟鍑芥暟鎸囬拡 + ** 杩 鍥 鍊: + 0 鎴愬姛 + ----------鏍煎紡妫鏌ヨ繑鍥為敊璇唬鐮---------------------- + -1 浼犲叆鏁版嵁闀垮害閿欒 + -2 浼犲叆涓嶆敮鎸佺殑鍛戒护鏍煎紡 + -3 浼犲叆涓嶆敮鎸佺殑鍛戒护鍊 + -4 鏀寔鐨勫懡浠ゅ硷紝浣嗘槸涓嶆敮鎸佽鍛戒护绫诲瀷 + -5 鏁版嵁闀垮害杩囬暱 + -6 鏁版嵁闀垮害杩囩煭 + -7 鏈垵濮嬪寲鎴愬姛 + -20 鍏朵粬鏈煡閿欒 + ----------鍙戦佹暟鎹繃绋嬭繑鍥為敊璇唬鐮---------------------- + 500 鍛戒护宸茬粡鍦ㄥ彂閫侀槦鍒椾腑 + 501 + 502 + 503 璁惧娌℃湁杩炴帴 + 504 鍙戦佹暟鎹垚鍔 + 505 鍙戦佹暟鎹け璐 + 506 鍙戦佹垚鍔熶絾鏄病鏈夋敹鍒板洖澶 + 507 鍙戦佸懡浠よ秴鏃 + 508 鎺ユ敹鍒版暟鎹 + ----------纭欢璁惧杩斿洖閿欒浠g爜---------------------- + 1 鍙戦佸懡浠ら敊璇 + 10 涓嶆敮鎸佺殑鍛戒护绫诲瀷 + 11 crc鏍¢獙澶辫触 + 12 鍛戒护鍙傛暟涓嶅湪鑼冨洿鍐 + 13 鍙戦佹暟鎹埌搴曞眰璁惧澶辫触 + 14 鍐檉lash澶辫触 + 15 涓嶆敮鎸佺殑鍊 + 16 浠庤澶囪鍙栨暟鎹け璐 + ** 浣溿 鑰: yl + ** 鍒涘缓鏃ユ湡: 2015-7-9 + ** 鐗 鏈細v1.0.0.0 + ** 淇敼浣滆: + ** 淇敼鏃ユ湡: + ** 淇敼鎻忚堪: + ****************************************************************/ + MSSCOMMUNICATION_API(int) Mss_SetCmdCallBack(void *pProtocol,void *pHandle ,PMSSPROTCOLCMDCALLBACK pMssCmdCallBack); + /***************************************************************** + ** 鍑 鏁 鍚: Mss_SetLuaCmdCallBack(void *pbuf,int len) + ** 鍔熻兘鎻忚堪: 璁剧疆鍛戒护鍙戦佸洖璋冨嚱鏁 + ** 杈撱 鍏: + pMssCmdCallBack 鍥炶皟鍑芥暟鎸囬拡 + ** 杩 鍥 鍊: + 0 鎴愬姛 + ----------鏍煎紡妫鏌ヨ繑鍥為敊璇唬鐮---------------------- + -1 浼犲叆鏁版嵁闀垮害閿欒 + -2 浼犲叆涓嶆敮鎸佺殑鍛戒护鏍煎紡 + -3 浼犲叆涓嶆敮鎸佺殑鍛戒护鍊 + -4 鏀寔鐨勫懡浠ゅ硷紝浣嗘槸涓嶆敮鎸佽鍛戒护绫诲瀷 + -5 鏁版嵁闀垮害杩囬暱 + -6 鏁版嵁闀垮害杩囩煭 + -7 鏈垵濮嬪寲鎴愬姛 + -20 鍏朵粬鏈煡閿欒 + ----------鍙戦佹暟鎹繃绋嬭繑鍥為敊璇唬鐮---------------------- + 500 鍛戒护宸茬粡鍦ㄥ彂閫侀槦鍒椾腑 + 501 + 502 + 503 璁惧娌℃湁杩炴帴 + 504 鍙戦佹暟鎹垚鍔 + 505 鍙戦佹暟鎹け璐 + 506 鍙戦佹垚鍔熶絾鏄病鏈夋敹鍒板洖澶 + 507 鍙戦佸懡浠よ秴鏃 + 508 鎺ユ敹鍒版暟鎹 + ----------纭欢璁惧杩斿洖閿欒浠g爜---------------------- + 1 鍙戦佸懡浠ら敊璇 + 10 涓嶆敮鎸佺殑鍛戒护绫诲瀷 + 11 crc鏍¢獙澶辫触 + 12 鍛戒护鍙傛暟涓嶅湪鑼冨洿鍐 + 13 鍙戦佹暟鎹埌搴曞眰璁惧澶辫触 + 14 鍐檉lash澶辫触 + 15 涓嶆敮鎸佺殑鍊 + 16 浠庤澶囪鍙栨暟鎹け璐 + ** 浣溿 鑰: yl + ** 鍒涘缓鏃ユ湡: 2018-6-4 + ** 鐗 鏈細v1.0.0.0 + ** 淇敼浣滆: + ** 淇敼鏃ユ湡: + ** 淇敼鎻忚堪: + ****************************************************************/ + MSSCOMMUNICATION_API(int) Mss_SetLuaCmdCallBack(void *pProtocol, PMSSPROTCOLCMDCALLBACK pMssCmdCallBack); + /***************************************************************** + ** 鍑 鏁 鍚: Mss_Destory + ** 鍔熻兘鎻忚堪: 閲婃斁璧勬簮鍑芥暟 + ** 杈撱 鍏: + + ** 杩 鍥 鍊: + 0 鎴愬姛 + ----------鏍煎紡妫鏌ヨ繑鍥為敊璇唬鐮---------------------- + -1 浼犲叆鏁版嵁闀垮害閿欒 + -2 浼犲叆涓嶆敮鎸佺殑鍛戒护鏍煎紡 + -3 浼犲叆涓嶆敮鎸佺殑鍛戒护鍊 + -4 鏀寔鐨勫懡浠ゅ硷紝浣嗘槸涓嶆敮鎸佽鍛戒护绫诲瀷 + -5 鏁版嵁闀垮害杩囬暱 + -6 鏁版嵁闀垮害杩囩煭 + -7 鏈垵濮嬪寲鎴愬姛 + -20 鍏朵粬鏈煡閿欒 + ----------鍙戦佹暟鎹繃绋嬭繑鍥為敊璇唬鐮---------------------- + 500 鍛戒护宸茬粡鍦ㄥ彂閫侀槦鍒椾腑 + 501 + 502 + 503 璁惧娌℃湁杩炴帴 + 504 鍙戦佹暟鎹垚鍔 + 505 鍙戦佹暟鎹け璐 + 506 鍙戦佹垚鍔熶絾鏄病鏈夋敹鍒板洖澶 + 507 鍙戦佸懡浠よ秴鏃 + 508 鎺ユ敹鍒版暟鎹 + ----------纭欢璁惧杩斿洖閿欒浠g爜---------------------- + 1 鍙戦佸懡浠ら敊璇 + 10 涓嶆敮鎸佺殑鍛戒护绫诲瀷 + 11 crc鏍¢獙澶辫触 + 12 鍛戒护鍙傛暟涓嶅湪鑼冨洿鍐 + 13 鍙戦佹暟鎹埌搴曞眰璁惧澶辫触 + 14 鍐檉lash澶辫触 + 15 涓嶆敮鎸佺殑鍊 + 16 浠庤澶囪鍙栨暟鎹け璐 + ** 浣溿 鑰: yl + ** 鍒涘缓鏃ユ湡: 2015-7-9 + ** 鐗 鏈細v1.0.0.0 + ** 淇敼浣滆: + ** 淇敼鏃ユ湡: + ** 淇敼鎻忚堪: + ****************************************************************/ + MSSCOMMUNICATION_API(int) Mss_Destory(); + /***************************************************************** + ** 鍑 鏁 鍚: Mss_Init + ** 鍔熻兘鎻忚堪: 鍒濆鍖栧嚱鏁 + ** 杈撱 鍏: + + ** 杩 鍥 鍊: + 0 鎴愬姛 + ----------鏍煎紡妫鏌ヨ繑鍥為敊璇唬鐮---------------------- + -1 浼犲叆鏁版嵁闀垮害閿欒 + -2 浼犲叆涓嶆敮鎸佺殑鍛戒护鏍煎紡 + -3 浼犲叆涓嶆敮鎸佺殑鍛戒护鍊 + -4 鏀寔鐨勫懡浠ゅ硷紝浣嗘槸涓嶆敮鎸佽鍛戒护绫诲瀷 + -5 鏁版嵁闀垮害杩囬暱 + -6 鏁版嵁闀垮害杩囩煭 + -7 鏈垵濮嬪寲鎴愬姛 + -20 鍏朵粬鏈煡閿欒 + ----------鍙戦佹暟鎹繃绋嬭繑鍥為敊璇唬鐮---------------------- + 500 鍛戒护宸茬粡鍦ㄥ彂閫侀槦鍒椾腑 + 501 + 502 + 503 璁惧娌℃湁杩炴帴 + 504 鍙戦佹暟鎹垚鍔 + 505 鍙戦佹暟鎹け璐 + 506 鍙戦佹垚鍔熶絾鏄病鏈夋敹鍒板洖澶 + 507 鍙戦佸懡浠よ秴鏃 + 508 鎺ユ敹鍒版暟鎹 + ----------纭欢璁惧杩斿洖閿欒浠g爜---------------------- + 1 鍙戦佸懡浠ら敊璇 + 10 涓嶆敮鎸佺殑鍛戒护绫诲瀷 + 11 crc鏍¢獙澶辫触 + 12 鍛戒护鍙傛暟涓嶅湪鑼冨洿鍐 + 13 鍙戦佹暟鎹埌搴曞眰璁惧澶辫触 + 14 鍐檉lash澶辫触 + 15 涓嶆敮鎸佺殑鍊 + 16 浠庤澶囪鍙栨暟鎹け璐 + ** 浣溿 鑰: yl + ** 鍒涘缓鏃ユ湡: 2015-7-9 + ** 鐗 鏈細v1.0.0.0 + ** 淇敼浣滆: + ** 淇敼鏃ユ湡: + ** 淇敼鎻忚堪: + ****************************************************************/ + MSSCOMMUNICATION_API(int) Mss_Init(); + /***************************************************************** + ** 鍑 鏁 鍚: Mss_FindDevice + ** 鍔熻兘鎻忚堪: 鏌ユ壘璁惧鍑芥暟锛屽簲璇ヨ皟鐢ㄦ洿涓婂眰鐨刦inddevice锛堬級鍑芥暟浠f浛璇ュ嚱鏁 + ** 杈撱 鍏: + pMssCmdCallBack 鍥炶皟鍑芥暟鎸囬拡 + ** 杩 鍥 鍊: + 0 鎴愬姛 + ----------鏍煎紡妫鏌ヨ繑鍥為敊璇唬鐮---------------------- + -1 浼犲叆鏁版嵁闀垮害閿欒 + -2 浼犲叆涓嶆敮鎸佺殑鍛戒护鏍煎紡 + -3 浼犲叆涓嶆敮鎸佺殑鍛戒护鍊 + -4 鏀寔鐨勫懡浠ゅ硷紝浣嗘槸涓嶆敮鎸佽鍛戒护绫诲瀷 + -5 鏁版嵁闀垮害杩囬暱 + -6 鏁版嵁闀垮害杩囩煭 + -7 鏈垵濮嬪寲鎴愬姛 + -20 鍏朵粬鏈煡閿欒 + ----------鍙戦佹暟鎹繃绋嬭繑鍥為敊璇唬鐮---------------------- + 500 鍛戒护宸茬粡鍦ㄥ彂閫侀槦鍒椾腑 + 501 + 502 + 503 璁惧娌℃湁杩炴帴 + 504 鍙戦佹暟鎹垚鍔 + 505 鍙戦佹暟鎹け璐 + 506 鍙戦佹垚鍔熶絾鏄病鏈夋敹鍒板洖澶 + 507 鍙戦佸懡浠よ秴鏃 + 508 鎺ユ敹鍒版暟鎹 + ----------纭欢璁惧杩斿洖閿欒浠g爜---------------------- + 1 鍙戦佸懡浠ら敊璇 + 10 涓嶆敮鎸佺殑鍛戒护绫诲瀷 + 11 crc鏍¢獙澶辫触 + 12 鍛戒护鍙傛暟涓嶅湪鑼冨洿鍐 + 13 鍙戦佹暟鎹埌搴曞眰璁惧澶辫触 + 14 鍐檉lash澶辫触 + 15 涓嶆敮鎸佺殑鍊 + 16 浠庤澶囪鍙栨暟鎹け璐 + ** 浣溿 鑰: yl + ** 鍒涘缓鏃ユ湡: 2015-7-9 + ** 鐗 鏈細v1.0.0.0 + ** 淇敼浣滆: + ** 淇敼鏃ユ湡: + ** 淇敼鎻忚堪: + ****************************************************************/ + MSSCOMMUNICATION_API(int) Mss_FindDevice(sFindDeviceConfig *pfdc,sServerInfo *pServerInfo, int len, sDeviceList* pDeviceList); + /***************************************************************** + ** 鍑 鏁 鍚: FindDevice + ** 鍔熻兘鎻忚堪: 鏌ユ壘璁惧鍑芥暟 + ** 杈撱 鍏: + inout 鏌ユ壘璁惧缁撴瀯锛屾渶澶ф煡鎵64涓澶 + ** 杩 鍥 鍊: + 0 鎴愬姛 + ----------鏍煎紡妫鏌ヨ繑鍥為敊璇唬鐮---------------------- + -1 浼犲叆鏁版嵁闀垮害閿欒 + -2 浼犲叆涓嶆敮鎸佺殑鍛戒护鏍煎紡 + -3 浼犲叆涓嶆敮鎸佺殑鍛戒护鍊 + -4 鏀寔鐨勫懡浠ゅ硷紝浣嗘槸涓嶆敮鎸佽鍛戒护绫诲瀷 + -5 鏁版嵁闀垮害杩囬暱 + -6 鏁版嵁闀垮害杩囩煭 + -7 鏈垵濮嬪寲鎴愬姛 + -20 鍏朵粬鏈煡閿欒 + ----------鍙戦佹暟鎹繃绋嬭繑鍥為敊璇唬鐮---------------------- + 500 鍛戒护宸茬粡鍦ㄥ彂閫侀槦鍒椾腑 + 501 + 502 + 503 璁惧娌℃湁杩炴帴 + 504 鍙戦佹暟鎹垚鍔 + 505 鍙戦佹暟鎹け璐 + 506 鍙戦佹垚鍔熶絾鏄病鏈夋敹鍒板洖澶 + 507 鍙戦佸懡浠よ秴鏃 + 508 鎺ユ敹鍒版暟鎹 + ----------纭欢璁惧杩斿洖閿欒浠g爜---------------------- + 1 鍙戦佸懡浠ら敊璇 + 10 涓嶆敮鎸佺殑鍛戒护绫诲瀷 + 11 crc鏍¢獙澶辫触 + 12 鍛戒护鍙傛暟涓嶅湪鑼冨洿鍐 + 13 鍙戦佹暟鎹埌搴曞眰璁惧澶辫触 + 14 鍐檉lash澶辫触 + 15 涓嶆敮鎸佺殑鍊 + 16 浠庤澶囪鍙栨暟鎹け璐 + ** 浣溿 鑰: yl + ** 鍒涘缓鏃ユ湡: 2015-7-9 + ** 鐗 鏈細v1.0.0.0 + ** 淇敼浣滆: + ** 淇敼鏃ユ湡: + ** 淇敼鎻忚堪: + ****************************************************************/ + MSSCOMMUNICATION_API(int) FindDevice(sDeviceList *sl, sFindDeviceConfig *pfdc); + /***************************************************************** + ** 鍑 鏁 鍚: Mss_SendOneCmd + ** 鍔熻兘鎻忚堪: 鍙戦佸懡浠わ紙鍚屾鍙戦侊級 + ** 杈撱 鍏: + in pProtocol finddevice杩斿洖鐨勮澶囧垪琛ㄦ寚閽 + in buf 鍛戒护缂撳啿鍖猴紙鍘绘帀甯уご甯у熬鍜屾暟鎹婚暱搴︼級 鍖呭惈: 鍛戒护绫诲瀷 鍛戒护鍊 鍛戒护鍐呭 + in len 鍛戒护鎬婚暱搴 + in pretbuf 杩斿洖鏁版嵁缂撳啿鍖 + inout iretlen 杩斿洖鏁版嵁缂撳啿鍖洪暱搴,濡傛灉缂撳啿鍖鸿冻澶熼暱锛岃鍊艰繑鍥炴帴鏀跺埌鐨勬暟鎹暱搴︼紝濡傛灉璇ュ兼瘮鎺ユ敹鏁版嵁缂撳啿鍖哄皬锛岃繑鍥炶闀垮害鏁版嵁 + ** 杩 鍥 鍊: + 0 鎴愬姛 + ----------鏍煎紡妫鏌ヨ繑鍥為敊璇唬鐮---------------------- + -1 浼犲叆鏁版嵁闀垮害閿欒 + -2 浼犲叆涓嶆敮鎸佺殑鍛戒护鏍煎紡 + -3 浼犲叆涓嶆敮鎸佺殑鍛戒护鍊 + -4 鏀寔鐨勫懡浠ゅ硷紝浣嗘槸涓嶆敮鎸佽鍛戒护绫诲瀷 + -5 鏁版嵁闀垮害杩囬暱 + -6 鏁版嵁闀垮害杩囩煭 + -7 鏈垵濮嬪寲鎴愬姛 + -20 鍏朵粬鏈煡閿欒 + ----------鍙戦佹暟鎹繃绋嬭繑鍥為敊璇唬鐮---------------------- + 500 鍛戒护宸茬粡鍦ㄥ彂閫侀槦鍒椾腑 + 501 + 502 + 503 璁惧娌℃湁杩炴帴 + 504 鍙戦佹暟鎹垚鍔 + 505 鍙戦佹暟鎹け璐 + 506 鍙戦佹垚鍔熶絾鏄病鏈夋敹鍒板洖澶 + 507 鍙戦佸懡浠よ秴鏃 + 508 鎺ユ敹鍒版暟鎹 + ----------纭欢璁惧杩斿洖閿欒浠g爜---------------------- + 1 鍙戦佸懡浠ら敊璇 + 10 涓嶆敮鎸佺殑鍛戒护绫诲瀷 + 11 crc鏍¢獙澶辫触 + 12 鍛戒护鍙傛暟涓嶅湪鑼冨洿鍐 + 13 鍙戦佹暟鎹埌搴曞眰璁惧澶辫触 + 14 鍐檉lash澶辫触 + 15 涓嶆敮鎸佺殑鍊 + 16 浠庤澶囪鍙栨暟鎹け璐 + ** 浣溿 鑰: yl + ** 鍒涘缓鏃ユ湡: 2015-7-9 + ** 鐗 鏈細v1.0.0.0 + ** 淇敼浣滆: + ** 淇敼鏃ユ湡: + ** 淇敼鎻忚堪: + ****************************************************************/ + MSSCOMMUNICATION_API(int) Mss_SendOneCmd(void *pProtocol, void *buf, int len, void *pretbuf, int *iretlen, PMSSPROTCOLCMDCALLBACK pCmdCallBack); + /***************************************************************** + ** 鍑 鏁 鍚: Mss_SendOneCmdAsyn + ** 鍔熻兘鎻忚堪: 鍙戦佸懡浠わ紙寮傛鍙戦侊級 + ** 杈撱 鍏: + in pProtocol finddevice杩斿洖鐨勮澶囧垪琛ㄦ寚閽 + in buf 鍛戒护缂撳啿鍖猴紙鍘绘帀甯уご甯у熬鍜屾暟鎹婚暱搴︼級 鍖呭惈: 鍛戒护绫诲瀷 鍛戒护鍊 鍛戒护鍐呭 + in len 鍛戒护鎬婚暱搴 + in pretbuf 杩斿洖鏁版嵁缂撳啿鍖 + inout iretlen 杩斿洖鏁版嵁缂撳啿鍖洪暱搴,濡傛灉缂撳啿鍖鸿冻澶熼暱锛岃鍊艰繑鍥炴帴鏀跺埌鐨勬暟鎹暱搴︼紝濡傛灉璇ュ兼瘮鎺ユ敹鏁版嵁缂撳啿鍖哄皬锛岃繑鍥炶闀垮害鏁版嵁 + ** 杩 鍥 鍊: + 0 鎴愬姛 + ----------鏍煎紡妫鏌ヨ繑鍥為敊璇唬鐮---------------------- + -1 浼犲叆鏁版嵁闀垮害閿欒 + -2 浼犲叆涓嶆敮鎸佺殑鍛戒护鏍煎紡 + -3 浼犲叆涓嶆敮鎸佺殑鍛戒护鍊 + -4 鏀寔鐨勫懡浠ゅ硷紝浣嗘槸涓嶆敮鎸佽鍛戒护绫诲瀷 + -5 鏁版嵁闀垮害杩囬暱 + -6 鏁版嵁闀垮害杩囩煭 + -7 鏈垵濮嬪寲鎴愬姛 + -20 鍏朵粬鏈煡閿欒 + ----------鍙戦佹暟鎹繃绋嬭繑鍥為敊璇唬鐮---------------------- + 500 鍛戒护宸茬粡鍦ㄥ彂閫侀槦鍒椾腑 + 501 + 502 + 503 璁惧娌℃湁杩炴帴 + 504 鍙戦佹暟鎹垚鍔 + 505 鍙戦佹暟鎹け璐 + 506 鍙戦佹垚鍔熶絾鏄病鏈夋敹鍒板洖澶 + 507 鍙戦佸懡浠よ秴鏃 + 508 鎺ユ敹鍒版暟鎹 + ----------纭欢璁惧杩斿洖閿欒浠g爜---------------------- + 1 鍙戦佸懡浠ら敊璇 + 10 涓嶆敮鎸佺殑鍛戒护绫诲瀷 + 11 crc鏍¢獙澶辫触 + 12 鍛戒护鍙傛暟涓嶅湪鑼冨洿鍐 + 13 鍙戦佹暟鎹埌搴曞眰璁惧澶辫触 + 14 鍐檉lash澶辫触 + 15 涓嶆敮鎸佺殑鍊 + 16 浠庤澶囪鍙栨暟鎹け璐 + ** 浣溿 鑰: yl + ** 鍒涘缓鏃ユ湡: 2015-7-9 + ** 鐗 鏈細v1.0.0.0 + ** 淇敼浣滆: + ** 淇敼鏃ユ湡: + ** 淇敼鎻忚堪: + ****************************************************************/ + MSSCOMMUNICATION_API(int) Mss_SendOneCmdAsyn(void *pProtocol, void *buf, int len); + MSSCOMMUNICATION_API(void) Mss_OnRecvLuaDataProcessFinish(void *pProtocal, void* pHandle, void *pBuf, int iLen, void *lpBuf, int ilpLen, int iCode, const char *msg); + + MSSCOMMUNICATION_API(int) Mss_FreeDevice(void *pProtocol); + + MSSCOMMUNICATION_API(eDataTransferType) Mss_GetTransferDataType(void *pProtocol); + + // 鍒涘缓閫氫俊 + MSSCOMMUNICATION_API(void *) Mss_CreateProtocol(int type, char *config); + MSSCOMMUNICATION_API(void) Mss_DeleteProtocol(void *pProtocol); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rdlib/DeviceManage/lib/DeviceManage.lib b/3rdlib/DeviceManage/lib/DeviceManage.lib new file mode 100644 index 0000000..fe8d90b Binary files /dev/null and b/3rdlib/DeviceManage/lib/DeviceManage.lib differ diff --git a/3rdlib/DeviceManage/lib/x64/DeviceManage.lib b/3rdlib/DeviceManage/lib/x64/DeviceManage.lib new file mode 100644 index 0000000..fe8d90b Binary files /dev/null and b/3rdlib/DeviceManage/lib/x64/DeviceManage.lib differ diff --git a/3rdlib/DeviceManage/lib/x86/DeviceManage.lib b/3rdlib/DeviceManage/lib/x86/DeviceManage.lib new file mode 100644 index 0000000..2910ea5 Binary files /dev/null and b/3rdlib/DeviceManage/lib/x86/DeviceManage.lib differ diff --git a/3rdlib/QsLog/QsLog.pri b/3rdlib/QsLog/QsLog.pri new file mode 100644 index 0000000..4beba04 --- /dev/null +++ b/3rdlib/QsLog/QsLog.pri @@ -0,0 +1,14 @@ +INCLUDEPATH += \ + $${PWD}/include + +# DEFINES += QS_LOG_LINE_NUMBERS + +CONFIG(debug, debug|release){ + win32 { + LIBS += -L$${PWD}/lib -lQsLogd2 + } else { + LIBS += -L$${PWD}/lib -lQsLogd + } +} else { + LIBS += -L$${PWD}/lib -lQsLog2 +} diff --git a/3rdlib/QsLog/bin/QsLog2.dll b/3rdlib/QsLog/bin/QsLog2.dll new file mode 100644 index 0000000..e326da5 Binary files /dev/null and b/3rdlib/QsLog/bin/QsLog2.dll differ diff --git a/3rdlib/QsLog/bin/QsLogd2.dll b/3rdlib/QsLog/bin/QsLogd2.dll new file mode 100644 index 0000000..38f4472 Binary files /dev/null and b/3rdlib/QsLog/bin/QsLogd2.dll differ diff --git a/3rdlib/QsLog/include/QsLog.h b/3rdlib/QsLog/include/QsLog.h new file mode 100644 index 0000000..c1beae2 --- /dev/null +++ b/3rdlib/QsLog/include/QsLog.h @@ -0,0 +1,146 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOG_H +#define QSLOG_H + +#include "QsLogLevel.h" +#include "QsLogDest.h" +#include +#include + +#define QS_LOG_VERSION "2.0b3" + +namespace QsLogging +{ +class Destination; +class LoggerImpl; // d pointer + +class QSLOG_SHARED_OBJECT Logger +{ +public: + static Logger& instance(); + static void destroyInstance(); + static Level levelFromLogMessage(const QString& logMessage, bool* conversionSucceeded = 0); + + ~Logger(); + + //! Adds a log message destination. Don't add null destinations. + void addDestination(DestinationPtr destination); + //! Logging at a level < 'newLevel' will be ignored + void setLoggingLevel(Level newLevel); + //! The default level is INFO + Level loggingLevel() const; + //! Set to false to disable timestamp inclusion in log messages + void setIncludeTimestamp(bool e); + //! Default value is true. + bool includeTimestamp() const; + //! Set to false to disable log level inclusion in log messages + void setIncludeLogLevel(bool l); + //! Default value is true. + bool includeLogLevel() const; + + //! The helper forwards the streaming to QDebug and builds the final + //! log message. + class QSLOG_SHARED_OBJECT Helper + { + public: + explicit Helper(Level logLevel) : + level(logLevel), + qtDebug(&buffer) + {} + ~Helper(); + QDebug& stream(){ return qtDebug; } + + private: + void writeToLog(); + + Level level; + QString buffer; + QDebug qtDebug; + }; + +private: + Logger(); + Logger(const Logger&); // not available + Logger& operator=(const Logger&); // not available + + void enqueueWrite(const QString& message, Level level); + void write(const QString& message, Level level); + + LoggerImpl* d; + + friend class LogWriterRunnable; +}; + +} // end namespace + +//! Logging macros: define QS_LOG_LINE_NUMBERS to get the file and line number +//! in the log output. +#ifndef QS_LOG_LINE_NUMBERS +#define QLOG_TRACE() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::TraceLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::TraceLevel).stream() +#define QLOG_DEBUG() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::DebugLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::DebugLevel).stream() +#define QLOG_INFO() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::InfoLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::InfoLevel).stream() +#define QLOG_WARN() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::WarnLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::WarnLevel).stream() +#define QLOG_ERROR() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::ErrorLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream() +#define QLOG_FATAL() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::FatalLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::FatalLevel).stream() +#else +#define QLOG_TRACE() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::TraceLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::TraceLevel).stream() << __FILE__ << '@' << __LINE__ +#define QLOG_DEBUG() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::DebugLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::DebugLevel).stream() << __FILE__ << '@' << __LINE__ +#define QLOG_INFO() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::InfoLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::InfoLevel).stream() << __FILE__ << '@' << __LINE__ +#define QLOG_WARN() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::WarnLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::WarnLevel).stream() << __FILE__ << '@' << __LINE__ +#define QLOG_ERROR() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::ErrorLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream() << __FILE__ << '@' << __LINE__ +#define QLOG_FATAL() \ + if (QsLogging::Logger::instance().loggingLevel() > QsLogging::FatalLevel) {} \ + else QsLogging::Logger::Helper(QsLogging::FatalLevel).stream() << __FILE__ << '@' << __LINE__ +#endif + +#ifdef QS_LOG_DISABLE +#include "QsLogDisableForThisFile.h" +#endif + +#endif // QSLOG_H diff --git a/3rdlib/QsLog/include/QsLogDest.h b/3rdlib/QsLog/include/QsLogDest.h new file mode 100644 index 0000000..4ed6157 --- /dev/null +++ b/3rdlib/QsLog/include/QsLogDest.h @@ -0,0 +1,99 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOGDEST_H +#define QSLOGDEST_H + +#include "QsLogLevel.h" +#include +#include +class QString; +class QObject; + +#ifdef QSLOG_IS_SHARED_LIBRARY +#define QSLOG_SHARED_OBJECT Q_DECL_EXPORT +#elif QSLOG_IS_SHARED_LIBRARY_IMPORT +#define QSLOG_SHARED_OBJECT Q_DECL_IMPORT +#else +#define QSLOG_SHARED_OBJECT +#endif + +namespace QsLogging +{ + +class QSLOG_SHARED_OBJECT Destination +{ +public: + typedef void (*LogFunction)(const QString &message, Level level); + +public: + virtual ~Destination(); + virtual void write(const QString& message, Level level) = 0; + virtual bool isValid() = 0; // returns whether the destination was created correctly +}; +typedef QSharedPointer DestinationPtr; + + +// a series of "named" paramaters, to make the file destination creation more readable +enum LogRotationOption +{ + DisableLogRotation = 0, + EnableLogRotation = 1 +}; + +struct QSLOG_SHARED_OBJECT MaxSizeBytes +{ + MaxSizeBytes() : size(0) {} + explicit MaxSizeBytes(qint64 size_) : size(size_) {} + qint64 size; +}; + +struct QSLOG_SHARED_OBJECT MaxOldLogCount +{ + MaxOldLogCount() : count(0) {} + explicit MaxOldLogCount(int count_) : count(count_) {} + int count; +}; + + +//! Creates logging destinations/sinks. The caller shares ownership of the destinations with the logger. +//! After being added to a logger, the caller can discard the pointers. +class QSLOG_SHARED_OBJECT DestinationFactory +{ +public: + static DestinationPtr MakeFileDestination(const QString& filePath, + LogRotationOption rotation = DisableLogRotation, + const MaxSizeBytes &sizeInBytesToRotateAfter = MaxSizeBytes(), + const MaxOldLogCount &oldLogsToKeep = MaxOldLogCount()); + static DestinationPtr MakeDebugOutputDestination(); + // takes a pointer to a function + static DestinationPtr MakeFunctorDestination(Destination::LogFunction f); + // takes a QObject + signal/slot + static DestinationPtr MakeFunctorDestination(QObject *receiver, const char *member); +}; + +} // end namespace + +#endif // QSLOGDEST_H diff --git a/3rdlib/QsLog/include/QsLogDestConsole.h b/3rdlib/QsLog/include/QsLogDestConsole.h new file mode 100644 index 0000000..d422c23 --- /dev/null +++ b/3rdlib/QsLog/include/QsLogDestConsole.h @@ -0,0 +1,52 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOGDESTCONSOLE_H +#define QSLOGDESTCONSOLE_H + +#include "QsLogDest.h" + +class QString; + +class QsDebugOutput +{ +public: + static void output(const QString& a_message); +}; + +namespace QsLogging +{ + +// debugger sink +class DebugOutputDestination : public Destination +{ +public: + virtual void write(const QString& message, Level level); + virtual bool isValid(); +}; + +} + +#endif // QSLOGDESTCONSOLE_H diff --git a/3rdlib/QsLog/include/QsLogDestFile.h b/3rdlib/QsLog/include/QsLogDestFile.h new file mode 100644 index 0000000..ee7b523 --- /dev/null +++ b/3rdlib/QsLog/include/QsLogDestFile.h @@ -0,0 +1,101 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOGDESTFILE_H +#define QSLOGDESTFILE_H + +#include "QsLogDest.h" +#include +#include +#include +#include + +namespace QsLogging +{ +class RotationStrategy +{ +public: + virtual ~RotationStrategy(); + + virtual void setInitialInfo(const QFile &file) = 0; + virtual void includeMessageInCalculation(const QString &message) = 0; + virtual bool shouldRotate() = 0; + virtual void rotate() = 0; + virtual QIODevice::OpenMode recommendedOpenModeFlag() = 0; +}; + +// Never rotates file, overwrites existing file. +class NullRotationStrategy : public RotationStrategy +{ +public: + virtual void setInitialInfo(const QFile &) {} + virtual void includeMessageInCalculation(const QString &) {} + virtual bool shouldRotate() { return false; } + virtual void rotate() {} + virtual QIODevice::OpenMode recommendedOpenModeFlag() { return QIODevice::Truncate; } +}; + +// Rotates after a size is reached, keeps a number of <= 10 backups, appends to existing file. +class SizeRotationStrategy : public RotationStrategy +{ +public: + SizeRotationStrategy(); + static const int MaxBackupCount; + + virtual void setInitialInfo(const QFile &file); + virtual void includeMessageInCalculation(const QString &message); + virtual bool shouldRotate(); + virtual void rotate(); + virtual QIODevice::OpenMode recommendedOpenModeFlag(); + + void setMaximumSizeInBytes(qint64 size); + void setBackupCount(int backups); + +private: + QString mFileName; + qint64 mCurrentSizeInBytes; + qint64 mMaxSizeInBytes; + int mBackupsCount; +}; + +typedef QSharedPointer RotationStrategyPtr; + +// file message sink +class FileDestination : public Destination +{ +public: + FileDestination(const QString& filePath, RotationStrategyPtr rotationStrategy); + virtual void write(const QString& message, Level level); + virtual bool isValid(); + +private: + QFile mFile; + QTextStream mOutputStream; + QSharedPointer mRotationStrategy; +}; + +} + +#endif // QSLOGDESTFILE_H diff --git a/3rdlib/QsLog/include/QsLogDestFunctor.h b/3rdlib/QsLog/include/QsLogDestFunctor.h new file mode 100644 index 0000000..e34631f --- /dev/null +++ b/3rdlib/QsLog/include/QsLogDestFunctor.h @@ -0,0 +1,59 @@ +// Copyright (c) 2014, Razvan Petru +// Copyright (c) 2014, Omar Carey +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOGDESTFUNCTOR_H +#define QSLOGDESTFUNCTOR_H + +#include "QsLogDest.h" +#include + +namespace QsLogging +{ +// Offers various types of function-like sinks. +// This is an advanced destination type. Depending on your configuration, LogFunction might be +// called from a different thread or even a different binary. You should not access QsLog from +// inside LogFunction and should not perform any time-consuming operations. +// logMessageReady is connected through a queued connection and trace messages are not included +class FunctorDestination : public QObject, public Destination +{ + Q_OBJECT +public: + explicit FunctorDestination(LogFunction f); + FunctorDestination(QObject *receiver, const char *member); + + virtual void write(const QString &message, Level level); + virtual bool isValid(); + +protected: + // int used to avoid registering a new enum type + Q_SIGNAL void logMessageReady(const QString &message, int level); + +private: + LogFunction mLogFunction; +}; +} + +#endif // QSLOGDESTFUNCTOR_H diff --git a/3rdlib/QsLog/include/QsLogDisableForThisFile.h b/3rdlib/QsLog/include/QsLogDisableForThisFile.h new file mode 100644 index 0000000..c70af10 --- /dev/null +++ b/3rdlib/QsLog/include/QsLogDisableForThisFile.h @@ -0,0 +1,22 @@ +#ifndef QSLOGDISABLEFORTHISFILE_H +#define QSLOGDISABLEFORTHISFILE_H + +#include +// When included AFTER QsLog.h, this file will disable logging in that C++ file. When included +// before, it will lead to compiler warnings or errors about macro redefinitions. + +#undef QLOG_TRACE +#undef QLOG_DEBUG +#undef QLOG_INFO +#undef QLOG_WARN +#undef QLOG_ERROR +#undef QLOG_FATAL + +#define QLOG_TRACE() if (1) {} else qDebug() +#define QLOG_DEBUG() if (1) {} else qDebug() +#define QLOG_INFO() if (1) {} else qDebug() +#define QLOG_WARN() if (1) {} else qDebug() +#define QLOG_ERROR() if (1) {} else qDebug() +#define QLOG_FATAL() if (1) {} else qDebug() + +#endif // QSLOGDISABLEFORTHISFILE_H diff --git a/3rdlib/QsLog/include/QsLogLevel.h b/3rdlib/QsLog/include/QsLogLevel.h new file mode 100644 index 0000000..5032f27 --- /dev/null +++ b/3rdlib/QsLog/include/QsLogLevel.h @@ -0,0 +1,45 @@ +// Copyright (c) 2013, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef QSLOGLEVEL_H +#define QSLOGLEVEL_H + +namespace QsLogging +{ + +enum Level +{ + TraceLevel = 0, + DebugLevel, + InfoLevel, + WarnLevel, + ErrorLevel, + FatalLevel, + OffLevel +}; + +} + +#endif // QSLOGLEVEL_H diff --git a/3rdlib/QsLog/include/QsLogManage.h b/3rdlib/QsLog/include/QsLogManage.h new file mode 100644 index 0000000..be5c37d --- /dev/null +++ b/3rdlib/QsLog/include/QsLogManage.h @@ -0,0 +1,40 @@ +锘#ifndef QSLOG_MANAGE_H +#define QSLOG_MANAGE_H + +#include "QsLog.h" +#include +#include +#include +#include + +namespace QsLogManage { + +using namespace QsLogging; + +static void createLogger() +{ + Logger& logger = Logger::instance(); + logger.setLoggingLevel(TraceLevel); + + QString logs_dir = QDir(qApp->applicationDirPath()).filePath("logs"); + QDir logsDir(logs_dir); + if (!logsDir.exists()) { + logsDir.mkdir(logs_dir); + } + QString logFileName = QCoreApplication::applicationName() + QString(".log"); + QString logFilePath = logsDir.filePath(logFileName); + MaxSizeBytes maxSize(1024 * 1024 * 10); + MaxOldLogCount count(10); + DestinationPtr fileDestination = DestinationFactory::MakeFileDestination(logFilePath, EnableLogRotation, maxSize, count); + logger.addDestination(fileDestination); + DestinationPtr debugDestination(QsLogging::DestinationFactory::MakeDebugOutputDestination()); + logger.addDestination(debugDestination); +} + +static void destoryLogger() +{ + Logger::instance().destroyInstance(); +} + +} +#endif // QSLOG_MANAGE_H diff --git a/3rdlib/QsLog/lib/QsLog2.lib b/3rdlib/QsLog/lib/QsLog2.lib new file mode 100644 index 0000000..c5c7dd2 Binary files /dev/null and b/3rdlib/QsLog/lib/QsLog2.lib differ diff --git a/3rdlib/QsLog/lib/QsLogd2.lib b/3rdlib/QsLog/lib/QsLogd2.lib new file mode 100644 index 0000000..e4a2c4e Binary files /dev/null and b/3rdlib/QsLog/lib/QsLogd2.lib differ diff --git a/3rdlib/csv/csv.h b/3rdlib/csv/csv.h new file mode 100644 index 0000000..9a1919a --- /dev/null +++ b/3rdlib/csv/csv.h @@ -0,0 +1,1199 @@ +// Copyright: (2012-2015) Ben Strasser +// License: BSD-3 +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef CSV_H +#define CSV_H + +#include +#include +#include +#include +#include +#include +#include +#ifndef CSV_IO_NO_THREAD +#include +#include +#include +#endif +#include +#include +#include +#include +#include + +namespace io { +//////////////////////////////////////////////////////////////////////////// +// LineReader // +//////////////////////////////////////////////////////////////////////////// + +namespace error { +struct base : std::exception { + virtual void format_error_message() const = 0; + + const char *what() const noexcept override { + format_error_message(); + return error_message_buffer; + } + + mutable char error_message_buffer[2048]; +}; + +// this only affects the file name in the error message +const int max_file_name_length = 1024; + +struct with_file_name { + with_file_name() { std::memset(file_name, 0, sizeof(file_name)); } + + void set_file_name(const char *file_name) { + if (file_name != nullptr) { + // This call to strncpy has parenthesis around it + // to silence the GCC -Wstringop-truncation warning + (strncpy(this->file_name, file_name, sizeof(this->file_name))); + this->file_name[sizeof(this->file_name) - 1] = '\0'; + } else { + this->file_name[0] = '\0'; + } + } + + char file_name[max_file_name_length + 1]; +}; + +struct with_file_line { + with_file_line() { file_line = -1; } + + void set_file_line(int file_line) { this->file_line = file_line; } + + int file_line; +}; + +struct with_errno { + with_errno() { errno_value = 0; } + + void set_errno(int errno_value) { this->errno_value = errno_value; } + + int errno_value; +}; + +struct can_not_open_file : base, with_file_name, with_errno { + void format_error_message() const override { + if (errno_value != 0) + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Can not open file \"%s\" because \"%s\".", file_name, + std::strerror(errno_value)); + else + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Can not open file \"%s\".", file_name); + } +}; + +struct line_length_limit_exceeded : base, with_file_name, with_file_line { + void format_error_message() const override { + std::snprintf( + error_message_buffer, sizeof(error_message_buffer), + "Line number %d in file \"%s\" exceeds the maximum length of 2^24-1.", + file_line, file_name); + } +}; +} // namespace error + +class ByteSourceBase { +public: + virtual int read(char *buffer, int size) = 0; + virtual ~ByteSourceBase() {} +}; + +namespace detail { + +class OwningStdIOByteSourceBase : public ByteSourceBase { +public: + explicit OwningStdIOByteSourceBase(FILE *file) : file(file) { + // Tell the std library that we want to do the buffering ourself. + std::setvbuf(file, 0, _IONBF, 0); + } + + int read(char *buffer, int size) { return std::fread(buffer, 1, size, file); } + + ~OwningStdIOByteSourceBase() { std::fclose(file); } + +private: + FILE *file; +}; + +class NonOwningIStreamByteSource : public ByteSourceBase { +public: + explicit NonOwningIStreamByteSource(std::istream &in) : in(in) {} + + int read(char *buffer, int size) { + in.read(buffer, size); + return in.gcount(); + } + + ~NonOwningIStreamByteSource() {} + +private: + std::istream ∈ +}; + +class NonOwningStringByteSource : public ByteSourceBase { +public: + NonOwningStringByteSource(const char *str, long long size) + : str(str), remaining_byte_count(size) {} + + int read(char *buffer, int desired_byte_count) { + int to_copy_byte_count = desired_byte_count; + if (remaining_byte_count < to_copy_byte_count) + to_copy_byte_count = remaining_byte_count; + std::memcpy(buffer, str, to_copy_byte_count); + remaining_byte_count -= to_copy_byte_count; + str += to_copy_byte_count; + return to_copy_byte_count; + } + + ~NonOwningStringByteSource() {} + +private: + const char *str; + long long remaining_byte_count; +}; + +#ifndef CSV_IO_NO_THREAD +class AsynchronousReader { +public: + void init(std::unique_ptr arg_byte_source) { + std::unique_lock guard(lock); + byte_source = std::move(arg_byte_source); + desired_byte_count = -1; + termination_requested = false; + worker = std::thread([&] { + std::unique_lock guard(lock); + try { + for (;;) { + read_requested_condition.wait(guard, [&] { + return desired_byte_count != -1 || termination_requested; + }); + if (termination_requested) + return; + + read_byte_count = byte_source->read(buffer, desired_byte_count); + desired_byte_count = -1; + if (read_byte_count == 0) + break; + read_finished_condition.notify_one(); + } + } catch (...) { + read_error = std::current_exception(); + } + read_finished_condition.notify_one(); + }); + } + + bool is_valid() const { return byte_source != nullptr; } + + void start_read(char *arg_buffer, int arg_desired_byte_count) { + std::unique_lock guard(lock); + buffer = arg_buffer; + desired_byte_count = arg_desired_byte_count; + read_byte_count = -1; + read_requested_condition.notify_one(); + } + + int finish_read() { + std::unique_lock guard(lock); + read_finished_condition.wait( + guard, [&] { return read_byte_count != -1 || read_error; }); + if (read_error) + std::rethrow_exception(read_error); + else + return read_byte_count; + } + + ~AsynchronousReader() { + if (byte_source != nullptr) { + { + std::unique_lock guard(lock); + termination_requested = true; + } + read_requested_condition.notify_one(); + worker.join(); + } + } + +private: + std::unique_ptr byte_source; + + std::thread worker; + + bool termination_requested; + std::exception_ptr read_error; + char *buffer; + int desired_byte_count; + int read_byte_count; + + std::mutex lock; + std::condition_variable read_finished_condition; + std::condition_variable read_requested_condition; +}; +#endif + +class SynchronousReader { +public: + void init(std::unique_ptr arg_byte_source) { + byte_source = std::move(arg_byte_source); + } + + bool is_valid() const { return byte_source != nullptr; } + + void start_read(char *arg_buffer, int arg_desired_byte_count) { + buffer = arg_buffer; + desired_byte_count = arg_desired_byte_count; + } + + int finish_read() { return byte_source->read(buffer, desired_byte_count); } + +private: + std::unique_ptr byte_source; + char *buffer; + int desired_byte_count; +}; +} // namespace detail + +class LineReader { +private: + static const int block_len = 1 << 20; + std::unique_ptr buffer; // must be constructed before (and thus + // destructed after) the reader! +#ifdef CSV_IO_NO_THREAD + detail::SynchronousReader reader; +#else + detail::AsynchronousReader reader; +#endif + int data_begin; + int data_end; + + char file_name[error::max_file_name_length + 1]; + unsigned file_line; + + static std::unique_ptr open_file(const char *file_name) { + // We open the file in binary mode as it makes no difference under *nix + // and under Windows we handle \r\n newlines ourself. + FILE *file = std::fopen(file_name, "rb"); + if (file == 0) { + int x = errno; // store errno as soon as possible, doing it after + // constructor call can fail. + error::can_not_open_file err; + err.set_errno(x); + err.set_file_name(file_name); + throw err; + } + return std::unique_ptr( + new detail::OwningStdIOByteSourceBase(file)); + } + + void init(std::unique_ptr byte_source) { + file_line = 0; + + buffer = std::unique_ptr(new char[3 * block_len]); + data_begin = 0; + data_end = byte_source->read(buffer.get(), 2 * block_len); + + // Ignore UTF-8 BOM + if (data_end >= 3 && buffer[0] == '\xEF' && buffer[1] == '\xBB' && + buffer[2] == '\xBF') + data_begin = 3; + + if (data_end == 2 * block_len) { + reader.init(std::move(byte_source)); + reader.start_read(buffer.get() + 2 * block_len, block_len); + } + } + +public: + LineReader() = delete; + LineReader(const LineReader &) = delete; + LineReader &operator=(const LineReader &) = delete; + + explicit LineReader(const char *file_name) { + set_file_name(file_name); + init(open_file(file_name)); + } + + explicit LineReader(const std::string &file_name) { + set_file_name(file_name.c_str()); + init(open_file(file_name.c_str())); + } + + LineReader(const char *file_name, + std::unique_ptr byte_source) { + set_file_name(file_name); + init(std::move(byte_source)); + } + + LineReader(const std::string &file_name, + std::unique_ptr byte_source) { + set_file_name(file_name.c_str()); + init(std::move(byte_source)); + } + + LineReader(const char *file_name, const char *data_begin, + const char *data_end) { + set_file_name(file_name); + init(std::unique_ptr(new detail::NonOwningStringByteSource( + data_begin, data_end - data_begin))); + } + + LineReader(const std::string &file_name, const char *data_begin, + const char *data_end) { + set_file_name(file_name.c_str()); + init(std::unique_ptr(new detail::NonOwningStringByteSource( + data_begin, data_end - data_begin))); + } + + LineReader(const char *file_name, FILE *file) { + set_file_name(file_name); + init(std::unique_ptr( + new detail::OwningStdIOByteSourceBase(file))); + } + + LineReader(const std::string &file_name, FILE *file) { + set_file_name(file_name.c_str()); + init(std::unique_ptr( + new detail::OwningStdIOByteSourceBase(file))); + } + + LineReader(const char *file_name, std::istream &in) { + set_file_name(file_name); + init(std::unique_ptr( + new detail::NonOwningIStreamByteSource(in))); + } + + LineReader(const std::string &file_name, std::istream &in) { + set_file_name(file_name.c_str()); + init(std::unique_ptr( + new detail::NonOwningIStreamByteSource(in))); + } + + void set_file_name(const std::string &file_name) { + set_file_name(file_name.c_str()); + } + + void set_file_name(const char *file_name) { + if (file_name != nullptr) { + strncpy(this->file_name, file_name, sizeof(this->file_name) - 1); + this->file_name[sizeof(this->file_name) - 1] = '\0'; + } else { + this->file_name[0] = '\0'; + } + } + + const char *get_truncated_file_name() const { return file_name; } + + void set_file_line(unsigned file_line) { this->file_line = file_line; } + + unsigned get_file_line() const { return file_line; } + + char *next_line() { + if (data_begin == data_end) + return nullptr; + + ++file_line; + + assert(data_begin < data_end); + assert(data_end <= block_len * 2); + + if (data_begin >= block_len) { + std::memcpy(buffer.get(), buffer.get() + block_len, block_len); + data_begin -= block_len; + data_end -= block_len; + if (reader.is_valid()) { + data_end += reader.finish_read(); + std::memcpy(buffer.get() + block_len, buffer.get() + 2 * block_len, + block_len); + reader.start_read(buffer.get() + 2 * block_len, block_len); + } + } + + int line_end = data_begin; + while (line_end != data_end && buffer[line_end] != '\n') { + ++line_end; + } + + if (line_end - data_begin + 1 > block_len) { + error::line_length_limit_exceeded err; + err.set_file_name(file_name); + err.set_file_line(file_line); + throw err; + } + + if (line_end != data_end && buffer[line_end] == '\n') { + buffer[line_end] = '\0'; + } else { + // some files are missing the newline at the end of the + // last line + ++data_end; + buffer[line_end] = '\0'; + } + + // handle windows \r\n-line breaks + if (line_end != data_begin && buffer[line_end - 1] == '\r') + buffer[line_end - 1] = '\0'; + + char *ret = buffer.get() + data_begin; + data_begin = line_end + 1; + return ret; + } +}; + +//////////////////////////////////////////////////////////////////////////// +// CSV // +//////////////////////////////////////////////////////////////////////////// + +namespace error { +const int max_column_name_length = 63; +struct with_column_name { + with_column_name() { + std::memset(column_name, 0, max_column_name_length + 1); + } + + void set_column_name(const char *column_name) { + if (column_name != nullptr) { + std::strncpy(this->column_name, column_name, max_column_name_length); + this->column_name[max_column_name_length] = '\0'; + } else { + this->column_name[0] = '\0'; + } + } + + char column_name[max_column_name_length + 1]; +}; + +const int max_column_content_length = 63; + +struct with_column_content { + with_column_content() { + std::memset(column_content, 0, max_column_content_length + 1); + } + + void set_column_content(const char *column_content) { + if (column_content != nullptr) { + std::strncpy(this->column_content, column_content, + max_column_content_length); + this->column_content[max_column_content_length] = '\0'; + } else { + this->column_content[0] = '\0'; + } + } + + char column_content[max_column_content_length + 1]; +}; + +struct extra_column_in_header : base, with_file_name, with_column_name { + void format_error_message() const override { + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(Extra column "%s" in header of file "%s".)", column_name, + file_name); + } +}; + +struct missing_column_in_header : base, with_file_name, with_column_name { + void format_error_message() const override { + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(Missing column "%s" in header of file "%s".)", column_name, + file_name); + } +}; + +struct duplicated_column_in_header : base, with_file_name, with_column_name { + void format_error_message() const override { + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + R"(Duplicated column "%s" in header of file "%s".)", + column_name, file_name); + } +}; + +struct header_missing : base, with_file_name { + void format_error_message() const override { + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Header missing in file \"%s\".", file_name); + } +}; + +struct too_few_columns : base, with_file_name, with_file_line { + void format_error_message() const override { + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Too few columns in line %d in file \"%s\".", file_line, + file_name); + } +}; + +struct too_many_columns : base, with_file_name, with_file_line { + void format_error_message() const override { + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Too many columns in line %d in file \"%s\".", file_line, + file_name); + } +}; + +struct escaped_string_not_closed : base, with_file_name, with_file_line { + void format_error_message() const override { + std::snprintf(error_message_buffer, sizeof(error_message_buffer), + "Escaped string was not closed in line %d in file \"%s\".", + file_line, file_name); + } +}; + +struct integer_must_be_positive : base, + with_file_name, + with_file_line, + with_column_name, + with_column_content { + void format_error_message() const override { + std::snprintf( + error_message_buffer, sizeof(error_message_buffer), + R"(The integer "%s" must be positive or 0 in column "%s" in file "%s" in line "%d".)", + column_content, column_name, file_name, file_line); + } +}; + +struct no_digit : base, + with_file_name, + with_file_line, + with_column_name, + with_column_content { + void format_error_message() const override { + std::snprintf( + error_message_buffer, sizeof(error_message_buffer), + R"(The integer "%s" contains an invalid digit in column "%s" in file "%s" in line "%d".)", + column_content, column_name, file_name, file_line); + } +}; + +struct integer_overflow : base, + with_file_name, + with_file_line, + with_column_name, + with_column_content { + void format_error_message() const override { + std::snprintf( + error_message_buffer, sizeof(error_message_buffer), + R"(The integer "%s" overflows in column "%s" in file "%s" in line "%d".)", + column_content, column_name, file_name, file_line); + } +}; + +struct integer_underflow : base, + with_file_name, + with_file_line, + with_column_name, + with_column_content { + void format_error_message() const override { + std::snprintf( + error_message_buffer, sizeof(error_message_buffer), + R"(The integer "%s" underflows in column "%s" in file "%s" in line "%d".)", + column_content, column_name, file_name, file_line); + } +}; + +struct invalid_single_character : base, + with_file_name, + with_file_line, + with_column_name, + with_column_content { + void format_error_message() const override { + std::snprintf( + error_message_buffer, sizeof(error_message_buffer), + R"(The content "%s" of column "%s" in file "%s" in line "%d" is not a single character.)", + column_content, column_name, file_name, file_line); + } +}; +} // namespace error + +using ignore_column = unsigned int; +static const ignore_column ignore_no_column = 0; +static const ignore_column ignore_extra_column = 1; +static const ignore_column ignore_missing_column = 2; + +template struct trim_chars { +private: + constexpr static bool is_trim_char(char) { return false; } + + template + constexpr static bool is_trim_char(char c, char trim_char, + OtherTrimChars... other_trim_chars) { + return c == trim_char || is_trim_char(c, other_trim_chars...); + } + +public: + static void trim(char *&str_begin, char *&str_end) { + while (str_begin != str_end && is_trim_char(*str_begin, trim_char_list...)) + ++str_begin; + while (str_begin != str_end && + is_trim_char(*(str_end - 1), trim_char_list...)) + --str_end; + *str_end = '\0'; + } +}; + +struct no_comment { + static bool is_comment(const char *) { return false; } +}; + +template struct single_line_comment { +private: + constexpr static bool is_comment_start_char(char) { return false; } + + template + constexpr static bool + is_comment_start_char(char c, char comment_start_char, + OtherCommentStartChars... other_comment_start_chars) { + return c == comment_start_char || + is_comment_start_char(c, other_comment_start_chars...); + } + +public: + static bool is_comment(const char *line) { + return is_comment_start_char(*line, comment_start_char_list...); + } +}; + +struct empty_line_comment { + static bool is_comment(const char *line) { + if (*line == '\0') + return true; + while (*line == ' ' || *line == '\t') { + ++line; + if (*line == 0) + return true; + } + return false; + } +}; + +template +struct single_and_empty_line_comment { + static bool is_comment(const char *line) { + return single_line_comment::is_comment(line) || + empty_line_comment::is_comment(line); + } +}; + +template struct no_quote_escape { + static const char *find_next_column_end(const char *col_begin) { + while (*col_begin != sep && *col_begin != '\0') + ++col_begin; + return col_begin; + } + + static void unescape(char *&, char *&) {} +}; + +template struct double_quote_escape { + static const char *find_next_column_end(const char *col_begin) { + while (*col_begin != sep && *col_begin != '\0') + if (*col_begin != quote) + ++col_begin; + else { + do { + ++col_begin; + while (*col_begin != quote) { + if (*col_begin == '\0') + throw error::escaped_string_not_closed(); + ++col_begin; + } + ++col_begin; + } while (*col_begin == quote); + } + return col_begin; + } + + static void unescape(char *&col_begin, char *&col_end) { + if (col_end - col_begin >= 2) { + if (*col_begin == quote && *(col_end - 1) == quote) { + ++col_begin; + --col_end; + char *out = col_begin; + for (char *in = col_begin; in != col_end; ++in) { + if (*in == quote && (in + 1) != col_end && *(in + 1) == quote) { + ++in; + } + *out = *in; + ++out; + } + col_end = out; + *col_end = '\0'; + } + } + } +}; + +struct throw_on_overflow { + template static void on_overflow(T &) { + throw error::integer_overflow(); + } + + template static void on_underflow(T &) { + throw error::integer_underflow(); + } +}; + +struct ignore_overflow { + template static void on_overflow(T &) {} + + template static void on_underflow(T &) {} +}; + +struct set_to_max_on_overflow { + template static void on_overflow(T &x) { + // using (std::numeric_limits::max) instead of + // std::numeric_limits::max to make code including windows.h with its max + // macro happy + x = (std::numeric_limits::max)(); + } + + template static void on_underflow(T &x) { + x = (std::numeric_limits::min)(); + } +}; + +namespace detail { +template +void chop_next_column(char *&line, char *&col_begin, char *&col_end) { + assert(line != nullptr); + + col_begin = line; + // the col_begin + (... - col_begin) removes the constness + col_end = + col_begin + (quote_policy::find_next_column_end(col_begin) - col_begin); + + if (*col_end == '\0') { + line = nullptr; + } else { + *col_end = '\0'; + line = col_end + 1; + } +} + +template +void parse_line(char *line, char **sorted_col, + const std::vector &col_order) { + for (int i : col_order) { + if (line == nullptr) + throw ::io::error::too_few_columns(); + char *col_begin, *col_end; + chop_next_column(line, col_begin, col_end); + + if (i != -1) { + trim_policy::trim(col_begin, col_end); + quote_policy::unescape(col_begin, col_end); + + sorted_col[i] = col_begin; + } + } + if (line != nullptr) + throw ::io::error::too_many_columns(); +} + +template +void parse_header_line(char *line, std::vector &col_order, + const std::string *col_name, + ignore_column ignore_policy) { + col_order.clear(); + + bool found[column_count]; + std::fill(found, found + column_count, false); + while (line) { + char *col_begin, *col_end; + chop_next_column(line, col_begin, col_end); + + trim_policy::trim(col_begin, col_end); + quote_policy::unescape(col_begin, col_end); + + for (unsigned i = 0; i < column_count; ++i) + if (col_begin == col_name[i]) { + if (found[i]) { + error::duplicated_column_in_header err; + err.set_column_name(col_begin); + throw err; + } + found[i] = true; + col_order.push_back(i); + col_begin = 0; + break; + } + if (col_begin) { + if (ignore_policy & ::io::ignore_extra_column) + col_order.push_back(-1); + else { + error::extra_column_in_header err; + err.set_column_name(col_begin); + throw err; + } + } + } + if (!(ignore_policy & ::io::ignore_missing_column)) { + for (unsigned i = 0; i < column_count; ++i) { + if (!found[i]) { + error::missing_column_in_header err; + err.set_column_name(col_name[i].c_str()); + throw err; + } + } + } +} + +template void parse(char *col, char &x) { + if (!*col) + throw error::invalid_single_character(); + x = *col; + ++col; + if (*col) + throw error::invalid_single_character(); +} + +template void parse(char *col, std::string &x) { + x = col; +} + +template void parse(char *col, const char *&x) { + x = col; +} + +template void parse(char *col, char *&x) { x = col; } + +template +void parse_unsigned_integer(const char *col, T &x) { + x = 0; + while (*col != '\0') { + if ('0' <= *col && *col <= '9') { + T y = *col - '0'; + if (x > ((std::numeric_limits::max)() - y) / 10) { + overflow_policy::on_overflow(x); + return; + } + x = 10 * x + y; + } else + throw error::no_digit(); + ++col; + } +} + +template void parse(char *col, unsigned char &x) { + parse_unsigned_integer(col, x); +} +template void parse(char *col, unsigned short &x) { + parse_unsigned_integer(col, x); +} +template void parse(char *col, unsigned int &x) { + parse_unsigned_integer(col, x); +} +template void parse(char *col, unsigned long &x) { + parse_unsigned_integer(col, x); +} +template void parse(char *col, unsigned long long &x) { + parse_unsigned_integer(col, x); +} + +template +void parse_signed_integer(const char *col, T &x) { + if (*col == '-') { + ++col; + + x = 0; + while (*col != '\0') { + if ('0' <= *col && *col <= '9') { + T y = *col - '0'; + if (x < ((std::numeric_limits::min)() + y) / 10) { + overflow_policy::on_underflow(x); + return; + } + x = 10 * x - y; + } else + throw error::no_digit(); + ++col; + } + return; + } else if (*col == '+') + ++col; + parse_unsigned_integer(col, x); +} + +template void parse(char *col, signed char &x) { + parse_signed_integer(col, x); +} +template void parse(char *col, signed short &x) { + parse_signed_integer(col, x); +} +template void parse(char *col, signed int &x) { + parse_signed_integer(col, x); +} +template void parse(char *col, signed long &x) { + parse_signed_integer(col, x); +} +template void parse(char *col, signed long long &x) { + parse_signed_integer(col, x); +} + +template void parse_float(const char *col, T &x) { + bool is_neg = false; + if (*col == '-') { + is_neg = true; + ++col; + } else if (*col == '+') + ++col; + + x = 0; + while ('0' <= *col && *col <= '9') { + int y = *col - '0'; + x *= 10; + x += y; + ++col; + } + + if (*col == '.' || *col == ',') { + ++col; + T pos = 1; + while ('0' <= *col && *col <= '9') { + pos /= 10; + int y = *col - '0'; + ++col; + x += y * pos; + } + } + + if (*col == 'e' || *col == 'E') { + ++col; + int e; + + parse_signed_integer(col, e); + + if (e != 0) { + T base; + if (e < 0) { + base = T(0.1); + e = -e; + } else { + base = T(10); + } + + while (e != 1) { + if ((e & 1) == 0) { + base = base * base; + e >>= 1; + } else { + x *= base; + --e; + } + } + x *= base; + } + } else { + if (*col != '\0') + throw error::no_digit(); + } + + if (is_neg) + x = -x; +} + +template void parse(char *col, float &x) { + parse_float(col, x); +} +template void parse(char *col, double &x) { + parse_float(col, x); +} +template void parse(char *col, long double &x) { + parse_float(col, x); +} + +template void parse(char *col, T &x) { + // Mute unused variable compiler warning + (void)col; + (void)x; + // GCC evaluates "false" when reading the template and + // "sizeof(T)!=sizeof(T)" only when instantiating it. This is why + // this strange construct is used. + static_assert(sizeof(T) != sizeof(T), + "Can not parse this type. Only builtin integrals, floats, " + "char, char*, const char* and std::string are supported"); +} + +} // namespace detail + +template , + class quote_policy = no_quote_escape<','>, + class overflow_policy = throw_on_overflow, + class comment_policy = no_comment> +class CSVReader { +private: + LineReader in; + + char *row[column_count]; + std::string column_names[column_count]; + + std::vector col_order; + + template + void set_column_names(std::string s, ColNames... cols) { + column_names[column_count - sizeof...(ColNames) - 1] = std::move(s); + set_column_names(std::forward(cols)...); + } + + void set_column_names() {} + +public: + CSVReader() = delete; + CSVReader(const CSVReader &) = delete; + CSVReader &operator=(const CSVReader &); + + template + explicit CSVReader(Args &&... args) : in(std::forward(args)...) { + std::fill(row, row + column_count, nullptr); + col_order.resize(column_count); + for (unsigned i = 0; i < column_count; ++i) + col_order[i] = i; + for (unsigned i = 1; i <= column_count; ++i) + column_names[i - 1] = "col" + std::to_string(i); + } + + char *next_line() { return in.next_line(); } + + template + void read_header(ignore_column ignore_policy, ColNames... cols) { + static_assert(sizeof...(ColNames) >= column_count, + "not enough column names specified"); + static_assert(sizeof...(ColNames) <= column_count, + "too many column names specified"); + try { + set_column_names(std::forward(cols)...); + + char *line; + do { + line = in.next_line(); + if (!line) + throw error::header_missing(); + } while (comment_policy::is_comment(line)); + + detail::parse_header_line( + line, col_order, column_names, ignore_policy); + } catch (error::with_file_name &err) { + err.set_file_name(in.get_truncated_file_name()); + throw; + } + } + + template void set_header(ColNames... cols) { + static_assert(sizeof...(ColNames) >= column_count, + "not enough column names specified"); + static_assert(sizeof...(ColNames) <= column_count, + "too many column names specified"); + set_column_names(std::forward(cols)...); + std::fill(row, row + column_count, nullptr); + col_order.resize(column_count); + for (unsigned i = 0; i < column_count; ++i) + col_order[i] = i; + } + + bool has_column(const std::string &name) const { + return col_order.end() != + std::find(col_order.begin(), col_order.end(), + std::find(std::begin(column_names), std::end(column_names), + name) - + std::begin(column_names)); + } + + void set_file_name(const std::string &file_name) { + in.set_file_name(file_name); + } + + void set_file_name(const char *file_name) { in.set_file_name(file_name); } + + const char *get_truncated_file_name() const { + return in.get_truncated_file_name(); + } + + void set_file_line(unsigned file_line) { in.set_file_line(file_line); } + + unsigned get_file_line() const { return in.get_file_line(); } + +private: + void parse_helper(std::size_t) {} + + template + void parse_helper(std::size_t r, T &t, ColType &... cols) { + if (row[r]) { + try { + try { + ::io::detail::parse(row[r], t); + } catch (error::with_column_content &err) { + err.set_column_content(row[r]); + throw; + } + } catch (error::with_column_name &err) { + err.set_column_name(column_names[r].c_str()); + throw; + } + } + parse_helper(r + 1, cols...); + } + +public: + template bool read_row(ColType &... cols) { + static_assert(sizeof...(ColType) >= column_count, + "not enough columns specified"); + static_assert(sizeof...(ColType) <= column_count, + "too many columns specified"); + try { + try { + + char *line; + do { + line = in.next_line(); + if (!line) + return false; + } while (comment_policy::is_comment(line)); + + detail::parse_line(line, row, col_order); + + parse_helper(0, cols...); + } catch (error::with_file_name &err) { + err.set_file_name(in.get_truncated_file_name()); + throw; + } + } catch (error::with_file_line &err) { + err.set_file_line(in.get_file_line()); + throw; + } + + return true; + } +}; +} // namespace io +#endif diff --git a/3rdlib/csv/csv.hpp b/3rdlib/csv/csv.hpp new file mode 100644 index 0000000..9cebfc2 --- /dev/null +++ b/3rdlib/csv/csv.hpp @@ -0,0 +1,8486 @@ +#pragma once +/* +CSV for C++, version 2.1.3 +https://github.com/vincentlaucsb/csv-parser + +MIT License + +Copyright (c) 2017-2020 Vincent La + +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. +*/ + +#ifndef CSV_HPP +#define CSV_HPP + +/** @file + * @brief Defines functionality needed for basic CSV parsing + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Copyright 2017 https://github.com/mandreyel + * + * 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. + */ + +#ifndef MIO_MMAP_HEADER +#define MIO_MMAP_HEADER + +// #include "mio/page.hpp" +/* Copyright 2017 https://github.com/mandreyel + * + * 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. + */ + +#ifndef MIO_PAGE_HEADER +#define MIO_PAGE_HEADER + +#ifdef _WIN32 +# include +#else +# include +#endif + +namespace mio { + +/** + * This is used by `basic_mmap` to determine whether to create a read-only or + * a read-write memory mapping. + */ +enum class access_mode +{ + read, + write +}; + +/** + * Determines the operating system's page allocation granularity. + * + * On the first call to this function, it invokes the operating system specific syscall + * to determine the page size, caches the value, and returns it. Any subsequent call to + * this function serves the cached value, so no further syscalls are made. + */ +inline size_t page_size() +{ + static const size_t page_size = [] + { +#ifdef _WIN32 + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + return SystemInfo.dwAllocationGranularity; +#else + return sysconf(_SC_PAGE_SIZE); +#endif + }(); + return page_size; +} + +/** + * Alligns `offset` to the operating's system page size such that it subtracts the + * difference until the nearest page boundary before `offset`, or does nothing if + * `offset` is already page aligned. + */ +inline size_t make_offset_page_aligned(size_t offset) noexcept +{ + const size_t page_size_ = page_size(); + // Use integer division to round down to the nearest page alignment. + return offset / page_size_ * page_size_; +} + +} // namespace mio + +#endif // MIO_PAGE_HEADER + + +#include +#include +#include +#include + +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif // WIN32_LEAN_AND_MEAN +# include +#else // ifdef _WIN32 +# define INVALID_HANDLE_VALUE -1 +#endif // ifdef _WIN32 + +namespace mio { + +// This value may be provided as the `length` parameter to the constructor or +// `map`, in which case a memory mapping of the entire file is created. +enum { map_entire_file = 0 }; + +#ifdef _WIN32 +using file_handle_type = HANDLE; +#else +using file_handle_type = int; +#endif + +// This value represents an invalid file handle type. This can be used to +// determine whether `basic_mmap::file_handle` is valid, for example. +const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE; + +template +struct basic_mmap +{ + using value_type = ByteT; + using size_type = size_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using difference_type = std::ptrdiff_t; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using iterator_category = std::random_access_iterator_tag; + using handle_type = file_handle_type; + + static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char."); + +private: + // Points to the first requested byte, and not to the actual start of the mapping. + pointer data_ = nullptr; + + // Length--in bytes--requested by user (which may not be the length of the + // full mapping) and the length of the full mapping. + size_type length_ = 0; + size_type mapped_length_ = 0; + + // Letting user map a file using both an existing file handle and a path + // introcudes some complexity (see `is_handle_internal_`). + // On POSIX, we only need a file handle to create a mapping, while on + // Windows systems the file handle is necessary to retrieve a file mapping + // handle, but any subsequent operations on the mapped region must be done + // through the latter. + handle_type file_handle_ = INVALID_HANDLE_VALUE; +#ifdef _WIN32 + handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE; +#endif + + // Letting user map a file using both an existing file handle and a path + // introcudes some complexity in that we must not close the file handle if + // user provided it, but we must close it if we obtained it using the + // provided path. For this reason, this flag is used to determine when to + // close `file_handle_`. + bool is_handle_internal_; + +public: + /** + * The default constructed mmap object is in a non-mapped state, that is, + * any operation that attempts to access nonexistent underlying data will + * result in undefined behaviour/segmentation faults. + */ + basic_mmap() = default; + +#ifdef __cpp_exceptions + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + template + basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(path, offset, length, error); + if(error) { throw std::system_error(error); } + } + + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(handle, offset, length, error); + if(error) { throw std::system_error(error); } + } +#endif // __cpp_exceptions + + /** + * `basic_mmap` has single-ownership semantics, so transferring ownership + * may only be accomplished by moving the object. + */ + basic_mmap(const basic_mmap&) = delete; + basic_mmap(basic_mmap&&); + basic_mmap& operator=(const basic_mmap&) = delete; + basic_mmap& operator=(basic_mmap&&); + + /** + * If this is a read-write mapping, the destructor invokes sync. Regardless + * of the access mode, unmap is invoked as a final step. + */ + ~basic_mmap(); + + /** + * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, + * however, a mapped region of a file gets its own handle, which is returned by + * 'mapping_handle'. + */ + handle_type file_handle() const noexcept { return file_handle_; } + handle_type mapping_handle() const noexcept; + + /** Returns whether a valid memory mapping has been created. */ + bool is_open() const noexcept { return file_handle_ != invalid_handle; } + + /** + * Returns true if no mapping was established, that is, conceptually the + * same as though the length that was mapped was 0. This function is + * provided so that this class has Container semantics. + */ + bool empty() const noexcept { return length() == 0; } + + /** Returns true if a mapping was established. */ + bool is_mapped() const noexcept; + + /** + * `size` and `length` both return the logical length, i.e. the number of bytes + * user requested to be mapped, while `mapped_length` returns the actual number of + * bytes that were mapped which is a multiple of the underlying operating system's + * page allocation granularity. + */ + size_type size() const noexcept { return length(); } + size_type length() const noexcept { return length_; } + size_type mapped_length() const noexcept { return mapped_length_; } + + /** Returns the offset relative to the start of the mapping. */ + size_type mapping_offset() const noexcept + { + return mapped_length_ - length_; + } + + /** + * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping + * exists. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > pointer data() noexcept { return data_; } + const_pointer data() const noexcept { return data_; } + + /** + * Returns an iterator to the first requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator begin() noexcept { return data(); } + const_iterator begin() const noexcept { return data(); } + const_iterator cbegin() const noexcept { return data(); } + + /** + * Returns an iterator one past the last requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator end() noexcept { return data() + length(); } + const_iterator end() const noexcept { return data() + length(); } + const_iterator cend() const noexcept { return data() + length(); } + + /** + * Returns a reverse iterator to the last memory mapped byte, if a valid + * memory mapping exists, otherwise this function call is undefined + * behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const noexcept + { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const noexcept + { return const_reverse_iterator(end()); } + + /** + * Returns a reverse iterator past the first mapped byte, if a valid memory + * mapping exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + const_reverse_iterator rend() const noexcept + { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const noexcept + { return const_reverse_iterator(begin()); } + + /** + * Returns a reference to the `i`th byte from the first requested byte (as returned + * by `data`). If this is invoked when no valid memory mapping has been created + * prior to this call, undefined behaviour ensues. + */ + reference operator[](const size_type i) noexcept { return data_[i]; } + const_reference operator[](const size_type i) const noexcept { return data_[i]; } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + template + void map(const String& path, const size_type offset, + const size_type length, std::error_code& error); + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * The entire file is mapped. + */ + template + void map(const String& path, std::error_code& error) + { + map(path, 0, map_entire_file, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is + * unsuccesful, the reason is reported via `error` and the object remains in + * a state as if this function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + void map(const handle_type handle, const size_type offset, + const size_type length, std::error_code& error); + + /** + * Establishes a memory mapping with AccessMode. If the mapping is + * unsuccesful, the reason is reported via `error` and the object remains in + * a state as if this function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * The entire file is mapped. + */ + void map(const handle_type handle, std::error_code& error) + { + map(handle, 0, map_entire_file, error); + } + + /** + * If a valid memory mapping has been created prior to this call, this call + * instructs the kernel to unmap the memory region and disassociate this object + * from the file. + * + * The file handle associated with the file that is mapped is only closed if the + * mapping was created using a file path. If, on the other hand, an existing + * file handle was used to create the mapping, the file handle is not closed. + */ + void unmap(); + + void swap(basic_mmap& other); + + /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ + template + typename std::enable_if::type + sync(std::error_code& error); + + /** + * All operators compare the address of the first byte and size of the two mapped + * regions. + */ + +private: + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > pointer get_mapping_start() noexcept + { + return !data() ? nullptr : data() - mapping_offset(); + } + + const_pointer get_mapping_start() const noexcept + { + return !data() ? nullptr : data() - mapping_offset(); + } + + /** + * The destructor syncs changes to disk if `AccessMode` is `write`, but not + * if it's `read`, but since the destructor cannot be templated, we need to + * do SFINAE in a dedicated function, where one syncs and the other is a noop. + */ + template + typename std::enable_if::type + conditional_sync(); + template + typename std::enable_if::type conditional_sync(); +}; + +template +bool operator==(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator!=(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator<(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator<=(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator>(const basic_mmap& a, + const basic_mmap& b); + +template +bool operator>=(const basic_mmap& a, + const basic_mmap& b); + +/** + * This is the basis for all read-only mmap objects and should be preferred over + * directly using `basic_mmap`. + */ +template +using basic_mmap_source = basic_mmap; + +/** + * This is the basis for all read-write mmap objects and should be preferred over + * directly using `basic_mmap`. + */ +template +using basic_mmap_sink = basic_mmap; + +/** + * These aliases cover the most common use cases, both representing a raw byte stream + * (either with a char or an unsigned char/uint8_t). + */ +using mmap_source = basic_mmap_source; +using ummap_source = basic_mmap_source; + +using mmap_sink = basic_mmap_sink; +using ummap_sink = basic_mmap_sink; + +/** + * Convenience factory method that constructs a mapping for any `basic_mmap` or + * `basic_mmap` type. + */ +template< + typename MMap, + typename MappingToken +> MMap make_mmap(const MappingToken& token, + int64_t offset, int64_t length, std::error_code& error) +{ + MMap mmap; + mmap.map(token, offset, length, error); + return mmap; +} + +/** + * Convenience factory method. + * + * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, + * `std::filesystem::path`, `std::vector`, or similar), or a + * `mmap_source::handle_type`. + */ +template +mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset, + mmap_source::size_type length, std::error_code& error) +{ + return make_mmap(token, offset, length, error); +} + +template +mmap_source make_mmap_source(const MappingToken& token, std::error_code& error) +{ + return make_mmap_source(token, 0, map_entire_file, error); +} + +/** + * Convenience factory method. + * + * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`, + * `std::filesystem::path`, `std::vector`, or similar), or a + * `mmap_sink::handle_type`. + */ +template +mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset, + mmap_sink::size_type length, std::error_code& error) +{ + return make_mmap(token, offset, length, error); +} + +template +mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error) +{ + return make_mmap_sink(token, 0, map_entire_file, error); +} + +} // namespace mio + +// #include "detail/mmap.ipp" +/* Copyright 2017 https://github.com/mandreyel + * + * 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. + */ + +#ifndef MIO_BASIC_MMAP_IMPL +#define MIO_BASIC_MMAP_IMPL + +// #include "mio/mmap.hpp" + +// #include "mio/page.hpp" + +// #include "mio/detail/string_util.hpp" +/* Copyright 2017 https://github.com/mandreyel + * + * 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. + */ + +#ifndef MIO_STRING_UTIL_HEADER +#define MIO_STRING_UTIL_HEADER + +#include + +namespace mio { +namespace detail { + +template< + typename S, + typename C = typename std::decay::type, + typename = decltype(std::declval().data()), + typename = typename std::enable_if< + std::is_same::value +#ifdef _WIN32 + || std::is_same::value +#endif + >::type +> struct char_type_helper { + using type = typename C::value_type; +}; + +template +struct char_type { + using type = typename char_type_helper::type; +}; + +// TODO: can we avoid this brute force approach? +template<> +struct char_type { + using type = char; +}; + +template<> +struct char_type { + using type = char; +}; + +template +struct char_type { + using type = char; +}; + +template +struct char_type { + using type = char; +}; + +#ifdef _WIN32 +template<> +struct char_type { + using type = wchar_t; +}; + +template<> +struct char_type { + using type = wchar_t; +}; + +template +struct char_type { + using type = wchar_t; +}; + +template +struct char_type { + using type = wchar_t; +}; +#endif // _WIN32 + +template +struct is_c_str_helper +{ + static constexpr bool value = std::is_same< + CharT*, + // TODO: I'm so sorry for this... Can this be made cleaner? + typename std::add_pointer< + typename std::remove_cv< + typename std::remove_pointer< + typename std::decay< + S + >::type + >::type + >::type + >::type + >::value; +}; + +template +struct is_c_str +{ + static constexpr bool value = is_c_str_helper::value; +}; + +#ifdef _WIN32 +template +struct is_c_wstr +{ + static constexpr bool value = is_c_str_helper::value; +}; +#endif // _WIN32 + +template +struct is_c_str_or_c_wstr +{ + static constexpr bool value = is_c_str::value +#ifdef _WIN32 + || is_c_wstr::value +#endif + ; +}; + +template< + typename String, + typename = decltype(std::declval().data()), + typename = typename std::enable_if::value>::type +> const typename char_type::type* c_str(const String& path) +{ + return path.data(); +} + +template< + typename String, + typename = decltype(std::declval().empty()), + typename = typename std::enable_if::value>::type +> bool empty(const String& path) +{ + return path.empty(); +} + +template< + typename String, + typename = typename std::enable_if::value>::type +> const typename char_type::type* c_str(String path) +{ + return path; +} + +template< + typename String, + typename = typename std::enable_if::value>::type +> bool empty(String path) +{ + return !path || (*path == 0); +} + +} // namespace detail +} // namespace mio + +#endif // MIO_STRING_UTIL_HEADER + + +#include + +#ifndef _WIN32 +# include +# include +# include +# include +#endif + +namespace mio { +namespace detail { + +#ifdef _WIN32 +namespace win { + +/** Returns the 4 upper bytes of an 8-byte integer. */ +inline DWORD int64_high(int64_t n) noexcept +{ + return n >> 32; +} + +/** Returns the 4 lower bytes of an 8-byte integer. */ +inline DWORD int64_low(int64_t n) noexcept +{ + return n & 0xffffffff; +} + +template< + typename String, + typename = typename std::enable_if< + std::is_same::type, char>::value + >::type +> file_handle_type open_file_helper(const String& path, const access_mode mode) +{ + return ::CreateFileA(c_str(path), + mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); +} + +template +typename std::enable_if< + std::is_same::type, wchar_t>::value, + file_handle_type +>::type open_file_helper(const String& path, const access_mode mode) +{ + return ::CreateFileW(c_str(path), + mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + 0); +} + +} // win +#endif // _WIN32 + +/** + * Returns the last platform specific system error (errno on POSIX and + * GetLastError on Win) as a `std::error_code`. + */ +inline std::error_code last_error() noexcept +{ + std::error_code error; +#ifdef _WIN32 + error.assign(GetLastError(), std::system_category()); +#else + error.assign(errno, std::system_category()); +#endif + return error; +} + +template +file_handle_type open_file(const String& path, const access_mode mode, + std::error_code& error) +{ + error.clear(); + if(detail::empty(path)) + { + error = std::make_error_code(std::errc::invalid_argument); + return invalid_handle; + } +#ifdef _WIN32 + const auto handle = win::open_file_helper(path, mode); +#else // POSIX + const auto handle = ::open(c_str(path), + mode == access_mode::read ? O_RDONLY : O_RDWR); +#endif + if(handle == invalid_handle) + { + error = detail::last_error(); + } + return handle; +} + +inline size_t query_file_size(file_handle_type handle, std::error_code& error) +{ + error.clear(); +#ifdef _WIN32 + LARGE_INTEGER file_size; + if(::GetFileSizeEx(handle, &file_size) == 0) + { + error = detail::last_error(); + return 0; + } + return static_cast(file_size.QuadPart); +#else // POSIX + struct stat sbuf; + if(::fstat(handle, &sbuf) == -1) + { + error = detail::last_error(); + return 0; + } + return sbuf.st_size; +#endif +} + +struct mmap_context +{ + char* data; + int64_t length; + int64_t mapped_length; +#ifdef _WIN32 + file_handle_type file_mapping_handle; +#endif +}; + +inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset, + const int64_t length, const access_mode mode, std::error_code& error) +{ + const int64_t aligned_offset = make_offset_page_aligned(offset); + const int64_t length_to_map = offset - aligned_offset + length; +#ifdef _WIN32 + const int64_t max_file_size = offset + length; + const auto file_mapping_handle = ::CreateFileMapping( + file_handle, + 0, + mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE, + win::int64_high(max_file_size), + win::int64_low(max_file_size), + 0); + if(file_mapping_handle == invalid_handle) + { + error = detail::last_error(); + return {}; + } + char* mapping_start = static_cast(::MapViewOfFile( + file_mapping_handle, + mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE, + win::int64_high(aligned_offset), + win::int64_low(aligned_offset), + length_to_map)); + if(mapping_start == nullptr) + { + // Close file handle if mapping it failed. + ::CloseHandle(file_mapping_handle); + error = detail::last_error(); + return {}; + } +#else // POSIX + char* mapping_start = static_cast(::mmap( + 0, // Don't give hint as to where to map. + length_to_map, + mode == access_mode::read ? PROT_READ : PROT_WRITE, + MAP_SHARED, + file_handle, + aligned_offset)); + if(mapping_start == MAP_FAILED) + { + error = detail::last_error(); + return {}; + } +#endif + mmap_context ctx; + ctx.data = mapping_start + offset - aligned_offset; + ctx.length = length; + ctx.mapped_length = length_to_map; +#ifdef _WIN32 + ctx.file_mapping_handle = file_mapping_handle; +#endif + return ctx; +} + +} // namespace detail + +// -- basic_mmap -- + +template +basic_mmap::~basic_mmap() +{ + conditional_sync(); + unmap(); +} + +template +basic_mmap::basic_mmap(basic_mmap&& other) + : data_(std::move(other.data_)) + , length_(std::move(other.length_)) + , mapped_length_(std::move(other.mapped_length_)) + , file_handle_(std::move(other.file_handle_)) +#ifdef _WIN32 + , file_mapping_handle_(std::move(other.file_mapping_handle_)) +#endif + , is_handle_internal_(std::move(other.is_handle_internal_)) +{ + other.data_ = nullptr; + other.length_ = other.mapped_length_ = 0; + other.file_handle_ = invalid_handle; +#ifdef _WIN32 + other.file_mapping_handle_ = invalid_handle; +#endif +} + +template +basic_mmap& +basic_mmap::operator=(basic_mmap&& other) +{ + if(this != &other) + { + // First the existing mapping needs to be removed. + unmap(); + data_ = std::move(other.data_); + length_ = std::move(other.length_); + mapped_length_ = std::move(other.mapped_length_); + file_handle_ = std::move(other.file_handle_); +#ifdef _WIN32 + file_mapping_handle_ = std::move(other.file_mapping_handle_); +#endif + is_handle_internal_ = std::move(other.is_handle_internal_); + + // The moved from basic_mmap's fields need to be reset, because + // otherwise other's destructor will unmap the same mapping that was + // just moved into this. + other.data_ = nullptr; + other.length_ = other.mapped_length_ = 0; + other.file_handle_ = invalid_handle; +#ifdef _WIN32 + other.file_mapping_handle_ = invalid_handle; +#endif + other.is_handle_internal_ = false; + } + return *this; +} + +template +typename basic_mmap::handle_type +basic_mmap::mapping_handle() const noexcept +{ +#ifdef _WIN32 + return file_mapping_handle_; +#else + return file_handle_; +#endif +} + +template +template +void basic_mmap::map(const String& path, const size_type offset, + const size_type length, std::error_code& error) +{ + error.clear(); + if(detail::empty(path)) + { + error = std::make_error_code(std::errc::invalid_argument); + return; + } + const auto handle = detail::open_file(path, AccessMode, error); + if(error) + { + return; + } + + map(handle, offset, length, error); + // This MUST be after the call to map, as that sets this to true. + if(!error) + { + is_handle_internal_ = true; + } +} + +template +void basic_mmap::map(const handle_type handle, + const size_type offset, const size_type length, std::error_code& error) +{ + error.clear(); + if(handle == invalid_handle) + { + error = std::make_error_code(std::errc::bad_file_descriptor); + return; + } + + const auto file_size = detail::query_file_size(handle, error); + if(error) + { + return; + } + + if(offset + length > file_size) + { + error = std::make_error_code(std::errc::invalid_argument); + return; + } + + const auto ctx = detail::memory_map(handle, offset, + length == map_entire_file ? (file_size - offset) : length, + AccessMode, error); + if(!error) + { + // We must unmap the previous mapping that may have existed prior to this call. + // Note that this must only be invoked after a new mapping has been created in + // order to provide the strong guarantee that, should the new mapping fail, the + // `map` function leaves this instance in a state as though the function had + // never been invoked. + unmap(); + file_handle_ = handle; + is_handle_internal_ = false; + data_ = reinterpret_cast(ctx.data); + length_ = ctx.length; + mapped_length_ = ctx.mapped_length; +#ifdef _WIN32 + file_mapping_handle_ = ctx.file_mapping_handle; +#endif + } +} + +template +template +typename std::enable_if::type +basic_mmap::sync(std::error_code& error) +{ + error.clear(); + if(!is_open()) + { + error = std::make_error_code(std::errc::bad_file_descriptor); + return; + } + + if(data()) + { +#ifdef _WIN32 + if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0 + || ::FlushFileBuffers(file_handle_) == 0) +#else // POSIX + if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0) +#endif + { + error = detail::last_error(); + return; + } + } +#ifdef _WIN32 + if(::FlushFileBuffers(file_handle_) == 0) + { + error = detail::last_error(); + } +#endif +} + +template +void basic_mmap::unmap() +{ + if(!is_open()) { return; } + // TODO do we care about errors here? +#ifdef _WIN32 + if(is_mapped()) + { + ::UnmapViewOfFile(get_mapping_start()); + ::CloseHandle(file_mapping_handle_); + } +#else // POSIX + if(data_) { ::munmap(const_cast(get_mapping_start()), mapped_length_); } +#endif + + // If `file_handle_` was obtained by our opening it (when map is called with + // a path, rather than an existing file handle), we need to close it, + // otherwise it must not be closed as it may still be used outside this + // instance. + if(is_handle_internal_) + { +#ifdef _WIN32 + ::CloseHandle(file_handle_); +#else // POSIX + ::close(file_handle_); +#endif + } + + // Reset fields to their default values. + data_ = nullptr; + length_ = mapped_length_ = 0; + file_handle_ = invalid_handle; +#ifdef _WIN32 + file_mapping_handle_ = invalid_handle; +#endif +} + +template +bool basic_mmap::is_mapped() const noexcept +{ +#ifdef _WIN32 + return file_mapping_handle_ != invalid_handle; +#else // POSIX + return is_open(); +#endif +} + +template +void basic_mmap::swap(basic_mmap& other) +{ + if(this != &other) + { + using std::swap; + swap(data_, other.data_); + swap(file_handle_, other.file_handle_); +#ifdef _WIN32 + swap(file_mapping_handle_, other.file_mapping_handle_); +#endif + swap(length_, other.length_); + swap(mapped_length_, other.mapped_length_); + swap(is_handle_internal_, other.is_handle_internal_); + } +} + +template +template +typename std::enable_if::type +basic_mmap::conditional_sync() +{ + // This is invoked from the destructor, so not much we can do about + // failures here. + std::error_code ec; + sync(ec); +} + +template +template +typename std::enable_if::type +basic_mmap::conditional_sync() +{ + // noop +} + +template +bool operator==(const basic_mmap& a, + const basic_mmap& b) +{ + return a.data() == b.data() + && a.size() == b.size(); +} + +template +bool operator!=(const basic_mmap& a, + const basic_mmap& b) +{ + return !(a == b); +} + +template +bool operator<(const basic_mmap& a, + const basic_mmap& b) +{ + if(a.data() == b.data()) { return a.size() < b.size(); } + return a.data() < b.data(); +} + +template +bool operator<=(const basic_mmap& a, + const basic_mmap& b) +{ + return !(a > b); +} + +template +bool operator>(const basic_mmap& a, + const basic_mmap& b) +{ + if(a.data() == b.data()) { return a.size() > b.size(); } + return a.data() > b.data(); +} + +template +bool operator>=(const basic_mmap& a, + const basic_mmap& b) +{ + return !(a < b); +} + +} // namespace mio + +#endif // MIO_BASIC_MMAP_IMPL + + +#endif // MIO_MMAP_HEADER +/* Copyright 2017 https://github.com/mandreyel + * + * 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. + */ + +#ifndef MIO_PAGE_HEADER +#define MIO_PAGE_HEADER + +#ifdef _WIN32 +# include +#else +# include +#endif + +namespace mio { + +/** + * This is used by `basic_mmap` to determine whether to create a read-only or + * a read-write memory mapping. + */ +enum class access_mode +{ + read, + write +}; + +/** + * Determines the operating system's page allocation granularity. + * + * On the first call to this function, it invokes the operating system specific syscall + * to determine the page size, caches the value, and returns it. Any subsequent call to + * this function serves the cached value, so no further syscalls are made. + */ +inline size_t page_size() +{ + static const size_t page_size = [] + { +#ifdef _WIN32 + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + return SystemInfo.dwAllocationGranularity; +#else + return sysconf(_SC_PAGE_SIZE); +#endif + }(); + return page_size; +} + +/** + * Alligns `offset` to the operating's system page size such that it subtracts the + * difference until the nearest page boundary before `offset`, or does nothing if + * `offset` is already page aligned. + */ +inline size_t make_offset_page_aligned(size_t offset) noexcept +{ + const size_t page_size_ = page_size(); + // Use integer division to round down to the nearest page alignment. + return offset / page_size_ * page_size_; +} + +} // namespace mio + +#endif // MIO_PAGE_HEADER +/* Copyright 2017 https://github.com/mandreyel + * + * 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. + */ + +#ifndef MIO_SHARED_MMAP_HEADER +#define MIO_SHARED_MMAP_HEADER + +// #include "mio/mmap.hpp" + + +#include // std::error_code +#include // std::shared_ptr + +namespace mio { + +/** + * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with + * `std::shared_ptr` semantics. + * + * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if + * shared semantics are not required. + */ +template< + access_mode AccessMode, + typename ByteT +> class basic_shared_mmap +{ + using impl_type = basic_mmap; + std::shared_ptr pimpl_; + +public: + using value_type = typename impl_type::value_type; + using size_type = typename impl_type::size_type; + using reference = typename impl_type::reference; + using const_reference = typename impl_type::const_reference; + using pointer = typename impl_type::pointer; + using const_pointer = typename impl_type::const_pointer; + using difference_type = typename impl_type::difference_type; + using iterator = typename impl_type::iterator; + using const_iterator = typename impl_type::const_iterator; + using reverse_iterator = typename impl_type::reverse_iterator; + using const_reverse_iterator = typename impl_type::const_reverse_iterator; + using iterator_category = typename impl_type::iterator_category; + using handle_type = typename impl_type::handle_type; + using mmap_type = impl_type; + + basic_shared_mmap() = default; + basic_shared_mmap(const basic_shared_mmap&) = default; + basic_shared_mmap& operator=(const basic_shared_mmap&) = default; + basic_shared_mmap(basic_shared_mmap&&) = default; + basic_shared_mmap& operator=(basic_shared_mmap&&) = default; + + /** Takes ownership of an existing mmap object. */ + basic_shared_mmap(mmap_type&& mmap) + : pimpl_(std::make_shared(std::move(mmap))) + {} + + /** Takes ownership of an existing mmap object. */ + basic_shared_mmap& operator=(mmap_type&& mmap) + { + pimpl_ = std::make_shared(std::move(mmap)); + return *this; + } + + /** Initializes this object with an already established shared mmap. */ + basic_shared_mmap(std::shared_ptr mmap) : pimpl_(std::move(mmap)) {} + + /** Initializes this object with an already established shared mmap. */ + basic_shared_mmap& operator=(std::shared_ptr mmap) + { + pimpl_ = std::move(mmap); + return *this; + } + +#ifdef __cpp_exceptions + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + template + basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(path, offset, length, error); + if(error) { throw std::system_error(error); } + } + + /** + * The same as invoking the `map` function, except any error that may occur + * while establishing the mapping is wrapped in a `std::system_error` and is + * thrown. + */ + basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file) + { + std::error_code error; + map(handle, offset, length, error); + if(error) { throw std::system_error(error); } + } +#endif // __cpp_exceptions + + /** + * If this is a read-write mapping and the last reference to the mapping, + * the destructor invokes sync. Regardless of the access mode, unmap is + * invoked as a final step. + */ + ~basic_shared_mmap() = default; + + /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */ + std::shared_ptr get_shared_ptr() { return pimpl_; } + + /** + * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows, + * however, a mapped region of a file gets its own handle, which is returned by + * 'mapping_handle'. + */ + handle_type file_handle() const noexcept + { + return pimpl_ ? pimpl_->file_handle() : invalid_handle; + } + + handle_type mapping_handle() const noexcept + { + return pimpl_ ? pimpl_->mapping_handle() : invalid_handle; + } + + /** Returns whether a valid memory mapping has been created. */ + bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); } + + /** + * Returns true if no mapping was established, that is, conceptually the + * same as though the length that was mapped was 0. This function is + * provided so that this class has Container semantics. + */ + bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); } + + /** + * `size` and `length` both return the logical length, i.e. the number of bytes + * user requested to be mapped, while `mapped_length` returns the actual number of + * bytes that were mapped which is a multiple of the underlying operating system's + * page allocation granularity. + */ + size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; } + size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; } + size_type mapped_length() const noexcept + { + return pimpl_ ? pimpl_->mapped_length() : 0; + } + + /** + * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping + * exists. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > pointer data() noexcept { return pimpl_->data(); } + const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; } + + /** + * Returns an iterator to the first requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + iterator begin() noexcept { return pimpl_->begin(); } + const_iterator begin() const noexcept { return pimpl_->begin(); } + const_iterator cbegin() const noexcept { return pimpl_->cbegin(); } + + /** + * Returns an iterator one past the last requested byte, if a valid memory mapping + * exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > iterator end() noexcept { return pimpl_->end(); } + const_iterator end() const noexcept { return pimpl_->end(); } + const_iterator cend() const noexcept { return pimpl_->cend(); } + + /** + * Returns a reverse iterator to the last memory mapped byte, if a valid + * memory mapping exists, otherwise this function call is undefined + * behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); } + const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); } + const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); } + + /** + * Returns a reverse iterator past the first mapped byte, if a valid memory + * mapping exists, otherwise this function call is undefined behaviour. + */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > reverse_iterator rend() noexcept { return pimpl_->rend(); } + const_reverse_iterator rend() const noexcept { return pimpl_->rend(); } + const_reverse_iterator crend() const noexcept { return pimpl_->crend(); } + + /** + * Returns a reference to the `i`th byte from the first requested byte (as returned + * by `data`). If this is invoked when no valid memory mapping has been created + * prior to this call, undefined behaviour ensues. + */ + reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; } + const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + template + void map(const String& path, const size_type offset, + const size_type length, std::error_code& error) + { + map_impl(path, offset, length, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `path`, which must be a path to an existing file, is used to retrieve a file + * handle (which is closed when the object destructs or `unmap` is called), which is + * then used to memory map the requested region. Upon failure, `error` is set to + * indicate the reason and the object remains in an unmapped state. + * + * The entire file is mapped. + */ + template + void map(const String& path, std::error_code& error) + { + map_impl(path, 0, map_entire_file, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * `offset` is the number of bytes, relative to the start of the file, where the + * mapping should begin. When specifying it, there is no need to worry about + * providing a value that is aligned with the operating system's page allocation + * granularity. This is adjusted by the implementation such that the first requested + * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at + * `offset` from the start of the file. + * + * `length` is the number of bytes to map. It may be `map_entire_file`, in which + * case a mapping of the entire file is created. + */ + void map(const handle_type handle, const size_type offset, + const size_type length, std::error_code& error) + { + map_impl(handle, offset, length, error); + } + + /** + * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the + * reason is reported via `error` and the object remains in a state as if this + * function hadn't been called. + * + * `handle`, which must be a valid file handle, which is used to memory map the + * requested region. Upon failure, `error` is set to indicate the reason and the + * object remains in an unmapped state. + * + * The entire file is mapped. + */ + void map(const handle_type handle, std::error_code& error) + { + map_impl(handle, 0, map_entire_file, error); + } + + /** + * If a valid memory mapping has been created prior to this call, this call + * instructs the kernel to unmap the memory region and disassociate this object + * from the file. + * + * The file handle associated with the file that is mapped is only closed if the + * mapping was created using a file path. If, on the other hand, an existing + * file handle was used to create the mapping, the file handle is not closed. + */ + void unmap() { if(pimpl_) pimpl_->unmap(); } + + void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); } + + /** Flushes the memory mapped page to disk. Errors are reported via `error`. */ + template< + access_mode A = AccessMode, + typename = typename std::enable_if::type + > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); } + + /** All operators compare the underlying `basic_mmap`'s addresses. */ + + friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ == b.pimpl_; + } + + friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return !(a == b); + } + + friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ < b.pimpl_; + } + + friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ <= b.pimpl_; + } + + friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ > b.pimpl_; + } + + friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b) + { + return a.pimpl_ >= b.pimpl_; + } + +private: + template + void map_impl(const MappingToken& token, const size_type offset, + const size_type length, std::error_code& error) + { + if(!pimpl_) + { + mmap_type mmap = make_mmap(token, offset, length, error); + if(error) { return; } + pimpl_ = std::make_shared(std::move(mmap)); + } + else + { + pimpl_->map(token, offset, length, error); + } + } +}; + +/** + * This is the basis for all read-only mmap objects and should be preferred over + * directly using basic_shared_mmap. + */ +template +using basic_shared_mmap_source = basic_shared_mmap; + +/** + * This is the basis for all read-write mmap objects and should be preferred over + * directly using basic_shared_mmap. + */ +template +using basic_shared_mmap_sink = basic_shared_mmap; + +/** + * These aliases cover the most common use cases, both representing a raw byte stream + * (either with a char or an unsigned char/uint8_t). + */ +using shared_mmap_source = basic_shared_mmap_source; +using shared_ummap_source = basic_shared_mmap_source; + +using shared_mmap_sink = basic_shared_mmap_sink; +using shared_ummap_sink = basic_shared_mmap_sink; + +} // namespace mio + +#endif // MIO_SHARED_MMAP_HEADER + +/** @file + * @brief Contains the main CSV parsing algorithm and various utility functions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** @file + * A standalone header file containing shared code + */ + +#include +#include +#include +#include +#include + +#if defined(_WIN32) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# undef max +# undef min +#elif defined(__linux__) +# include +#endif + + /** Helper macro which should be #defined as "inline" + * in the single header version + */ +#define CSV_INLINE inline + +#include + +// Copyright 2017-2019 by Martin Moene +// +// string-view lite, a C++17-like string_view for C++98 and later. +// For more information see https://github.com/martinmoene/string-view-lite +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef NONSTD_SV_LITE_H_INCLUDED +#define NONSTD_SV_LITE_H_INCLUDED + +#define string_view_lite_MAJOR 1 +#define string_view_lite_MINOR 1 +#define string_view_lite_PATCH 0 + +#define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH) + +#define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x ) +#define nssv_STRINGIFY_( x ) #x + +// string-view lite configuration: + +#define nssv_STRING_VIEW_DEFAULT 0 +#define nssv_STRING_VIEW_NONSTD 1 +#define nssv_STRING_VIEW_STD 2 + +#if !defined( nssv_CONFIG_SELECT_STRING_VIEW ) +# define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD ) +#endif + +#if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW ) +# error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_... +#endif + +#ifndef nssv_CONFIG_STD_SV_OPERATOR +# define nssv_CONFIG_STD_SV_OPERATOR 0 +#endif + +#ifndef nssv_CONFIG_USR_SV_OPERATOR +# define nssv_CONFIG_USR_SV_OPERATOR 1 +#endif + +#ifdef nssv_CONFIG_CONVERSION_STD_STRING +# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING +# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING +#endif + +#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS +# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1 +#endif + +#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS +# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1 +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef nssv_CONFIG_NO_EXCEPTIONS +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) +# define nssv_CONFIG_NO_EXCEPTIONS 0 +# else +# define nssv_CONFIG_NO_EXCEPTIONS 1 +# endif +#endif + +// C++ language version detection (C++20 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef nssv_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define nssv_CPLUSPLUS __cplusplus +# endif +#endif + +#define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L ) +#define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L ) +#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L ) +#define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L ) +#define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L ) +#define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202000L ) + +// use C++17 std::string_view if available and requested: + +#if nssv_CPP17_OR_GREATER && defined(__has_include ) +# if __has_include( ) +# define nssv_HAVE_STD_STRING_VIEW 1 +# else +# define nssv_HAVE_STD_STRING_VIEW 0 +# endif +#else +# define nssv_HAVE_STD_STRING_VIEW 0 +#endif + +#define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) ) + +#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW ) +#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH + +// +// Use C++17 std::string_view: +// + +#if nssv_USES_STD_STRING_VIEW + +#include + +// Extensions for std::string: + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { + +template< class CharT, class Traits, class Allocator = std::allocator > +std::basic_string +to_string( std::basic_string_view v, Allocator const & a = Allocator() ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} + +template< class CharT, class Traits, class Allocator > +std::basic_string_view +to_string_view( std::basic_string const & s ) +{ + return std::basic_string_view( s.data(), s.size() ); +} + +// Literal operators sv and _sv: + +#if nssv_CONFIG_STD_SV_OPERATOR + +using namespace std::literals::string_view_literals; + +#endif + +#if nssv_CONFIG_USR_SV_OPERATOR + +inline namespace literals { +inline namespace string_view_literals { + + +constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1) +{ + return std::string_view{ str, len }; +} + +constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2) +{ + return std::u16string_view{ str, len }; +} + +constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3) +{ + return std::u32string_view{ str, len }; +} + +constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4) +{ + return std::wstring_view{ str, len }; +} + +}} // namespace literals::string_view_literals + +#endif // nssv_CONFIG_USR_SV_OPERATOR + +} // namespace nonstd + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { + +using std::string_view; +using std::wstring_view; +using std::u16string_view; +using std::u32string_view; +using std::basic_string_view; + +// literal "sv" and "_sv", see above + +using std::operator==; +using std::operator!=; +using std::operator<; +using std::operator<=; +using std::operator>; +using std::operator>=; + +using std::operator<<; + +} // namespace nonstd + +#else // nssv_HAVE_STD_STRING_VIEW + +// +// Before C++17: use string_view lite: +// + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define nssv_COMPILER_MSVC_VER (_MSC_VER ) +# define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) +#else +# define nssv_COMPILER_MSVC_VER 0 +# define nssv_COMPILER_MSVC_VERSION 0 +#endif + +#define nssv_COMPILER_VERSION( major, minor, patch ) (10 * ( 10 * major + minor) + patch) + +#if defined(__clang__) +# define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +# define nssv_COMPILER_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +# define nssv_COMPILER_GNUC_VERSION 0 +#endif + +// half-open range [lo..hi): +#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Presence of language and library features: + +#ifdef _HAS_CPP0X +# define nssv_HAS_CPP0X _HAS_CPP0X +#else +# define nssv_HAS_CPP0X 0 +#endif + +// Unless defined otherwise below, consider VC14 as C++11 for variant-lite: + +#if nssv_COMPILER_MSVC_VER >= 1900 +# undef nssv_CPP11_OR_GREATER +# define nssv_CPP11_OR_GREATER 1 +#endif + +#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500) +#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600) +#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700) +#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800) +#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900) +#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910) + +#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER) +#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140 +#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140 +#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140 +#define nssv_HAVE_NOEXCEPT nssv_CPP11_140 +#define nssv_HAVE_NULLPTR nssv_CPP11_100 +#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140 +#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140 +#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140 +#define nssv_HAVE_WCHAR16_T nssv_CPP11_100 +#define nssv_HAVE_WCHAR32_T nssv_CPP11_100 + +#if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) +# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 +#endif + +// Presence of C++14 language features: + +#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000 + +// Presence of C++17 language features: + +#define nssv_HAVE_NODISCARD nssv_CPP17_000 + +// Presence of C++ library features: + +#define nssv_HAVE_STD_HASH nssv_CPP11_120 + +// C++ feature usage: + +#if nssv_HAVE_CONSTEXPR_11 +# define nssv_constexpr constexpr +#else +# define nssv_constexpr /*constexpr*/ +#endif + +#if nssv_HAVE_CONSTEXPR_14 +# define nssv_constexpr14 constexpr +#else +# define nssv_constexpr14 /*constexpr*/ +#endif + +#if nssv_HAVE_EXPLICIT_CONVERSION +# define nssv_explicit explicit +#else +# define nssv_explicit /*explicit*/ +#endif + +#if nssv_HAVE_INLINE_NAMESPACE +# define nssv_inline_ns inline +#else +# define nssv_inline_ns /*inline*/ +#endif + +#if nssv_HAVE_NOEXCEPT +# define nssv_noexcept noexcept +#else +# define nssv_noexcept /*noexcept*/ +#endif + +//#if nssv_HAVE_REF_QUALIFIER +//# define nssv_ref_qual & +//# define nssv_refref_qual && +//#else +//# define nssv_ref_qual /*&*/ +//# define nssv_refref_qual /*&&*/ +//#endif + +#if nssv_HAVE_NULLPTR +# define nssv_nullptr nullptr +#else +# define nssv_nullptr NULL +#endif + +#if nssv_HAVE_NODISCARD +# define nssv_nodiscard [[nodiscard]] +#else +# define nssv_nodiscard /*[[nodiscard]]*/ +#endif + +// Additional includes: + +#include +#include +#include +#include +#include +#include // std::char_traits<> + +#if ! nssv_CONFIG_NO_EXCEPTIONS +# include +#endif + +#if nssv_CPP11_OR_GREATER +# include +#endif + +// Clang, GNUC, MSVC warning suppression macros: + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wreserved-user-defined-literal" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wuser-defined-literals" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wliteral-suffix" +#endif // __clang__ + +#if nssv_COMPILER_MSVC_VERSION >= 140 +# define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] +# define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) ) +# define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) +#else +# define nssv_SUPPRESS_MSGSL_WARNING(expr) +# define nssv_SUPPRESS_MSVC_WARNING(code, descr) +# define nssv_DISABLE_MSVC_WARNINGS(codes) +#endif + +#if defined(__clang__) +# define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) +# define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") +#elif nssv_COMPILER_MSVC_VERSION >= 140 +# define nssv_RESTORE_WARNINGS() __pragma(warning(pop )) +#else +# define nssv_RESTORE_WARNINGS() +#endif + +// Suppress the following MSVC (GSL) warnings: +// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not +// start with an underscore are reserved +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narow +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead + +nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 ) +//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) +//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) + +namespace nonstd { namespace sv_lite { + +template +< + class CharT, + class Traits = std::char_traits +> +class basic_string_view; + +// +// basic_string_view: +// + +template +< + class CharT, + class Traits /* = std::char_traits */ +> +class basic_string_view +{ +public: + // Member types: + + typedef Traits traits_type; + typedef CharT value_type; + + typedef CharT * pointer; + typedef CharT const * const_pointer; + typedef CharT & reference; + typedef CharT const & const_reference; + + typedef const_pointer iterator; + typedef const_pointer const_iterator; + typedef std::reverse_iterator< const_iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + // 24.4.2.1 Construction and assignment: + + nssv_constexpr basic_string_view() nssv_noexcept + : data_( nssv_nullptr ) + , size_( 0 ) + {} + +#if nssv_CPP11_OR_GREATER + nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default; +#else + nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept + : data_( other.data_) + , size_( other.size_) + {} +#endif + + nssv_constexpr basic_string_view( CharT const * s, size_type count ) + : data_( s ) + , size_( count ) + {} + + nssv_constexpr basic_string_view( CharT const * s) + : data_( s ) + , size_( Traits::length(s) ) + {} + + // Assignment: + +#if nssv_CPP11_OR_GREATER + nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default; +#else + nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept + { + data_ = other.data_; + size_ = other.size_; + return *this; + } +#endif + + // 24.4.2.2 Iterator support: + + nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; } + nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; } + + nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); } + nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); } + + nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); } + nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); } + + nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); } + nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); } + + // 24.4.2.3 Capacity: + + nssv_constexpr size_type size() const nssv_noexcept { return size_; } + nssv_constexpr size_type length() const nssv_noexcept { return size_; } + nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); } + + // since C++20 + nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept + { + return 0 == size_; + } + + // 24.4.2.4 Element access: + + nssv_constexpr const_reference operator[]( size_type pos ) const + { + return data_at( pos ); + } + + nssv_constexpr14 const_reference at( size_type pos ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos < size() ); +#else + if ( pos >= size() ) + { + throw std::out_of_range("nonst::string_view::at()"); + } +#endif + return data_at( pos ); + } + + nssv_constexpr const_reference front() const { return data_at( 0 ); } + nssv_constexpr const_reference back() const { return data_at( size() - 1 ); } + + nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } + + // 24.4.2.5 Modifiers: + + nssv_constexpr14 void remove_prefix( size_type n ) + { + assert( n <= size() ); + data_ += n; + size_ -= n; + } + + nssv_constexpr14 void remove_suffix( size_type n ) + { + assert( n <= size() ); + size_ -= n; + } + + nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept + { + using std::swap; + swap( data_, other.data_ ); + swap( size_, other.size_ ); + } + + // 24.4.2.6 String operations: + + size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos <= size() ); +#else + if ( pos > size() ) + { + throw std::out_of_range("nonst::string_view::copy()"); + } +#endif + const size_type rlen = (std::min)( n, size() - pos ); + + (void) Traits::copy( dest, data() + pos, rlen ); + + return rlen; + } + + nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const + { +#if nssv_CONFIG_NO_EXCEPTIONS + assert( pos <= size() ); +#else + if ( pos > size() ) + { + throw std::out_of_range("nonst::string_view::substr()"); + } +#endif + return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); + } + + // compare(), 6x: + + nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1) + { + if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) + return result; + + return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; + } + + nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2) + { + return substr( pos1, n1 ).compare( other ); + } + + nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3) + { + return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) ); + } + + nssv_constexpr int compare( CharT const * s ) const // (4) + { + return compare( basic_string_view( s ) ); + } + + nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5) + { + return substr( pos1, n1 ).compare( basic_string_view( s ) ); + } + + nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6) + { + return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) ); + } + + // 24.4.2.7 Searching: + + // starts_with(), 3x, since C++20: + + nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1) + { + return size() >= v.size() && compare( 0, v.size(), v ) == 0; + } + + nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2) + { + return starts_with( basic_string_view( &c, 1 ) ); + } + + nssv_constexpr bool starts_with( CharT const * s ) const // (3) + { + return starts_with( basic_string_view( s ) ); + } + + // ends_with(), 3x, since C++20: + + nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1) + { + return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0; + } + + nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2) + { + return ends_with( basic_string_view( &c, 1 ) ); + } + + nssv_constexpr bool ends_with( CharT const * s ) const // (3) + { + return ends_with( basic_string_view( s ) ); + } + + // find(), 4x: + + nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) + { + return assert( v.size() == 0 || v.data() != nssv_nullptr ) + , pos >= size() + ? npos + : to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); + } + + nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) + { + return find( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const // (3) + { + return find( basic_string_view( s, n ), pos ); + } + + nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const // (4) + { + return find( basic_string_view( s ), pos ); + } + + // rfind(), 4x: + + nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) + { + if ( size() < v.size() ) + return npos; + + if ( v.empty() ) + return (std::min)( size(), pos ); + + const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size(); + const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq ); + + return result != last ? size_type( result - cbegin() ) : npos; + } + + nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2) + { + return rfind( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3) + { + return rfind( basic_string_view( s, n ), pos ); + } + + nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4) + { + return rfind( basic_string_view( s ), pos ); + } + + // find_first_of(), 4x: + + nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) + { + return pos >= size() + ? npos + : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); + } + + nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) + { + return find_first_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3) + { + return find_first_of( basic_string_view( s, n ), pos ); + } + + nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4) + { + return find_first_of( basic_string_view( s ), pos ); + } + + // find_last_of(), 4x: + + nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) + { + return empty() + ? npos + : pos >= size() + ? find_last_of( v, size() - 1 ) + : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) ); + } + + nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) + { + return find_last_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3) + { + return find_last_of( basic_string_view( s, count ), pos ); + } + + nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4) + { + return find_last_of( basic_string_view( s ), pos ); + } + + // find_first_not_of(), 4x: + + nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) + { + return pos >= size() + ? npos + : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) ); + } + + nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) + { + return find_first_not_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3) + { + return find_first_not_of( basic_string_view( s, count ), pos ); + } + + nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4) + { + return find_first_not_of( basic_string_view( s ), pos ); + } + + // find_last_not_of(), 4x: + + nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) + { + return empty() + ? npos + : pos >= size() + ? find_last_not_of( v, size() - 1 ) + : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) ); + } + + nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) + { + return find_last_not_of( basic_string_view( &c, 1 ), pos ); + } + + nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3) + { + return find_last_not_of( basic_string_view( s, count ), pos ); + } + + nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4) + { + return find_last_not_of( basic_string_view( s ), pos ); + } + + // Constants: + +#if nssv_CPP17_OR_GREATER + static nssv_constexpr size_type npos = size_type(-1); +#elif nssv_CPP11_OR_GREATER + enum : size_type { npos = size_type(-1) }; +#else + enum { npos = size_type(-1) }; +#endif + +private: + struct not_in_view + { + const basic_string_view v; + + nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {} + + nssv_constexpr bool operator()( CharT c ) const + { + return npos == v.find_first_of( c ); + } + }; + + nssv_constexpr size_type to_pos( const_iterator it ) const + { + return it == cend() ? npos : size_type( it - cbegin() ); + } + + nssv_constexpr size_type to_pos( const_reverse_iterator it ) const + { + return it == crend() ? npos : size_type( crend() - it - 1 ); + } + + nssv_constexpr const_reference data_at( size_type pos ) const + { +#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 ) + return data_[pos]; +#else + return assert( pos < size() ), data_[pos]; +#endif + } + +private: + const_pointer data_; + size_type size_; + +public: +#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS + + template< class Allocator > + basic_string_view( std::basic_string const & s ) nssv_noexcept + : data_( s.data() ) + , size_( s.size() ) + {} + +#if nssv_HAVE_EXPLICIT_CONVERSION + + template< class Allocator > + explicit operator std::basic_string() const + { + return to_string( Allocator() ); + } + +#endif // nssv_HAVE_EXPLICIT_CONVERSION + +#if nssv_CPP11_OR_GREATER + + template< class Allocator = std::allocator > + std::basic_string + to_string( Allocator const & a = Allocator() ) const + { + return std::basic_string( begin(), end(), a ); + } + +#else + + std::basic_string + to_string() const + { + return std::basic_string( begin(), end() ); + } + + template< class Allocator > + std::basic_string + to_string( Allocator const & a ) const + { + return std::basic_string( begin(), end(), a ); + } + +#endif // nssv_CPP11_OR_GREATER + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS +}; + +// +// Non-member functions: +// + +// 24.4.3 Non-member comparison functions: +// lexicographically compare two string views (function template): + +template< class CharT, class Traits > +nssv_constexpr bool operator== ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) == 0 ; } + +template< class CharT, class Traits > +nssv_constexpr bool operator!= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) != 0 ; } + +template< class CharT, class Traits > +nssv_constexpr bool operator< ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0 ; } + +template< class CharT, class Traits > +nssv_constexpr bool operator<= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0 ; } + +template< class CharT, class Traits > +nssv_constexpr bool operator> ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0 ; } + +template< class CharT, class Traits > +nssv_constexpr bool operator>= ( + basic_string_view lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0 ; } + +// Let S be basic_string_view, and sv be an instance of S. +// Implementations shall provide sufficient additional overloads marked +// constexpr and noexcept so that an object t with an implicit conversion +// to S can be compared according to Table 67. + +#if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) + +#define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type + +#if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 ) +# define nssv_MSVC_ORDER(x) , int=x +#else +# define nssv_MSVC_ORDER(x) /*, int=x*/ +#endif + +// == + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator==( + basic_string_view lhs, + nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) == 0; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator==( + nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, + basic_string_view rhs ) nssv_noexcept +{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } + +// != + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator!= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.size() != rhs.size() || lhs.compare( rhs ) != 0 ; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator!= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) != 0 ; } + +// < + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator< ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0 ; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator< ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) < 0 ; } + +// <= + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator<= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0 ; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator<= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) <= 0 ; } + +// > + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator> ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0 ; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator> ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) > 0 ; } + +// >= + +template< class CharT, class Traits nssv_MSVC_ORDER(1) > +nssv_constexpr bool operator>= ( + basic_string_view < CharT, Traits > lhs, + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0 ; } + +template< class CharT, class Traits nssv_MSVC_ORDER(2) > +nssv_constexpr bool operator>= ( + nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, + basic_string_view < CharT, Traits > rhs ) nssv_noexcept +{ return lhs.compare( rhs ) >= 0 ; } + +#undef nssv_MSVC_ORDER +#undef nssv_BASIC_STRING_VIEW_I + +#endif // nssv_CPP11_OR_GREATER + +// 24.4.4 Inserters and extractors: + +namespace detail { + +template< class Stream > +void write_padding( Stream & os, std::streamsize n ) +{ + for ( std::streamsize i = 0; i < n; ++i ) + os.rdbuf()->sputc( os.fill() ); +} + +template< class Stream, class View > +Stream & write_to_stream( Stream & os, View const & sv ) +{ + typename Stream::sentry sentry( os ); + + if ( !os ) + return os; + + const std::streamsize length = static_cast( sv.length() ); + + // Whether, and how, to pad: + const bool pad = ( length < os.width() ); + const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right; + + if ( left_pad ) + write_padding( os, os.width() - length ); + + // Write span characters: + os.rdbuf()->sputn( sv.begin(), length ); + + if ( pad && !left_pad ) + write_padding( os, os.width() - length ); + + // Reset output stream width: + os.width( 0 ); + + return os; +} + +} // namespace detail + +template< class CharT, class Traits > +std::basic_ostream & +operator<<( + std::basic_ostream& os, + basic_string_view sv ) +{ + return detail::write_to_stream( os, sv ); +} + +// Several typedefs for common character types are provided: + +typedef basic_string_view string_view; +typedef basic_string_view wstring_view; +#if nssv_HAVE_WCHAR16_T +typedef basic_string_view u16string_view; +typedef basic_string_view u32string_view; +#endif + +}} // namespace nonstd::sv_lite + +// +// 24.4.6 Suffix for basic_string_view literals: +// + +#if nssv_HAVE_USER_DEFINED_LITERALS + +namespace nonstd { +nssv_inline_ns namespace literals { +nssv_inline_ns namespace string_view_literals { + +#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS + +nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) +{ + return nonstd::sv_lite::string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) +{ + return nonstd::sv_lite::u16string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) +{ + return nonstd::sv_lite::u32string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) +{ + return nonstd::sv_lite::wstring_view{ str, len }; +} + +#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS + +#if nssv_CONFIG_USR_SV_OPERATOR + +nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) +{ + return nonstd::sv_lite::string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) +{ + return nonstd::sv_lite::u16string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) +{ + return nonstd::sv_lite::u32string_view{ str, len }; +} + +nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) +{ + return nonstd::sv_lite::wstring_view{ str, len }; +} + +#endif // nssv_CONFIG_USR_SV_OPERATOR + +}}} // namespace nonstd::literals::string_view_literals + +#endif + +// +// Extensions for std::string: +// + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +namespace nonstd { +namespace sv_lite { + +// Exclude MSVC 14 (19.00): it yields ambiguous to_string(): + +#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140 + +template< class CharT, class Traits, class Allocator = std::allocator > +std::basic_string +to_string( basic_string_view v, Allocator const & a = Allocator() ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} + +#else + +template< class CharT, class Traits > +std::basic_string +to_string( basic_string_view v ) +{ + return std::basic_string( v.begin(), v.end() ); +} + +template< class CharT, class Traits, class Allocator > +std::basic_string +to_string( basic_string_view v, Allocator const & a ) +{ + return std::basic_string( v.begin(), v.end(), a ); +} + +#endif // nssv_CPP11_OR_GREATER + +template< class CharT, class Traits, class Allocator > +basic_string_view +to_string_view( std::basic_string const & s ) +{ + return basic_string_view( s.data(), s.size() ); +} + +}} // namespace nonstd::sv_lite + +#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS + +// +// make types and algorithms available in namespace nonstd: +// + +namespace nonstd { + +using sv_lite::basic_string_view; +using sv_lite::string_view; +using sv_lite::wstring_view; + +#if nssv_HAVE_WCHAR16_T +using sv_lite::u16string_view; +#endif +#if nssv_HAVE_WCHAR32_T +using sv_lite::u32string_view; +#endif + +// literal "sv" + +using sv_lite::operator==; +using sv_lite::operator!=; +using sv_lite::operator<; +using sv_lite::operator<=; +using sv_lite::operator>; +using sv_lite::operator>=; + +using sv_lite::operator<<; + +#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS +using sv_lite::to_string; +using sv_lite::to_string_view; +#endif + +} // namespace nonstd + +// 24.4.5 Hash support (C++11): + +// Note: The hash value of a string view object is equal to the hash value of +// the corresponding string object. + +#if nssv_HAVE_STD_HASH + +#include + +namespace std { + +template<> +struct hash< nonstd::string_view > +{ +public: + std::size_t operator()( nonstd::string_view v ) const nssv_noexcept + { + return std::hash()( std::string( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::wstring_view > +{ +public: + std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept + { + return std::hash()( std::wstring( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::u16string_view > +{ +public: + std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept + { + return std::hash()( std::u16string( v.data(), v.size() ) ); + } +}; + +template<> +struct hash< nonstd::u32string_view > +{ +public: + std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept + { + return std::hash()( std::u32string( v.data(), v.size() ) ); + } +}; + +} // namespace std + +#endif // nssv_HAVE_STD_HASH + +nssv_RESTORE_WARNINGS() + +#endif // nssv_HAVE_STD_STRING_VIEW +#endif // NONSTD_SV_LITE_H_INCLUDED + + + // If there is another version of Hedley, then the newer one + // takes precedence. + // See: https://github.com/nemequ/hedley +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < 9) +#if defined(HEDLEY_VERSION) +# undef HEDLEY_VERSION +#endif +#define HEDLEY_VERSION 9 + +#if defined(HEDLEY_STRINGIFY_EX) +# undef HEDLEY_STRINGIFY_EX +#endif +#define HEDLEY_STRINGIFY_EX(x) #x + +#if defined(HEDLEY_STRINGIFY) +# undef HEDLEY_STRINGIFY +#endif +#define HEDLEY_STRINGIFY(x) HEDLEY_STRINGIFY_EX(x) + +#if defined(HEDLEY_CONCAT_EX) +# undef HEDLEY_CONCAT_EX +#endif +#define HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(HEDLEY_CONCAT) +# undef HEDLEY_CONCAT +#endif +#define HEDLEY_CONCAT(a,b) HEDLEY_CONCAT_EX(a,b) + +#if defined(HEDLEY_VERSION_ENCODE) +# undef HEDLEY_VERSION_ENCODE +#endif +#define HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(HEDLEY_VERSION_DECODE_MAJOR) +# undef HEDLEY_VERSION_DECODE_MAJOR +#endif +#define HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(HEDLEY_VERSION_DECODE_MINOR) +# undef HEDLEY_VERSION_DECODE_MINOR +#endif +#define HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(HEDLEY_VERSION_DECODE_REVISION) +# undef HEDLEY_VERSION_DECODE_REVISION +#endif +#define HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(HEDLEY_GNUC_VERSION) +# undef HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) +# define HEDLEY_GNUC_VERSION HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) +# define HEDLEY_GNUC_VERSION HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(HEDLEY_GNUC_VERSION_CHECK) +# undef HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(HEDLEY_GNUC_VERSION) +# define HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (HEDLEY_GNUC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_MSVC_VERSION) +# undef HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) +# define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) +# define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) +# define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(HEDLEY_MSVC_VERSION_CHECK) +# undef HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(_MSC_VER) +# define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) +# define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +# define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else +# define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(HEDLEY_INTEL_VERSION) +# undef HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) +# define HEDLEY_INTEL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) +# define HEDLEY_INTEL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(HEDLEY_INTEL_VERSION_CHECK) +# undef HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(HEDLEY_INTEL_VERSION) +# define HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (HEDLEY_INTEL_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_PGI_VERSION) +# undef HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) +# define HEDLEY_PGI_VERSION HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(HEDLEY_PGI_VERSION_CHECK) +# undef HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(HEDLEY_PGI_VERSION) +# define HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (HEDLEY_PGI_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_SUNPRO_VERSION) +# undef HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) +# define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) +# define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) +# define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) +# define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(HEDLEY_SUNPRO_VERSION_CHECK) +# undef HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(HEDLEY_SUNPRO_VERSION) +# define HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (HEDLEY_SUNPRO_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_EMSCRIPTEN_VERSION) +# undef HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) +# define HEDLEY_EMSCRIPTEN_VERSION HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(HEDLEY_EMSCRIPTEN_VERSION_CHECK) +# undef HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(HEDLEY_EMSCRIPTEN_VERSION) +# define HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (HEDLEY_EMSCRIPTEN_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_ARM_VERSION) +# undef HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) +# define HEDLEY_ARM_VERSION HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) +# define HEDLEY_ARM_VERSION HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(HEDLEY_ARM_VERSION_CHECK) +# undef HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(HEDLEY_ARM_VERSION) +# define HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (HEDLEY_ARM_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_IBM_VERSION) +# undef HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) +# define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) +# define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) +# define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(HEDLEY_IBM_VERSION_CHECK) +# undef HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(HEDLEY_IBM_VERSION) +# define HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (HEDLEY_IBM_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_TI_VERSION) +# undef HEDLEY_TI_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) +# define HEDLEY_TI_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(HEDLEY_TI_VERSION_CHECK) +# undef HEDLEY_TI_VERSION_CHECK +#endif +#if defined(HEDLEY_TI_VERSION) +# define HEDLEY_TI_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_CRAY_VERSION) +# undef HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) +# if defined(_RELEASE_PATCHLEVEL) +# define HEDLEY_CRAY_VERSION HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) +# else +# define HEDLEY_CRAY_VERSION HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) +# endif +#endif + +#if defined(HEDLEY_CRAY_VERSION_CHECK) +# undef HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(HEDLEY_CRAY_VERSION) +# define HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (HEDLEY_CRAY_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_IAR_VERSION) +# undef HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) +# if __VER__ > 1000 +# define HEDLEY_IAR_VERSION HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) +# else +# define HEDLEY_IAR_VERSION HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) +# endif +#endif + +#if defined(HEDLEY_IAR_VERSION_CHECK) +# undef HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(HEDLEY_IAR_VERSION) +# define HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (HEDLEY_IAR_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_TINYC_VERSION) +# undef HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) +# define HEDLEY_TINYC_VERSION HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(HEDLEY_TINYC_VERSION_CHECK) +# undef HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(HEDLEY_TINYC_VERSION) +# define HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (HEDLEY_TINYC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_DMC_VERSION) +# undef HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) +# define HEDLEY_DMC_VERSION HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(HEDLEY_DMC_VERSION_CHECK) +# undef HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(HEDLEY_DMC_VERSION) +# define HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (HEDLEY_DMC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_COMPCERT_VERSION) +# undef HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) +# define HEDLEY_COMPCERT_VERSION HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(HEDLEY_COMPCERT_VERSION_CHECK) +# undef HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(HEDLEY_COMPCERT_VERSION) +# define HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (HEDLEY_COMPCERT_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_PELLES_VERSION) +# undef HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) +# define HEDLEY_PELLES_VERSION HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(HEDLEY_PELLES_VERSION_CHECK) +# undef HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(HEDLEY_PELLES_VERSION) +# define HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (HEDLEY_PELLES_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_GCC_VERSION) +# undef HEDLEY_GCC_VERSION +#endif +#if \ + defined(HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(HEDLEY_INTEL_VERSION) && \ + !defined(HEDLEY_PGI_VERSION) && \ + !defined(HEDLEY_ARM_VERSION) && \ + !defined(HEDLEY_TI_VERSION) && \ + !defined(__COMPCERT__) +# define HEDLEY_GCC_VERSION HEDLEY_GNUC_VERSION +#endif + +#if defined(HEDLEY_GCC_VERSION_CHECK) +# undef HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(HEDLEY_GCC_VERSION) +# define HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (HEDLEY_GCC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +# define HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(HEDLEY_HAS_ATTRIBUTE) +# undef HEDLEY_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +# define HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_ATTRIBUTE) +# undef HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +# define HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else +# define HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_ATTRIBUTE) +# undef HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +# define HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else +# define HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_CPP_ATTRIBUTE) +# undef HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +# define HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else +# define HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) +# undef HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +# define HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else +# define HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_CPP_ATTRIBUTE) +# undef HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +# define HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else +# define HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_BUILTIN) +# undef HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) +# define HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else +# define HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_BUILTIN) +# undef HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) +# define HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else +# define HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_BUILTIN) +# undef HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) +# define HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else +# define HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_FEATURE) +# undef HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) +# define HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else +# define HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_FEATURE) +# undef HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) +# define HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else +# define HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_FEATURE) +# undef HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) +# define HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else +# define HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_EXTENSION) +# undef HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) +# define HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else +# define HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_EXTENSION) +# undef HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) +# define HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else +# define HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_EXTENSION) +# undef HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) +# define HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else +# define HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_DECLSPEC_ATTRIBUTE) +# undef HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +# define HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else +# define HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) +# undef HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +# define HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else +# define HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) +# undef HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +# define HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else +# define HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_HAS_WARNING) +# undef HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) +# define HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else +# define HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(HEDLEY_GNUC_HAS_WARNING) +# undef HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) +# define HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else +# define HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_GCC_HAS_WARNING) +# undef HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) +# define HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else +# define HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_TI_VERSION_CHECK(6,0,0) || \ + HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) +# define HEDLEY_PRAGMA(value) _Pragma(#value) +#elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define HEDLEY_PRAGMA(value) __pragma(value) +#else +# define HEDLEY_PRAGMA(value) +#endif + +#if defined(HEDLEY_DIAGNOSTIC_PUSH) +# undef HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(HEDLEY_DIAGNOSTIC_POP) +# undef HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif HEDLEY_GCC_VERSION_CHECK(4,6,0) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) +# define HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif HEDLEY_ARM_VERSION_CHECK(5,6,0) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif HEDLEY_TI_VERSION_CHECK(8,1,0) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif HEDLEY_PELLES_VERSION_CHECK(2,90,0) +# define HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") +# define HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else +# define HEDLEY_DIAGNOSTIC_PUSH +# define HEDLEY_DIAGNOSTIC_POP +#endif + +#if defined(HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) +# undef HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if HEDLEY_HAS_WARNING("-Wdeprecated-declarations") +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif HEDLEY_GCC_VERSION_CHECK(4,3,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif HEDLEY_TI_VERSION_CHECK(8,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif HEDLEY_PELLES_VERSION_CHECK(2,90,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else +# define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) +# undef HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif HEDLEY_GCC_VERSION_CHECK(4,3,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif HEDLEY_TI_VERSION_CHECK(8,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#else +# define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) +# undef HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if HEDLEY_HAS_WARNING("-Wcast-qual") +# define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif HEDLEY_GCC_VERSION_CHECK(3,0,0) +# define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else +# define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(HEDLEY_DEPRECATED) +# undef HEDLEY_DEPRECATED +#endif +#if defined(HEDLEY_DEPRECATED_FOR) +# undef HEDLEY_DEPRECATED_FOR +#endif +#if defined(__cplusplus) && (__cplusplus >= 201402L) +# define HEDLEY_DEPRECATED(since) [[deprecated("Since " #since)]] +# define HEDLEY_DEPRECATED_FOR(since, replacement) [[deprecated("Since " #since "; use " #replacement)]] +#elif \ + HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \ + HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + HEDLEY_TI_VERSION_CHECK(8,3,0) +# define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) +# define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif \ + HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) +# define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif HEDLEY_MSVC_VERSION_CHECK(14,0,0) +# define HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) +# define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + HEDLEY_PELLES_VERSION_CHECK(6,50,0) +# define HEDLEY_DEPRECATED(since) _declspec(deprecated) +# define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_DEPRECATED(since) _Pragma("deprecated") +# define HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else +# define HEDLEY_DEPRECATED(since) +# define HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(HEDLEY_UNAVAILABLE) +# undef HEDLEY_UNAVAILABLE +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(warning) || \ + HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else +# define HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(HEDLEY_WARN_UNUSED_RESULT) +# undef HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(__cplusplus) && (__cplusplus >= 201703L) +# define HEDLEY_WARN_UNUSED_RESULT [[nodiscard]] +#elif \ + HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + (HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) +#elif defined(_Check_return_) /* SAL */ +# define HEDLEY_WARN_UNUSED_RESULT _Check_return_ +#else +# define HEDLEY_WARN_UNUSED_RESULT +#endif + +#if defined(HEDLEY_SENTINEL) +# undef HEDLEY_SENTINEL +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(5,4,0) +# define HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else +# define HEDLEY_SENTINEL(position) +#endif + +#if defined(HEDLEY_NO_RETURN) +# undef HEDLEY_NO_RETURN +#endif +#if HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_NO_RETURN __noreturn +#elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +# define HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) +# define HEDLEY_NO_RETURN [[noreturn]] +#elif \ + HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(18,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(17,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0) +# define HEDLEY_NO_RETURN __declspec(noreturn) +#elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) +# define HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) +# define HEDLEY_NO_RETURN __attribute((noreturn)) +#elif HEDLEY_PELLES_VERSION_CHECK(9,0,0) +# define HEDLEY_NO_RETURN __declspec(noreturn) +#else +# define HEDLEY_NO_RETURN +#endif + +#if defined(HEDLEY_UNREACHABLE) +# undef HEDLEY_UNREACHABLE +#endif +#if defined(HEDLEY_UNREACHABLE_RETURN) +# undef HEDLEY_UNREACHABLE_RETURN +#endif +#if \ + (HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(HEDLEY_ARM_VERSION))) || \ + HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_IBM_VERSION_CHECK(13,1,5) +# define HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0) +# define HEDLEY_UNREACHABLE() __assume(0) +#elif HEDLEY_TI_VERSION_CHECK(6,0,0) +# if defined(__cplusplus) +# define HEDLEY_UNREACHABLE() std::_nassert(0) +# else +# define HEDLEY_UNREACHABLE() _nassert(0) +# endif +# define HEDLEY_UNREACHABLE_RETURN(value) return value +#elif defined(EXIT_FAILURE) +# define HEDLEY_UNREACHABLE() abort() +#else +# define HEDLEY_UNREACHABLE() +# define HEDLEY_UNREACHABLE_RETURN(value) return value +#endif +#if !defined(HEDLEY_UNREACHABLE_RETURN) +# define HEDLEY_UNREACHABLE_RETURN(value) HEDLEY_UNREACHABLE() +#endif + +#if defined(HEDLEY_ASSUME) +# undef HEDLEY_ASSUME +#endif +#if \ + HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_ASSUME(expr) __assume(expr) +#elif HEDLEY_HAS_BUILTIN(__builtin_assume) +# define HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif HEDLEY_TI_VERSION_CHECK(6,0,0) +# if defined(__cplusplus) +# define HEDLEY_ASSUME(expr) std::_nassert(expr) +# else +# define HEDLEY_ASSUME(expr) _nassert(expr) +# endif +#elif \ + (HEDLEY_HAS_BUILTIN(__builtin_unreachable) && !defined(HEDLEY_ARM_VERSION)) || \ + HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_IBM_VERSION_CHECK(13,1,5) +# define HEDLEY_ASSUME(expr) ((void) ((expr) ? 1 : (__builtin_unreachable(), 1))) +#else +# define HEDLEY_ASSUME(expr) ((void) (expr)) +#endif + + +HEDLEY_DIAGNOSTIC_PUSH +#if \ + HEDLEY_HAS_WARNING("-Wvariadic-macros") || \ + HEDLEY_GCC_VERSION_CHECK(4,0,0) +# if defined(__clang__) +# pragma clang diagnostic ignored "-Wvariadic-macros" +# elif defined(HEDLEY_GCC_VERSION) +# pragma GCC diagnostic ignored "-Wvariadic-macros" +# endif +#endif +#if defined(HEDLEY_NON_NULL) +# undef HEDLEY_NON_NULL +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) +# define HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else +# define HEDLEY_NON_NULL(...) +#endif +HEDLEY_DIAGNOSTIC_POP + +#if defined(HEDLEY_PRINTF_FORMAT) +# undef HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) +# define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) +# define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + HEDLEY_HAS_ATTRIBUTE(format) || \ + HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif HEDLEY_PELLES_VERSION_CHECK(6,0,0) +# define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else +# define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(HEDLEY_CONSTEXPR) +# undef HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) +# if __cplusplus >= 201103L +# define HEDLEY_CONSTEXPR constexpr +# endif +#endif +#if !defined(HEDLEY_CONSTEXPR) +# define HEDLEY_CONSTEXPR +#endif + +#if defined(HEDLEY_PREDICT) +# undef HEDLEY_PREDICT +#endif +#if defined(HEDLEY_LIKELY) +# undef HEDLEY_LIKELY +#endif +#if defined(HEDLEY_UNLIKELY) +# undef HEDLEY_UNLIKELY +#endif +#if defined(HEDLEY_UNPREDICTABLE) +# undef HEDLEY_UNPREDICTABLE +#endif +#if HEDLEY_HAS_BUILTIN(__builtin_unpredictable) +# define HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable(!!(expr)) +#endif +#if \ + HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \ + HEDLEY_GCC_VERSION_CHECK(9,0,0) +# define HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(expr, value, probability) +# define HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1, probability) +# define HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0, probability) +# define HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +# if !defined(HEDLEY_BUILTIN_UNPREDICTABLE) +# define HEDLEY_BUILTIN_UNPREDICTABLE(expr) __builtin_expect_with_probability(!!(expr), 1, 0.5) +# endif +#elif \ + HEDLEY_HAS_BUILTIN(__builtin_expect) || \ + HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(6,1,0) || \ + HEDLEY_TINYC_VERSION_CHECK(0,9,27) +# define HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect(!!(expr), (expected)) : (((void) (expected)), !!(expr))) +# define HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define HEDLEY_PREDICT(expr, expected, probability) (((void) (expected)), !!(expr)) +# define HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define HEDLEY_LIKELY(expr) (!!(expr)) +# define HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(HEDLEY_UNPREDICTABLE) +# define HEDLEY_UNPREDICTABLE(expr) HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(HEDLEY_MALLOC) +# undef HEDLEY_MALLOC +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(malloc) || \ + HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_MALLOC __attribute__((__malloc__)) +#elif HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) +# define HEDLEY_MALLOC __declspec(restrict) +#else +# define HEDLEY_MALLOC +#endif + +#if defined(HEDLEY_PURE) +# undef HEDLEY_PURE +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(pure) || \ + HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define HEDLEY_PURE __attribute__((__pure__)) +#elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) +# define HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define HEDLEY_PURE +#endif + +#if defined(HEDLEY_CONST) +# undef HEDLEY_CONST +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(const) || \ + HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define HEDLEY_CONST __attribute__((__const__)) +#else +# define HEDLEY_CONST HEDLEY_PURE +#endif + +#if defined(HEDLEY_RESTRICT) +# undef HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) +# define HEDLEY_RESTRICT restrict +#elif \ + HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) +# define HEDLEY_RESTRICT __restrict +#elif HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) +# define HEDLEY_RESTRICT _Restrict +#else +# define HEDLEY_RESTRICT +#endif + +#if defined(HEDLEY_INLINE) +# undef HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) +# define HEDLEY_INLINE inline +#elif \ + defined(HEDLEY_GCC_VERSION) || \ + HEDLEY_ARM_VERSION_CHECK(6,2,0) +# define HEDLEY_INLINE __inline__ +#elif \ + HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) +# define HEDLEY_INLINE __inline +#else +# define HEDLEY_INLINE +#endif + +#if defined(HEDLEY_ALWAYS_INLINE) +# undef HEDLEY_ALWAYS_INLINE +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) HEDLEY_INLINE +#elif HEDLEY_MSVC_VERSION_CHECK(12,0,0) +# define HEDLEY_ALWAYS_INLINE __forceinline +#elif HEDLEY_TI_VERSION_CHECK(7,0,0) && defined(__cplusplus) +# define HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define HEDLEY_ALWAYS_INLINE HEDLEY_INLINE +#endif + +#if defined(HEDLEY_NEVER_INLINE) +# undef HEDLEY_NEVER_INLINE +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(noinline) || \ + HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0) +# define HEDLEY_NEVER_INLINE __declspec(noinline) +#elif HEDLEY_PGI_VERSION_CHECK(10,2,0) +# define HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus) +# define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) +# define HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif HEDLEY_PELLES_VERSION_CHECK(9,0,0) +# define HEDLEY_NEVER_INLINE __declspec(noinline) +#else +# define HEDLEY_NEVER_INLINE +#endif + +#if defined(HEDLEY_PRIVATE) +# undef HEDLEY_PRIVATE +#endif +#if defined(HEDLEY_PUBLIC) +# undef HEDLEY_PUBLIC +#endif +#if defined(HEDLEY_IMPORT) +# undef HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define HEDLEY_PRIVATE +# define HEDLEY_PUBLIC __declspec(dllexport) +# define HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + HEDLEY_HAS_ATTRIBUTE(visibility) || \ + HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + HEDLEY_TI_VERSION_CHECK(8,0,0) || \ + (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_EABI__) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) +# define HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define HEDLEY_PRIVATE +# define HEDLEY_PUBLIC +# endif +# define HEDLEY_IMPORT extern +#endif + +#if defined(HEDLEY_NO_THROW) +# undef HEDLEY_NO_THROW +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) +# define HEDLEY_NO_THROW __declspec(nothrow) +#else +# define HEDLEY_NO_THROW +#endif + +#if defined(HEDLEY_FALL_THROUGH) +# undef HEDLEY_FALL_THROUGH +#endif +#if \ + defined(__cplusplus) && \ + (!defined(HEDLEY_SUNPRO_VERSION) || HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + !defined(HEDLEY_PGI_VERSION) +# if \ + (__cplusplus >= 201703L) || \ + ((__cplusplus >= 201103L) && HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)) +# define HEDLEY_FALL_THROUGH [[fallthrough]] +# elif (__cplusplus >= 201103L) && HEDLEY_HAS_CPP_ATTRIBUTE(clang::fallthrough) +# define HEDLEY_FALL_THROUGH [[clang::fallthrough]] +# elif (__cplusplus >= 201103L) && HEDLEY_GCC_VERSION_CHECK(7,0,0) +# define HEDLEY_FALL_THROUGH [[gnu::fallthrough]] +# endif +#endif +#if !defined(HEDLEY_FALL_THROUGH) +# if HEDLEY_GNUC_HAS_ATTRIBUTE(fallthrough,7,0,0) && !defined(HEDLEY_PGI_VERSION) +# define HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +# elif defined(__fallthrough) /* SAL */ +# define HEDLEY_FALL_THROUGH __fallthrough +# else +# define HEDLEY_FALL_THROUGH +# endif +#endif + +#if defined(HEDLEY_RETURNS_NON_NULL) +# undef HEDLEY_RETURNS_NON_NULL +#endif +#if \ + HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + HEDLEY_GCC_VERSION_CHECK(4,9,0) +# define HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ +# define HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else +# define HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(HEDLEY_ARRAY_PARAM) +# undef HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(HEDLEY_PGI_VERSION) && \ + !defined(HEDLEY_TINYC_VERSION) +# define HEDLEY_ARRAY_PARAM(name) (name) +#else +# define HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(HEDLEY_IS_CONSTANT) +# undef HEDLEY_IS_CONSTANT +#endif +#if defined(HEDLEY_REQUIRE_CONSTEXPR) +# undef HEDLEY_REQUIRE_CONSTEXPR +#endif +/* Note the double-underscore. For internal use only; no API + * guarantees! */ +#if defined(HEDLEY__IS_CONSTEXPR) +# undef HEDLEY__IS_CONSTEXPR +#endif + +#if \ + HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + HEDLEY_TI_VERSION_CHECK(6,1,0) || \ + HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) || \ + HEDLEY_CRAY_VERSION_CHECK(8,1,0) +# define HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + HEDLEY_TINYC_VERSION_CHECK(0,9,24) +# if defined(__INTPTR_TYPE__) +# define HEDLEY__IS_CONSTEXPR(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +# else +# include +# define HEDLEY__IS_CONSTEXPR(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +# endif +# elif \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(HEDLEY_SUNPRO_VERSION) && !defined(HEDLEY_PGI_VERSION)) || \ + HEDLEY_HAS_EXTENSION(c_generic_selections) || \ + HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + HEDLEY_ARM_VERSION_CHECK(5,3,0) +# if defined(__INTPTR_TYPE__) +# define HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +# else +# include +# define HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +# endif +# elif \ + defined(HEDLEY_GCC_VERSION) || \ + defined(HEDLEY_INTEL_VERSION) || \ + defined(HEDLEY_TINYC_VERSION) || \ + defined(HEDLEY_TI_VERSION) || \ + defined(__clang__) +# define HEDLEY__IS_CONSTEXPR(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ + ((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(HEDLEY__IS_CONSTEXPR) +# if !defined(HEDLEY_IS_CONSTANT) +# define HEDLEY_IS_CONSTANT(expr) HEDLEY__IS_CONSTEXPR(expr) +# endif +# define HEDLEY_REQUIRE_CONSTEXPR(expr) (HEDLEY__IS_CONSTEXPR(expr) ? (expr) : (-1)) +#else +# if !defined(HEDLEY_IS_CONSTANT) +# define HEDLEY_IS_CONSTANT(expr) (0) +# endif +# define HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(HEDLEY_BEGIN_C_DECLS) +# undef HEDLEY_BEGIN_C_DECLS +#endif +#if defined(HEDLEY_END_C_DECLS) +# undef HEDLEY_END_C_DECLS +#endif +#if defined(HEDLEY_C_DECL) +# undef HEDLEY_C_DECL +#endif +#if defined(__cplusplus) +# define HEDLEY_BEGIN_C_DECLS extern "C" { +# define HEDLEY_END_C_DECLS } +# define HEDLEY_C_DECL extern "C" +#else +# define HEDLEY_BEGIN_C_DECLS +# define HEDLEY_END_C_DECLS +# define HEDLEY_C_DECL +#endif + +#if defined(HEDLEY_STATIC_ASSERT) +# undef HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + HEDLEY_HAS_FEATURE(c_static_assert) || \ + HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201703L)) || \ + HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + (defined(__cplusplus) && HEDLEY_TI_VERSION_CHECK(8,3,0)) +# define HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr, message) +#elif defined(__cplusplus) && (__cplusplus >= 201103L) +# define HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr) +#else +# define HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(HEDLEY_CONST_CAST) +# undef HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + HEDLEY_HAS_WARNING("-Wcast-qual") || \ + HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + HEDLEY_DIAGNOSTIC_PUSH \ + HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(HEDLEY_REINTERPRET_CAST) +# undef HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) +# define HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else +# define HEDLEY_REINTERPRET_CAST(T, expr) (*((T*) &(expr))) +#endif + +#if defined(HEDLEY_STATIC_CAST) +# undef HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) +# define HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else +# define HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(HEDLEY_CPP_CAST) +# undef HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# define HEDLEY_CPP_CAST(T, expr) static_cast(expr) +#else +# define HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(HEDLEY_MESSAGE) +# undef HEDLEY_MESSAGE +#endif +#if HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define HEDLEY_MESSAGE(msg) \ + HEDLEY_DIAGNOSTIC_PUSH \ + HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + HEDLEY_PRAGMA(message msg) \ + HEDLEY_DIAGNOSTIC_POP +#elif \ + HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message msg) +#elif HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(_CRI message msg) +#elif HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message(msg)) +#elif HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message(msg)) +#else +# define HEDLEY_MESSAGE(msg) +#endif + +#if defined(HEDLEY_WARNING) +# undef HEDLEY_WARNING +#endif +#if HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define HEDLEY_WARNING(msg) \ + HEDLEY_DIAGNOSTIC_PUSH \ + HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + HEDLEY_PRAGMA(clang warning msg) \ + HEDLEY_DIAGNOSTIC_POP +#elif \ + HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + HEDLEY_PGI_VERSION_CHECK(18,4,0) +# define HEDLEY_WARNING(msg) HEDLEY_PRAGMA(GCC warning msg) +#elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) +# define HEDLEY_WARNING(msg) HEDLEY_PRAGMA(message(msg)) +#else +# define HEDLEY_WARNING(msg) HEDLEY_MESSAGE(msg) +#endif + +#if defined(HEDLEY_REQUIRE_MSG) +# undef HEDLEY_REQUIRE_MSG +#endif +#if HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if HEDLEY_HAS_WARNING("-Wgcc-compat") +# define HEDLEY_REQUIRE_MSG(expr, msg) \ + HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((__diagnose_if__(!(expr), msg, "error"))) \ + HEDLEY_DIAGNOSTIC_POP +# else +# define HEDLEY_REQUIRE_MSG(expr, msg) __attribute__((__diagnose_if__(!(expr), msg, "error"))) +# endif +#else +# define HEDLEY_REQUIRE_MSG(expr, msg) +#endif + +#if defined(HEDLEY_REQUIRE) +# undef HEDLEY_REQUIRE +#endif +#define HEDLEY_REQUIRE(expr) HEDLEY_REQUIRE_MSG(expr, #expr) + +#if defined(HEDLEY_FLAGS) +# undef HEDLEY_FLAGS +#endif +#if HEDLEY_HAS_ATTRIBUTE(flag_enum) +# define HEDLEY_FLAGS __attribute__((__flag_enum__)) +#endif + +#if defined(HEDLEY_FLAGS_CAST) +# undef HEDLEY_FLAGS_CAST +#endif +#if HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define HEDLEY_FLAGS_CAST(T, expr) HEDLEY_STATIC_CAST(T, expr) +#endif + +/* Remaining macros are deprecated. */ + +#if defined(HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) +# undef HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) +# define HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else +# define HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(HEDLEY_CLANG_HAS_ATTRIBUTE) +# undef HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) +# undef HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(HEDLEY_CLANG_HAS_BUILTIN) +# undef HEDLEY_CLANG_HAS_BUILTIN +#endif +#define HEDLEY_CLANG_HAS_BUILTIN(builtin) HEDLEY_HAS_BUILTIN(builtin) + +#if defined(HEDLEY_CLANG_HAS_FEATURE) +# undef HEDLEY_CLANG_HAS_FEATURE +#endif +#define HEDLEY_CLANG_HAS_FEATURE(feature) HEDLEY_HAS_FEATURE(feature) + +#if defined(HEDLEY_CLANG_HAS_EXTENSION) +# undef HEDLEY_CLANG_HAS_EXTENSION +#endif +#define HEDLEY_CLANG_HAS_EXTENSION(extension) HEDLEY_HAS_EXTENSION(extension) + +#if defined(HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) +# undef HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(HEDLEY_CLANG_HAS_WARNING) +# undef HEDLEY_CLANG_HAS_WARNING +#endif +#define HEDLEY_CLANG_HAS_WARNING(warning) HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < X) */ + + +namespace csv { +#ifdef _MSC_VER +#pragma region Compatibility Macros +#endif + /** + * @def IF_CONSTEXPR + * Expands to `if constexpr` in C++17 and `if` otherwise + * + * @def CONSTEXPR_VALUE + * Expands to `constexpr` in C++17 and `const` otherwise. + * Mainly used for global variables. + * + * @def CONSTEXPR + * Expands to `constexpr` in decent compilers and `inline` otherwise. + * Intended for functions and methods. + */ + +#define STATIC_ASSERT(x) static_assert(x, "Assertion failed") + +#if CMAKE_CXX_STANDARD == 17 || __cplusplus >= 201703L +#define CSV_HAS_CXX17 +#endif + +#if CMAKE_CXX_STANDARD >= 14 || __cplusplus >= 201402L +#define CSV_HAS_CXX14 +#endif + +#ifdef CSV_HAS_CXX17 +#include + /** @typedef string_view + * The string_view class used by this library. + */ + using string_view = std::string_view; +#else + /** @typedef string_view + * The string_view class used by this library. + */ + using string_view = nonstd::string_view; +#endif + +#ifdef CSV_HAS_CXX17 + #define IF_CONSTEXPR if constexpr + #define CONSTEXPR_VALUE constexpr + + #define CONSTEXPR_17 constexpr +#else + #define IF_CONSTEXPR if + #define CONSTEXPR_VALUE const + + #define CONSTEXPR_17 inline +#endif + +#ifdef CSV_HAS_CXX14 + template + using enable_if_t = std::enable_if_t; + + #define CONSTEXPR_14 constexpr + #define CONSTEXPR_VALUE_14 constexpr +#else + template + using enable_if_t = typename std::enable_if::type; + + #define CONSTEXPR_14 inline + #define CONSTEXPR_VALUE_14 const +#endif + + // Resolves g++ bug with regard to constexpr methods + // See: https://stackoverflow.com/questions/36489369/constexpr-non-static-member-function-with-non-constexpr-constructor-gcc-clang-d +#if defined __GNUC__ && !defined __clang__ + #if (__GNUC__ >= 7 &&__GNUC_MINOR__ >= 2) || (__GNUC__ >= 8) + #define CONSTEXPR constexpr + #endif + #else + #ifdef CSV_HAS_CXX17 + #define CONSTEXPR constexpr + #endif +#endif + +#ifndef CONSTEXPR +#define CONSTEXPR inline +#endif + +#ifdef _MSC_VER +#pragma endregion +#endif + + namespace internals { + // PAGE_SIZE macro could be already defined by the host system. +#if defined(PAGE_SIZE) +#undef PAGE_SIZE +#endif + +// Get operating system specific details +#if defined(_WIN32) + inline int getpagesize() { + _SYSTEM_INFO sys_info = {}; + GetSystemInfo(&sys_info); + return std::max(sys_info.dwPageSize, sys_info.dwAllocationGranularity); + } + + const int PAGE_SIZE = getpagesize(); +#elif defined(__linux__) + const int PAGE_SIZE = getpagesize(); +#else + /** Size of a memory page in bytes. Used by + * csv::internals::CSVFieldArray when allocating blocks. + */ + const int PAGE_SIZE = 4096; +#endif + + /** For functions that lazy load a large CSV, this determines how + * many bytes are read at a time + */ + constexpr size_t ITERATION_CHUNK_SIZE = 10000000; // 10MB + + template + inline bool is_equal(T a, T b, T epsilon = 0.001) { + /** Returns true if two floating point values are about the same */ + static_assert(std::is_floating_point::value, "T must be a floating point type."); + return std::abs(a - b) < epsilon; + } + + /** @typedef ParseFlags + * An enum used for describing the significance of each character + * with respect to CSV parsing + * + * @see quote_escape_flag + */ + enum class ParseFlags { + QUOTE_ESCAPE_QUOTE = 0, /**< A quote inside or terminating a quote_escaped field */ + QUOTE = 2 | 1, /**< Characters which may signify a quote escape */ + NOT_SPECIAL = 4, /**< Characters with no special meaning or escaped delimiters and newlines */ + DELIMITER = 4 | 2, /**< Characters which signify a new field */ + NEWLINE = 4 | 2 | 1 /**< Characters which signify a new row */ + }; + + /** Transform the ParseFlags given the context of whether or not the current + * field is quote escaped */ + constexpr ParseFlags quote_escape_flag(ParseFlags flag, bool quote_escape) noexcept { + return (ParseFlags)((int)flag & ~((int)ParseFlags::QUOTE * quote_escape)); + } + + // Assumed to be true by parsing functions: allows for testing + // if an item is DELIMITER or NEWLINE with a >= statement + STATIC_ASSERT(ParseFlags::DELIMITER < ParseFlags::NEWLINE); + + /** Optimizations for reducing branching in parsing loop + * + * Idea: The meaning of all non-quote characters changes depending + * on whether or not the parser is in a quote-escaped mode (0 or 1) + */ + STATIC_ASSERT(quote_escape_flag(ParseFlags::NOT_SPECIAL, false) == ParseFlags::NOT_SPECIAL); + STATIC_ASSERT(quote_escape_flag(ParseFlags::QUOTE, false) == ParseFlags::QUOTE); + STATIC_ASSERT(quote_escape_flag(ParseFlags::DELIMITER, false) == ParseFlags::DELIMITER); + STATIC_ASSERT(quote_escape_flag(ParseFlags::NEWLINE, false) == ParseFlags::NEWLINE); + + STATIC_ASSERT(quote_escape_flag(ParseFlags::NOT_SPECIAL, true) == ParseFlags::NOT_SPECIAL); + STATIC_ASSERT(quote_escape_flag(ParseFlags::QUOTE, true) == ParseFlags::QUOTE_ESCAPE_QUOTE); + STATIC_ASSERT(quote_escape_flag(ParseFlags::DELIMITER, true) == ParseFlags::NOT_SPECIAL); + STATIC_ASSERT(quote_escape_flag(ParseFlags::NEWLINE, true) == ParseFlags::NOT_SPECIAL); + + /** An array which maps ASCII chars to a parsing flag */ + using ParseFlagMap = std::array; + + /** An array which maps ASCII chars to a flag indicating if it is whitespace */ + using WhitespaceMap = std::array; + } + + /** Integer indicating a requested column wasn't found. */ + constexpr int CSV_NOT_FOUND = -1; +} + + +namespace csv { + namespace internals { + struct ColNames; + using ColNamesPtr = std::shared_ptr; + + /** @struct ColNames + * A data structure for handling column name information. + * + * These are created by CSVReader and passed (via smart pointer) + * to CSVRow objects it creates, thus + * allowing for indexing by column name. + */ + struct ColNames { + public: + ColNames() = default; + ColNames(const std::vector& names) { + set_col_names(names); + } + + std::vector get_col_names() const; + void set_col_names(const std::vector&); + int index_of(csv::string_view) const; + + bool empty() const noexcept { return this->col_names.empty(); } + size_t size() const noexcept; + + private: + std::vector col_names; + std::unordered_map col_pos; + }; + } +} +/** @file + * Defines an object used to store CSV format settings + */ + +#include +#include +#include +#include + + +namespace csv { + namespace internals { + class IBasicCSVParser; + } + + class CSVReader; + + /** Determines how to handle rows that are shorter or longer than the majority */ + enum class VariableColumnPolicy { + THROW = -1, + IGNORE_ROW = 0, + KEEP = 1 + }; + + /** Stores the inferred format of a CSV file. */ + struct CSVGuessResult { + char delim; + int header_row; + }; + + /** Stores information about how to parse a CSV file. + * Can be used to construct a csv::CSVReader. + */ + class CSVFormat { + public: + /** Settings for parsing a RFC 4180 CSV file */ + CSVFormat() = default; + + /** Sets the delimiter of the CSV file + * + * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap + */ + CSVFormat& delimiter(char delim); + + /** Sets a list of potential delimiters + * + * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap + * @param[in] delim An array of possible delimiters to try parsing the CSV with + */ + CSVFormat& delimiter(const std::vector & delim); + + /** Sets the whitespace characters to be trimmed + * + * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap + * @param[in] ws An array of whitespace characters that should be trimmed + */ + CSVFormat& trim(const std::vector & ws); + + /** Sets the quote character + * + * @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap + */ + CSVFormat& quote(char quote); + + /** Sets the column names. + * + * @note Unsets any values set by header_row() + */ + CSVFormat& column_names(const std::vector& names); + + /** Sets the header row + * + * @note Unsets any values set by column_names() + */ + CSVFormat& header_row(int row); + + /** Tells the parser that this CSV has no header row + * + * @note Equivalent to `header_row(-1)` + * + */ + CSVFormat& no_header() { + this->header_row(-1); + return *this; + } + + /** Turn quoting on or off */ + CSVFormat& quote(bool use_quote) { + this->no_quote = !use_quote; + return *this; + } + + /** Tells the parser how to handle columns of a different length than the others */ + CONSTEXPR_14 CSVFormat& variable_columns(VariableColumnPolicy policy = VariableColumnPolicy::IGNORE_ROW) { + this->variable_column_policy = policy; + return *this; + } + + /** Tells the parser how to handle columns of a different length than the others */ + CONSTEXPR_14 CSVFormat& variable_columns(bool policy) { + this->variable_column_policy = (VariableColumnPolicy)policy; + return *this; + } + + #ifndef DOXYGEN_SHOULD_SKIP_THIS + char get_delim() const { + // This error should never be received by end users. + if (this->possible_delimiters.size() > 1) { + throw std::runtime_error("There is more than one possible delimiter."); + } + + return this->possible_delimiters.at(0); + } + + CONSTEXPR bool is_quoting_enabled() const { return !this->no_quote; } + CONSTEXPR char get_quote_char() const { return this->quote_char; } + CONSTEXPR int get_header() const { return this->header; } + std::vector get_possible_delims() const { return this->possible_delimiters; } + std::vector get_trim_chars() const { return this->trim_chars; } + CONSTEXPR VariableColumnPolicy get_variable_column_policy() const { return this->variable_column_policy; } + #endif + + /** CSVFormat for guessing the delimiter */ + CSV_INLINE static CSVFormat guess_csv() { + CSVFormat format; + format.delimiter({ ',', '|', '\t', ';', '^' }) + .quote('"') + .header_row(0); + + return format; + } + + bool guess_delim() { + return this->possible_delimiters.size() > 1; + } + + friend CSVReader; + friend internals::IBasicCSVParser; + + private: + /**< Throws an error if delimiters and trim characters overlap */ + void assert_no_char_overlap(); + + /**< Set of possible delimiters */ + std::vector possible_delimiters = { ',' }; + + /**< Set of whitespace characters to trim */ + std::vector trim_chars = {}; + + /**< Row number with columns (ignored if col_names is non-empty) */ + int header = 0; + + /**< Whether or not to use quoting */ + bool no_quote = false; + + /**< Quote character */ + char quote_char = '"'; + + /**< Should be left empty unless file doesn't include header */ + std::vector col_names = {}; + + /**< Allow variable length columns? */ + VariableColumnPolicy variable_column_policy = VariableColumnPolicy::IGNORE_ROW; + }; +} +/** @file + * Defines the data type used for storing information about a CSV row + */ + +#include +#include +#include // For CSVField +#include // For CSVField +#include +#include +#include +#include +#include + +/** @file + * @brief Implements data type parsing functionality + */ + +#include +#include +#include +#include + + +namespace csv { + /** Enumerates the different CSV field types that are + * recognized by this library + * + * @note Overflowing integers will be stored and classified as doubles. + * @note Unlike previous releases, integer enums here are platform agnostic. + */ + enum class DataType { + UNKNOWN = -1, + CSV_NULL, /**< Empty string */ + CSV_STRING, /**< Non-numeric string */ + CSV_INT8, /**< 8-bit integer */ + CSV_INT16, /**< 16-bit integer (short on MSVC/GCC) */ + CSV_INT32, /**< 32-bit integer (int on MSVC/GCC) */ + CSV_INT64, /**< 64-bit integer (long long on MSVC/GCC) */ + CSV_DOUBLE /**< Floating point value */ + }; + + static_assert(DataType::CSV_STRING < DataType::CSV_INT8, "String type should come before numeric types."); + static_assert(DataType::CSV_INT8 < DataType::CSV_INT64, "Smaller integer types should come before larger integer types."); + static_assert(DataType::CSV_INT64 < DataType::CSV_DOUBLE, "Integer types should come before floating point value types."); + + namespace internals { + /** Compute 10 to the power of n */ + template + HEDLEY_CONST CONSTEXPR_14 + long double pow10(const T& n) noexcept { + long double multiplicand = n > 0 ? 10 : 0.1, + ret = 1; + + // Make all numbers positive + T iterations = n > 0 ? n : -n; + + for (T i = 0; i < iterations; i++) { + ret *= multiplicand; + } + + return ret; + } + + /** Compute 10 to the power of n */ + template<> + HEDLEY_CONST CONSTEXPR_14 + long double pow10(const unsigned& n) noexcept { + long double multiplicand = n > 0 ? 10 : 0.1, + ret = 1; + + for (unsigned i = 0; i < n; i++) { + ret *= multiplicand; + } + + return ret; + } + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + /** Private site-indexed array mapping byte sizes to an integer size enum */ + constexpr DataType int_type_arr[8] = { + DataType::CSV_INT8, // 1 + DataType::CSV_INT16, // 2 + DataType::UNKNOWN, + DataType::CSV_INT32, // 4 + DataType::UNKNOWN, + DataType::UNKNOWN, + DataType::UNKNOWN, + DataType::CSV_INT64 // 8 + }; + + template + inline DataType type_num() { + static_assert(std::is_integral::value, "T should be an integral type."); + static_assert(sizeof(T) <= 8, "Byte size must be no greater than 8."); + return int_type_arr[sizeof(T) - 1]; + } + + template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } + template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } + template<> inline DataType type_num() { return DataType::CSV_DOUBLE; } + template<> inline DataType type_num() { return DataType::CSV_NULL; } + template<> inline DataType type_num() { return DataType::CSV_STRING; } + + CONSTEXPR_14 DataType data_type(csv::string_view in, long double* const out = nullptr); +#endif + + /** Given a byte size, return the largest number than can be stored in + * an integer of that size + * + * Note: Provides a platform-agnostic way of mapping names like "long int" to + * byte sizes + */ + template + CONSTEXPR_14 long double get_int_max() { + static_assert(Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8, + "Bytes must be a power of 2 below 8."); + + IF_CONSTEXPR (sizeof(signed char) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR (sizeof(short) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR (sizeof(int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR (sizeof(long int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR (sizeof(long long int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + HEDLEY_UNREACHABLE(); + } + + /** Given a byte size, return the largest number than can be stored in + * an unsigned integer of that size + */ + template + CONSTEXPR_14 long double get_uint_max() { + static_assert(Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8, + "Bytes must be a power of 2 below 8."); + + IF_CONSTEXPR(sizeof(unsigned char) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR(sizeof(unsigned short) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR(sizeof(unsigned int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR(sizeof(unsigned long int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + IF_CONSTEXPR(sizeof(unsigned long long int) == Bytes) { + return (long double)std::numeric_limits::max(); + } + + HEDLEY_UNREACHABLE(); + } + + /** Largest number that can be stored in a 8-bit integer */ + CONSTEXPR_VALUE_14 long double CSV_INT8_MAX = get_int_max<1>(); + + /** Largest number that can be stored in a 16-bit integer */ + CONSTEXPR_VALUE_14 long double CSV_INT16_MAX = get_int_max<2>(); + + /** Largest number that can be stored in a 32-bit integer */ + CONSTEXPR_VALUE_14 long double CSV_INT32_MAX = get_int_max<4>(); + + /** Largest number that can be stored in a 64-bit integer */ + CONSTEXPR_VALUE_14 long double CSV_INT64_MAX = get_int_max<8>(); + + /** Largest number that can be stored in a 8-bit ungisned integer */ + CONSTEXPR_VALUE_14 long double CSV_UINT8_MAX = get_uint_max<1>(); + + /** Largest number that can be stored in a 16-bit unsigned integer */ + CONSTEXPR_VALUE_14 long double CSV_UINT16_MAX = get_uint_max<2>(); + + /** Largest number that can be stored in a 32-bit unsigned integer */ + CONSTEXPR_VALUE_14 long double CSV_UINT32_MAX = get_uint_max<4>(); + + /** Largest number that can be stored in a 64-bit unsigned integer */ + CONSTEXPR_VALUE_14 long double CSV_UINT64_MAX = get_uint_max<8>(); + + /** Given a pointer to the start of what is start of + * the exponential part of a number written (possibly) in scientific notation + * parse the exponent + */ + HEDLEY_PRIVATE CONSTEXPR_14 + DataType _process_potential_exponential( + csv::string_view exponential_part, + const long double& coeff, + long double * const out) { + long double exponent = 0; + auto result = data_type(exponential_part, &exponent); + + // Exponents in scientific notation should not be decimal numbers + if (result >= DataType::CSV_INT8 && result < DataType::CSV_DOUBLE) { + if (out) *out = coeff * pow10(exponent); + return DataType::CSV_DOUBLE; + } + + return DataType::CSV_STRING; + } + + /** Given the absolute value of an integer, determine what numeric type + * it fits in + */ + HEDLEY_PRIVATE HEDLEY_PURE CONSTEXPR_14 + DataType _determine_integral_type(const long double& number) noexcept { + // We can assume number is always non-negative + assert(number >= 0); + + if (number <= internals::CSV_INT8_MAX) + return DataType::CSV_INT8; + else if (number <= internals::CSV_INT16_MAX) + return DataType::CSV_INT16; + else if (number <= internals::CSV_INT32_MAX) + return DataType::CSV_INT32; + else if (number <= internals::CSV_INT64_MAX) + return DataType::CSV_INT64; + else // Conversion to long long will cause an overflow + return DataType::CSV_DOUBLE; + } + + /** Distinguishes numeric from other text values. Used by various + * type casting functions, like csv_parser::CSVReader::read_row() + * + * #### Rules + * - Leading and trailing whitespace ("padding") ignored + * - A string of just whitespace is NULL + * + * @param[in] in String value to be examined + * @param[out] out Pointer to long double where results of numeric parsing + * get stored + */ + CONSTEXPR_14 + DataType data_type(csv::string_view in, long double* const out) { + // Empty string --> NULL + if (in.size() == 0) + return DataType::CSV_NULL; + + bool ws_allowed = true, + neg_allowed = true, + dot_allowed = true, + digit_allowed = true, + has_digit = false, + prob_float = false; + + unsigned places_after_decimal = 0; + long double integral_part = 0, + decimal_part = 0; + + for (size_t i = 0, ilen = in.size(); i < ilen; i++) { + const char& current = in[i]; + + switch (current) { + case ' ': + if (!ws_allowed) { + if (isdigit(in[i - 1])) { + digit_allowed = false; + ws_allowed = true; + } + else { + // Ex: '510 123 4567' + return DataType::CSV_STRING; + } + } + break; + case '-': + if (!neg_allowed) { + // Ex: '510-123-4567' + return DataType::CSV_STRING; + } + + neg_allowed = false; + break; + case '.': + if (!dot_allowed) { + return DataType::CSV_STRING; + } + + dot_allowed = false; + prob_float = true; + break; + case 'e': + case 'E': + // Process scientific notation + if (prob_float || (i && i + 1 < ilen && isdigit(in[i - 1]))) { + size_t exponent_start_idx = i + 1; + prob_float = true; + + // Strip out plus sign + if (in[i + 1] == '+') { + exponent_start_idx++; + } + + return _process_potential_exponential( + in.substr(exponent_start_idx), + neg_allowed ? integral_part + decimal_part : -(integral_part + decimal_part), + out + ); + } + + return DataType::CSV_STRING; + break; + default: + short digit = static_cast(current - '0'); + if (digit >= 0 && digit <= 9) { + // Process digit + has_digit = true; + + if (!digit_allowed) + return DataType::CSV_STRING; + else if (ws_allowed) // Ex: '510 456' + ws_allowed = false; + + // Build current number + if (prob_float) + decimal_part += digit / pow10(++places_after_decimal); + else + integral_part = (integral_part * 10) + digit; + } + else { + return DataType::CSV_STRING; + } + } + } + + // No non-numeric/non-whitespace characters found + if (has_digit) { + long double number = integral_part + decimal_part; + if (out) { + *out = neg_allowed ? number : -number; + } + + return prob_float ? DataType::CSV_DOUBLE : _determine_integral_type(number); + } + + // Just whitespace + return DataType::CSV_NULL; + } + } +} + +namespace csv { + namespace internals { + class IBasicCSVParser; + + static const std::string ERROR_NAN = "Not a number."; + static const std::string ERROR_OVERFLOW = "Overflow error."; + static const std::string ERROR_FLOAT_TO_INT = + "Attempted to convert a floating point value to an integral type."; + static const std::string ERROR_NEG_TO_UNSIGNED = "Negative numbers cannot be converted to unsigned types."; + + std::string json_escape_string(csv::string_view s) noexcept; + + /** A barebones class used for describing CSV fields */ + struct RawCSVField { + RawCSVField() = default; + RawCSVField(size_t _start, size_t _length, bool _double_quote = false) { + start = _start; + length = _length; + has_double_quote = _double_quote; + } + + /** The start of the field, relative to the beginning of the row */ + size_t start; + + /** The length of the row, ignoring quote escape characters */ + size_t length; + + /** Whether or not the field contains an escaped quote */ + bool has_double_quote; + }; + + /** A class used for efficiently storing RawCSVField objects and expanding as necessary + * + * @par Implementation + * This data structure stores RawCSVField in continguous blocks. When more capacity + * is needed, a new block is allocated, but previous data stays put. + * + * @par Thread Safety + * This class may be safely read from multiple threads and written to from one, + * as long as the writing thread does not actively touch fields which are being + * read. + */ + class CSVFieldList { + public: + /** Construct a CSVFieldList which allocates blocks of a certain size */ + CSVFieldList(size_t single_buffer_capacity = (size_t)(internals::PAGE_SIZE / sizeof(RawCSVField))) : + _single_buffer_capacity(single_buffer_capacity) { + this->allocate(); + } + + // No copy constructor + CSVFieldList(const CSVFieldList& other) = delete; + + // CSVFieldArrays may be moved + CSVFieldList(CSVFieldList&& other) : + _single_buffer_capacity(other._single_buffer_capacity) { + buffers = std::move(other.buffers); + _current_buffer_size = other._current_buffer_size; + _back = other._back; + } + + ~CSVFieldList() { + for (auto& buffer : buffers) + delete[] buffer; + } + + template + void emplace_back(Args&&... args) { + if (this->_current_buffer_size == this->_single_buffer_capacity) { + this->allocate(); + } + + *(_back++) = RawCSVField(std::forward(args)...); + _current_buffer_size++; + } + + size_t size() const noexcept { + return this->_current_buffer_size + ((this->buffers.size() - 1) * this->_single_buffer_capacity); + } + + RawCSVField& operator[](size_t n) const; + + private: + const size_t _single_buffer_capacity; + + std::vector buffers = {}; + + /** Number of items in the current buffer */ + size_t _current_buffer_size = 0; + + /** Pointer to the current empty field */ + RawCSVField* _back = nullptr; + + /** Allocate a new page of memory */ + void allocate(); + }; + + + /** A class for storing raw CSV data and associated metadata */ + struct RawCSVData { + std::shared_ptr _data = nullptr; + csv::string_view data = ""; + + internals::CSVFieldList fields; + + std::unordered_set has_double_quotes = {}; + + // TODO: Consider replacing with a more thread-safe structure + std::unordered_map double_quote_fields = {}; + + internals::ColNamesPtr col_names = nullptr; + internals::ParseFlagMap parse_flags; + internals::WhitespaceMap ws_flags; + }; + + using RawCSVDataPtr = std::shared_ptr; + } + + /** + * @class CSVField + * @brief Data type representing individual CSV values. + * CSVFields can be obtained by using CSVRow::operator[] + */ + class CSVField { + public: + /** Constructs a CSVField from a string_view */ + constexpr explicit CSVField(csv::string_view _sv) noexcept : sv(_sv) { }; + + operator std::string() const { + return std::string(" ") + std::string(this->sv); + } + + /** Returns the value casted to the requested type, performing type checking before. + * + * \par Valid options for T + * - std::string or csv::string_view + * - signed integral types (signed char, short, int, long int, long long int) + * - floating point types (float, double, long double) + * - unsigned integers are not supported at this time, but may be in a later release + * + * \par Invalid conversions + * - Converting non-numeric values to any numeric type + * - Converting floating point values to integers + * - Converting a large integer to a smaller type that will not hold it + * + * @note This method is capable of parsing scientific E-notation. + * See [this page](md_docs_source_scientific_notation.html) + * for more details. + * + * @throws std::runtime_error Thrown if an invalid conversion is performed. + * + * @warning Currently, conversions to floating point types are not + * checked for loss of precision + * + * @warning Any string_views returned are only guaranteed to be valid + * if the parent CSVRow is still alive. If you are concerned + * about object lifetimes, then grab a std::string or a + * numeric value. + * + */ + template T get() { + IF_CONSTEXPR(std::is_arithmetic::value) { + // Note: this->type() also converts the CSV value to float + if (this->type() <= DataType::CSV_STRING) { + throw std::runtime_error(internals::ERROR_NAN); + } + } + + IF_CONSTEXPR(std::is_integral::value) { + // Note: this->is_float() also converts the CSV value to float + if (this->is_float()) { + throw std::runtime_error(internals::ERROR_FLOAT_TO_INT); + } + + IF_CONSTEXPR(std::is_unsigned::value) { + if (this->value < 0) { + throw std::runtime_error(internals::ERROR_NEG_TO_UNSIGNED); + } + } + } + + // Allow fallthrough from previous if branch + IF_CONSTEXPR(!std::is_floating_point::value) { + IF_CONSTEXPR(std::is_unsigned::value) { + // Quick hack to perform correct unsigned integer boundary checks + if (this->value > internals::get_uint_max()) { + throw std::runtime_error(internals::ERROR_OVERFLOW); + } + } + else if (internals::type_num() < this->_type) { + throw std::runtime_error(internals::ERROR_OVERFLOW); + } + } + + return static_cast(this->value); + } + + /** Parse a hexadecimal value, returning false if the value is not hex. */ + bool try_parse_hex(int& parsedValue); + + /** Compares the contents of this field to a numeric value. If this + * field does not contain a numeric value, then all comparisons return + * false. + * + * @note Floating point values are considered equal if they are within + * `0.000001` of each other. + * + * @warning Multiple numeric comparisons involving the same field can + * be done more efficiently by calling the CSVField::get<>() method. + * + * @sa csv::CSVField::operator==(const char * other) + * @sa csv::CSVField::operator==(csv::string_view other) + */ + template + CONSTEXPR_14 bool operator==(T other) const noexcept + { + static_assert(std::is_arithmetic::value, + "T should be a numeric value."); + + if (this->_type != DataType::UNKNOWN) { + if (this->_type == DataType::CSV_STRING) { + return false; + } + + return internals::is_equal(value, static_cast(other), 0.000001L); + } + + long double out = 0; + if (internals::data_type(this->sv, &out) == DataType::CSV_STRING) { + return false; + } + + return internals::is_equal(out, static_cast(other), 0.000001L); + } + + /** Return a string view over the field's contents */ + CONSTEXPR csv::string_view get_sv() const noexcept { return this->sv; } + + /** Returns true if field is an empty string or string of whitespace characters */ + CONSTEXPR_14 bool is_null() noexcept { return type() == DataType::CSV_NULL; } + + /** Returns true if field is a non-numeric, non-empty string */ + CONSTEXPR_14 bool is_str() noexcept { return type() == DataType::CSV_STRING; } + + /** Returns true if field is an integer or float */ + CONSTEXPR_14 bool is_num() noexcept { return type() >= DataType::CSV_INT8; } + + /** Returns true if field is an integer */ + CONSTEXPR_14 bool is_int() noexcept { + return (type() >= DataType::CSV_INT8) && (type() <= DataType::CSV_INT64); + } + + /** Returns true if field is a floating point value */ + CONSTEXPR_14 bool is_float() noexcept { return type() == DataType::CSV_DOUBLE; }; + + /** Return the type of the underlying CSV data */ + CONSTEXPR_14 DataType type() noexcept { + this->get_value(); + return _type; + } + + private: + long double value = 0; /**< Cached numeric value */ + csv::string_view sv = ""; /**< A pointer to this field's text */ + DataType _type = DataType::UNKNOWN; /**< Cached data type value */ + CONSTEXPR_14 void get_value() noexcept { + /* Check to see if value has been cached previously, if not + * evaluate it + */ + if ((int)_type < 0) { + this->_type = internals::data_type(this->sv, &this->value); + } + } + }; + + /** Data structure for representing CSV rows */ + class CSVRow { + public: + friend internals::IBasicCSVParser; + + CSVRow() = default; + + /** Construct a CSVRow from a RawCSVDataPtr */ + CSVRow(internals::RawCSVDataPtr _data) : data(_data) {} + CSVRow(internals::RawCSVDataPtr _data, size_t _data_start, size_t _field_bounds) + : data(_data), data_start(_data_start), fields_start(_field_bounds) {} + + /** Indicates whether row is empty or not */ + CONSTEXPR bool empty() const noexcept { return this->size() == 0; } + + /** Return the number of fields in this row */ + CONSTEXPR size_t size() const noexcept { return row_length; } + + /** @name Value Retrieval */ + ///@{ + CSVField operator[](size_t n) const; + CSVField operator[](const std::string&) const; + std::string to_json(const std::vector& subset = {}) const; + std::string to_json_array(const std::vector& subset = {}) const; + + /** Retrieve this row's associated column names */ + std::vector get_col_names() const { + return this->data->col_names->get_col_names(); + } + + /** Convert this CSVRow into a vector of strings. + * **Note**: This is a less efficient method of + * accessing data than using the [] operator. + */ + operator std::vector() const; + ///@} + + /** A random access iterator over the contents of a CSV row. + * Each iterator points to a CSVField. + */ + class iterator { + public: +#ifndef DOXYGEN_SHOULD_SKIP_THIS + using value_type = CSVField; + using difference_type = int; + + // Using CSVField * as pointer type causes segfaults in MSVC debug builds + // but using shared_ptr as pointer type won't compile in g++ +#ifdef _MSC_BUILD + using pointer = std::shared_ptr; +#else + using pointer = CSVField * ; +#endif + + using reference = CSVField & ; + using iterator_category = std::random_access_iterator_tag; +#endif + iterator(const CSVRow*, int i); + + reference operator*() const; + pointer operator->() const; + + iterator operator++(int); + iterator& operator++(); + iterator operator--(int); + iterator& operator--(); + iterator operator+(difference_type n) const; + iterator operator-(difference_type n) const; + + /** Two iterators are equal if they point to the same field */ + CONSTEXPR bool operator==(const iterator& other) const noexcept { + return this->i == other.i; + }; + + CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); } + +#ifndef NDEBUG + friend CSVRow; +#endif + + private: + const CSVRow * daddy = nullptr; // Pointer to parent + std::shared_ptr field = nullptr; // Current field pointed at + int i = 0; // Index of current field + }; + + /** A reverse iterator over the contents of a CSVRow. */ + using reverse_iterator = std::reverse_iterator; + + /** @name Iterators + * @brief Each iterator points to a CSVField object. + */ + ///@{ + iterator begin() const; + iterator end() const noexcept; + reverse_iterator rbegin() const noexcept; + reverse_iterator rend() const; + ///@} + + private: + /** Retrieve a string view corresponding to the specified index */ + csv::string_view get_field(size_t index) const; + + internals::RawCSVDataPtr data; + + /** Where in RawCSVData.data we start */ + size_t data_start = 0; + + /** Where in the RawCSVDataPtr.fields array we start */ + size_t fields_start = 0; + + /** How many columns this row spans */ + size_t row_length = 0; + }; + +#ifdef _MSC_VER +#pragma region CSVField::get Specializations +#endif + /** Retrieve this field's original string */ + template<> + inline std::string CSVField::get() { + return std::string(this->sv); + } + + /** Retrieve a view over this field's string + * + * @warning This string_view is only guaranteed to be valid as long as this + * CSVRow is still alive. + */ + template<> + CONSTEXPR_14 csv::string_view CSVField::get() { + return this->sv; + } + + /** Retrieve this field's value as a long double */ + template<> + CONSTEXPR_14 long double CSVField::get() { + if (!is_num()) + throw std::runtime_error(internals::ERROR_NAN); + + return this->value; + } +#ifdef _MSC_VER +#pragma endregion CSVField::get Specializations +#endif + + /** Compares the contents of this field to a string */ + template<> + CONSTEXPR bool CSVField::operator==(const char * other) const noexcept + { + return this->sv == other; + } + + /** Compares the contents of this field to a string */ + template<> + CONSTEXPR bool CSVField::operator==(csv::string_view other) const noexcept + { + return this->sv == other; + } +} + +inline std::ostream& operator << (std::ostream& os, csv::CSVField const& value) { + os << std::string(value); + return os; +} + + +namespace csv { + namespace internals { + /** Create a vector v where each index i corresponds to the + * ASCII number for a character and, v[i + 128] labels it according to + * the CSVReader::ParseFlags enum + */ + HEDLEY_CONST CONSTEXPR_17 ParseFlagMap make_parse_flags(char delimiter) { + std::array ret = {}; + for (int i = -128; i < 128; i++) { + const int arr_idx = i + 128; + char ch = char(i); + + if (ch == delimiter) + ret[arr_idx] = ParseFlags::DELIMITER; + else if (ch == '\r' || ch == '\n') + ret[arr_idx] = ParseFlags::NEWLINE; + else + ret[arr_idx] = ParseFlags::NOT_SPECIAL; + } + + return ret; + } + + /** Create a vector v where each index i corresponds to the + * ASCII number for a character and, v[i + 128] labels it according to + * the CSVReader::ParseFlags enum + */ + HEDLEY_CONST CONSTEXPR_17 ParseFlagMap make_parse_flags(char delimiter, char quote_char) { + std::array ret = make_parse_flags(delimiter); + ret[(size_t)quote_char + 128] = ParseFlags::QUOTE; + return ret; + } + + /** Create a vector v where each index i corresponds to the + * ASCII number for a character c and, v[i + 128] is true if + * c is a whitespace character + */ + HEDLEY_CONST CONSTEXPR_17 WhitespaceMap make_ws_flags(const char* ws_chars, size_t n_chars) { + std::array ret = {}; + for (int i = -128; i < 128; i++) { + const int arr_idx = i + 128; + char ch = char(i); + ret[arr_idx] = false; + + for (size_t j = 0; j < n_chars; j++) { + if (ws_chars[j] == ch) { + ret[arr_idx] = true; + } + } + } + + return ret; + } + + inline WhitespaceMap make_ws_flags(const std::vector& flags) { + return make_ws_flags(flags.data(), flags.size()); + } + + CSV_INLINE size_t get_file_size(csv::string_view filename); + + CSV_INLINE std::string get_csv_head(csv::string_view filename); + + /** Read the first 500KB of a CSV file */ + CSV_INLINE std::string get_csv_head(csv::string_view filename, size_t file_size); + + /** A std::deque wrapper which allows multiple read and write threads to concurrently + * access it along with providing read threads the ability to wait for the deque + * to become populated + */ + template + class ThreadSafeDeque { + public: + ThreadSafeDeque(size_t notify_size = 100) : _notify_size(notify_size) {}; + ThreadSafeDeque(const ThreadSafeDeque& other) { + this->data = other.data; + this->_notify_size = other._notify_size; + } + + ThreadSafeDeque(const std::deque& source) : ThreadSafeDeque() { + this->data = source; + } + + void clear() noexcept { this->data.clear(); } + + bool empty() const noexcept { + return this->data.empty(); + } + + T& front() noexcept { + return this->data.front(); + } + + T& operator[](size_t n) { + return this->data[n]; + } + + void push_back(T&& item) { + std::lock_guard lock{ this->_lock }; + this->data.push_back(std::move(item)); + + if (this->size() >= _notify_size) { + this->_cond.notify_all(); + } + } + + T pop_front() noexcept { + std::lock_guard lock{ this->_lock }; + T item = std::move(data.front()); + data.pop_front(); + return item; + } + + size_t size() const noexcept { return this->data.size(); } + + /** Returns true if a thread is actively pushing items to this deque */ + constexpr bool is_waitable() const noexcept { return this->_is_waitable; } + + /** Wait for an item to become available */ + void wait() { + if (!is_waitable()) { + return; + } + + std::unique_lock lock{ this->_lock }; + this->_cond.wait(lock, [this] { return this->size() >= _notify_size || !this->is_waitable(); }); + lock.unlock(); + } + + typename std::deque::iterator begin() noexcept { + return this->data.begin(); + } + + typename std::deque::iterator end() noexcept { + return this->data.end(); + } + + /** Tell listeners that this deque is actively being pushed to */ + void notify_all() { + std::unique_lock lock{ this->_lock }; + this->_is_waitable = true; + this->_cond.notify_all(); + } + + /** Tell all listeners to stop */ + void kill_all() { + std::unique_lock lock{ this->_lock }; + this->_is_waitable = false; + this->_cond.notify_all(); + } + + private: + bool _is_waitable = false; + size_t _notify_size; + std::mutex _lock; + std::condition_variable _cond; + std::deque data; + }; + + constexpr const int UNINITIALIZED_FIELD = -1; + } + + /** Standard type for storing collection of rows */ + using RowCollection = internals::ThreadSafeDeque; + + namespace internals { + /** Abstract base class which provides CSV parsing logic. + * + * Concrete implementations may customize this logic across + * different input sources, such as memory mapped files, stringstreams, + * etc... + */ + class IBasicCSVParser { + public: + IBasicCSVParser() = default; + IBasicCSVParser(const CSVFormat&, const ColNamesPtr&); + IBasicCSVParser(const ParseFlagMap& parse_flags, const WhitespaceMap& ws_flags + ) : _parse_flags(parse_flags), _ws_flags(ws_flags) {}; + + virtual ~IBasicCSVParser() {} + + /** Whether or not we have reached the end of source */ + bool eof() { return this->_eof; } + + /** Parse the next block of data */ + virtual void next(size_t bytes) = 0; + + /** Indicate the last block of data has been parsed */ + void end_feed(); + + CONSTEXPR_17 ParseFlags parse_flag(const char ch) const noexcept { + return _parse_flags.data()[ch + 128]; + } + + CONSTEXPR_17 ParseFlags compound_parse_flag(const char ch) const noexcept { + return quote_escape_flag(parse_flag(ch), this->quote_escape); + } + + /** Whether or not this CSV has a UTF-8 byte order mark */ + CONSTEXPR bool utf8_bom() const { return this->_utf8_bom; } + + void set_output(RowCollection& rows) { this->_records = &rows; } + + protected: + /** @name Current Parser State */ + ///@{ + CSVRow current_row; + RawCSVDataPtr data_ptr = nullptr; + ColNamesPtr _col_names = nullptr; + CSVFieldList* fields = nullptr; + int field_start = UNINITIALIZED_FIELD; + size_t field_length = 0; + + /** An array where the (i + 128)th slot gives the ParseFlags for ASCII character i */ + ParseFlagMap _parse_flags; + ///@} + + /** @name Current Stream/File State */ + ///@{ + bool _eof = false; + + /** The size of the incoming CSV */ + size_t source_size = 0; + ///@} + + /** Whether or not source needs to be read in chunks */ + CONSTEXPR bool no_chunk() const { return this->source_size < ITERATION_CHUNK_SIZE; } + + /** Parse the current chunk of data * + * + * @returns How many character were read that are part of complete rows + */ + size_t parse(); + + /** Create a new RawCSVDataPtr for a new chunk of data */ + void reset_data_ptr(); + private: + /** An array where the (i + 128)th slot determines whether ASCII character i should + * be trimmed + */ + WhitespaceMap _ws_flags; + bool quote_escape = false; + bool field_has_double_quote = false; + + /** Where we are in the current data block */ + size_t data_pos = 0; + + /** Whether or not an attempt to find Unicode BOM has been made */ + bool unicode_bom_scan = false; + bool _utf8_bom = false; + + /** Where complete rows should be pushed to */ + RowCollection* _records = nullptr; + + CONSTEXPR_17 bool ws_flag(const char ch) const noexcept { + return _ws_flags.data()[ch + 128]; + } + + size_t& current_row_start() { + return this->current_row.data_start; + } + + void parse_field() noexcept; + + /** Finish parsing the current field */ + void push_field(); + + /** Finish parsing the current row */ + void push_row(); + + /** Handle possible Unicode byte order mark */ + void trim_utf8_bom(); + }; + + /** A class for parsing CSV data from a `std::stringstream` + * or an `std::ifstream` + */ + template + class StreamParser: public IBasicCSVParser { + using RowCollection = ThreadSafeDeque; + + public: + StreamParser(TStream& source, + const CSVFormat& format, + const ColNamesPtr& col_names = nullptr + ) : IBasicCSVParser(format, col_names), _source(std::move(source)) {}; + + StreamParser( + TStream& source, + internals::ParseFlagMap parse_flags, + internals::WhitespaceMap ws_flags) : + IBasicCSVParser(parse_flags, ws_flags), + _source(std::move(source)) + {}; + + ~StreamParser() {} + + void next(size_t bytes = ITERATION_CHUNK_SIZE) override { + if (this->eof()) return; + + this->reset_data_ptr(); + this->data_ptr->_data = std::make_shared(); + + if (source_size == 0) { + const auto start = _source.tellg(); + _source.seekg(0, std::ios::end); + const auto end = _source.tellg(); + _source.seekg(0, std::ios::beg); + + source_size = end - start; + } + + // Read data into buffer + size_t length = std::min(source_size - stream_pos, bytes); + std::unique_ptr buff(new char[length]); + _source.seekg(stream_pos, std::ios::beg); + _source.read(buff.get(), length); + stream_pos = _source.tellg(); + ((std::string*)(this->data_ptr->_data.get()))->assign(buff.get(), length); + + // Create string_view + this->data_ptr->data = *((std::string*)this->data_ptr->_data.get()); + + // Parse + this->current_row = CSVRow(this->data_ptr); + size_t remainder = this->parse(); + + if (stream_pos == source_size || no_chunk()) { + this->_eof = true; + this->end_feed(); + } + else { + this->stream_pos -= (length - remainder); + } + } + + private: + TStream _source; + size_t stream_pos = 0; + }; + + /** Parser for memory-mapped files + * + * @par Implementation + * This class constructs moving windows over a file to avoid + * creating massive memory maps which may require more RAM + * than the user has available. It contains logic to automatically + * re-align each memory map to the beginning of a CSV row. + * + */ + class MmapParser : public IBasicCSVParser { + public: + MmapParser(csv::string_view filename, + const CSVFormat& format, + const ColNamesPtr& col_names = nullptr + ) : IBasicCSVParser(format, col_names) { + this->_filename = filename.data(); + this->source_size = get_file_size(filename); + }; + + ~MmapParser() {} + + void next(size_t bytes) override; + + private: + std::string _filename; + size_t mmap_pos = 0; + }; + } +} + + +/** The all encompassing namespace */ +namespace csv { + /** Stuff that is generally not of interest to end-users */ + namespace internals { + std::string format_row(const std::vector& row, csv::string_view delim = ", "); + + std::vector _get_col_names( csv::string_view head, const CSVFormat format = CSVFormat::guess_csv()); + + struct GuessScore { + double score; + size_t header; + }; + + CSV_INLINE GuessScore calculate_score(csv::string_view head, CSVFormat format); + + CSVGuessResult _guess_format(csv::string_view head, const std::vector& delims = { ',', '|', '\t', ';', '^', '~' }); + } + + std::vector get_col_names( + csv::string_view filename, + const CSVFormat format = CSVFormat::guess_csv()); + + /** Guess the delimiter used by a delimiter-separated values file */ + CSVGuessResult guess_format(csv::string_view filename, + const std::vector& delims = { ',', '|', '\t', ';', '^', '~' }); + + /** @class CSVReader + * @brief Main class for parsing CSVs from files and in-memory sources + * + * All rows are compared to the column names for length consistency + * - By default, rows that are too short or too long are dropped + * - Custom behavior can be defined by overriding bad_row_handler in a subclass + */ + class CSVReader { + public: + /** + * An input iterator capable of handling large files. + * @note Created by CSVReader::begin() and CSVReader::end(). + * + * @par Iterating over a file + * @snippet tests/test_csv_iterator.cpp CSVReader Iterator 1 + * + * @par Using with `` library + * @snippet tests/test_csv_iterator.cpp CSVReader Iterator 2 + */ + class iterator { + public: + #ifndef DOXYGEN_SHOULD_SKIP_THIS + using value_type = CSVRow; + using difference_type = std::ptrdiff_t; + using pointer = CSVRow * ; + using reference = CSVRow & ; + using iterator_category = std::input_iterator_tag; + #endif + + iterator() = default; + iterator(CSVReader* reader) : daddy(reader) {}; + iterator(CSVReader*, CSVRow&&); + + /** Access the CSVRow held by the iterator */ + CONSTEXPR_14 reference operator*() { return this->row; } + + /** Return a pointer to the CSVRow the iterator has stopped at */ + CONSTEXPR_14 pointer operator->() { return &(this->row); } + + iterator& operator++(); /**< Pre-increment iterator */ + iterator operator++(int); /**< Post-increment ierator */ + iterator& operator--(); + + /** Returns true if iterators were constructed from the same CSVReader + * and point to the same row + */ + CONSTEXPR bool operator==(const iterator& other) const noexcept { + return (this->daddy == other.daddy) && (this->i == other.i); + } + + CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); } + private: + CSVReader * daddy = nullptr; // Pointer to parent + CSVRow row; // Current row + size_t i = 0; // Index of current row + }; + + /** @name Constructors + * Constructors for iterating over large files and parsing in-memory sources. + */ + ///@{ + CSVReader(csv::string_view filename, CSVFormat format = CSVFormat::guess_csv()); + + /** Allows parsing stream sources such as `std::stringstream` or `std::ifstream` + * + * @tparam TStream An input stream deriving from `std::istream` + * @note Currently this constructor requires special CSV dialects to be manually + * specified. + */ + template::value, int> = 0> + CSVReader(TStream& source, CSVFormat format = CSVFormat()) : _format(format) { + using Parser = internals::StreamParser; + + if (!format.col_names.empty()) + this->set_col_names(format.col_names); + + this->parser = std::unique_ptr( + new Parser(source, format, col_names)); // For C++11 + this->initial_read(); + } + ///@} + + CSVReader(const CSVReader&) = delete; // No copy constructor + CSVReader(CSVReader&&) = default; // Move constructor + CSVReader& operator=(const CSVReader&) = delete; // No copy assignment + CSVReader& operator=(CSVReader&& other) = default; + ~CSVReader() { + if (this->read_csv_worker.joinable()) { + this->read_csv_worker.join(); + } + } + + /** @name Retrieving CSV Rows */ + ///@{ + bool read_row(CSVRow &row); + iterator begin(); + HEDLEY_CONST iterator end() const noexcept; + + /** Returns true if we have reached end of file */ + bool eof() const noexcept { return this->parser->eof(); }; + ///@} + + /** @name CSV Metadata */ + ///@{ + CSVFormat get_format() const; + std::vector get_col_names() const; + int index_of(csv::string_view col_name) const; + ///@} + + /** @name CSV Metadata: Attributes */ + ///@{ + /** Whether or not the file or stream contains valid CSV rows, + * not including the header. + * + * @note Gives an accurate answer regardless of when it is called. + * + */ + CONSTEXPR bool empty() const noexcept { return this->n_rows() == 0; } + + /** Retrieves the number of rows that have been read so far */ + CONSTEXPR size_t n_rows() const noexcept { return this->_n_rows; } + + /** Whether or not CSV was prefixed with a UTF-8 bom */ + bool utf8_bom() const noexcept { return this->parser->utf8_bom(); } + ///@} + + protected: + /** + * \defgroup csv_internal CSV Parser Internals + * @brief Internals of CSVReader. Only maintainers and those looking to + * extend the parser should read this. + * @{ + */ + + /** Sets this reader's column names and associated data */ + void set_col_names(const std::vector&); + + /** @name CSV Settings **/ + ///@{ + CSVFormat _format; + ///@} + + /** @name Parser State */ + ///@{ + /** Pointer to a object containing column information */ + internals::ColNamesPtr col_names = std::make_shared(); + + /** Helper class which actually does the parsing */ + std::unique_ptr parser = nullptr; + + /** Queue of parsed CSV rows */ + std::unique_ptr records{new RowCollection(100)}; + + size_t n_cols = 0; /**< The number of columns in this CSV */ + size_t _n_rows = 0; /**< How many rows (minus header) have been read so far */ + + /** @name Multi-Threaded File Reading Functions */ + ///@{ + bool read_csv(size_t bytes = internals::ITERATION_CHUNK_SIZE); + ///@} + + /**@}*/ + + private: + /** Whether or not rows before header were trimmed */ + bool header_trimmed = false; + + /** @name Multi-Threaded File Reading: Flags and State */ + ///@{ + std::thread read_csv_worker; /**< Worker thread for read_csv() */ + ///@} + + /** Read initial chunk to get metadata */ + void initial_read() { + this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE); + this->read_csv_worker.join(); + } + + void trim_header(); + }; +} + +/** @file + * Calculates statistics from CSV files + */ + +#include +#include +#include + +namespace csv { + /** Class for calculating statistics from CSV files and in-memory sources + * + * **Example** + * \include programs/csv_stats.cpp + * + */ + class CSVStat { + public: + using FreqCount = std::unordered_map; + using TypeCount = std::unordered_map; + + std::vector get_mean() const; + std::vector get_variance() const; + std::vector get_mins() const; + std::vector get_maxes() const; + std::vector get_counts() const; + std::vector get_dtypes() const; + + std::vector get_col_names() const { + return this->reader.get_col_names(); + } + + CSVStat(csv::string_view filename, CSVFormat format = CSVFormat::guess_csv()); + CSVStat(std::stringstream& source, CSVFormat format = CSVFormat()); + private: + // An array of rolling averages + // Each index corresponds to the rolling mean for the column at said index + std::vector rolling_means; + std::vector rolling_vars; + std::vector mins; + std::vector maxes; + std::vector counts; + std::vector dtypes; + std::vector n; + + // Statistic calculators + void variance(const long double&, const size_t&); + void count(CSVField&, const size_t&); + void min_max(const long double&, const size_t&); + void dtype(CSVField&, const size_t&); + + void calc(); + void calc_chunk(); + void calc_worker(const size_t&); + + CSVReader reader; + std::deque records = {}; + }; +} + +#include +#include +#include + +namespace csv { + /** Returned by get_file_info() */ + struct CSVFileInfo { + std::string filename; /**< Filename */ + std::vector col_names; /**< CSV column names */ + char delim; /**< Delimiting character */ + size_t n_rows; /**< Number of rows in a file */ + size_t n_cols; /**< Number of columns in a CSV */ + }; + + /** @name Shorthand Parsing Functions + * @brief Convienience functions for parsing small strings + */ + ///@{ + CSVReader operator ""_csv(const char*, size_t); + CSVReader operator ""_csv_no_header(const char*, size_t); + CSVReader parse(csv::string_view in, CSVFormat format = CSVFormat()); + CSVReader parse_no_header(csv::string_view in); + ///@} + + /** @name Utility Functions */ + ///@{ + std::unordered_map csv_data_types(const std::string&); + CSVFileInfo get_file_info(const std::string& filename); + int get_col_pos(csv::string_view filename, csv::string_view col_name, + const CSVFormat& format = CSVFormat::guess_csv()); + ///@} +} +/** @file + * A standalone header file for writing delimiter-separated files + */ + +#include +#include +#include +#include +#include +#include + + +namespace csv { + namespace internals { + static int DECIMAL_PLACES = 5; + + /** to_string() for unsigned integers */ + template::value, int> = 0> + inline std::string to_string(T value) { + std::string digits_reverse = ""; + + if (value == 0) return "0"; + + while (value > 0) { + digits_reverse += (char)('0' + (value % 10)); + value /= 10; + } + + return std::string(digits_reverse.rbegin(), digits_reverse.rend()); + } + + /** to_string() for signed integers */ + template< + typename T, + csv::enable_if_t::value && std::is_signed::value, int> = 0 + > + inline std::string to_string(T value) { + if (value >= 0) + return to_string((size_t)value); + + return "-" + to_string((size_t)(value * -1)); + } + + /** to_string() for floating point numbers */ + template< + typename T, + csv::enable_if_t::value, int> = 0 + > + inline std::string to_string(T value) { + std::string result; + + T integral_part; + T fractional_part = std::abs(std::modf(value, &integral_part)); + integral_part = std::abs(integral_part); + + // Integral part + if (value < 0) result = "-"; + + if (integral_part == 0) { + result = "0"; + } + else { + for (int n_digits = (int)(std::log(integral_part) / std::log(10)); + n_digits + 1 > 0; n_digits --) { + int digit = (int)(std::fmod(integral_part, pow10(n_digits + 1)) / pow10(n_digits)); + result += (char)('0' + digit); + } + } + + // Decimal part + result += "."; + + if (fractional_part > 0) { + fractional_part *= (T)(pow10(DECIMAL_PLACES)); + for (int n_digits = DECIMAL_PLACES; n_digits > 0; n_digits--) { + int digit = (int)(std::fmod(fractional_part, pow10(n_digits)) / pow10(n_digits - 1)); + result += (char)('0' + digit); + } + } + else { + result += "0"; + } + + return result; + } + } + + /** Sets how many places after the decimal will be written for floating point numbers + * + * @param precision Number of decimal places + */ + inline static void set_decimal_places(int precision) { + internals::DECIMAL_PLACES = precision; + } + + /** @name CSV Writing */ + ///@{ + /** + * Class for writing delimiter separated values files + * + * To write formatted strings, one should + * -# Initialize a DelimWriter with respect to some output stream + * -# Call write_row() on std::vectors of unformatted text + * + * @tparam OutputStream The output stream, e.g. `std::ofstream`, `std::stringstream` + * @tparam Delim The delimiter character + * @tparam Quote The quote character + * @tparam Flush True: flush after every writing function, + * false: you need to flush explicitly if needed. + * In both cases the destructor will flush. + * + * @par Hint + * Use the aliases csv::CSVWriter to write CSV + * formatted strings and csv::TSVWriter + * to write tab separated strings + * + * @par Example w/ std::vector, std::deque, std::list + * @snippet test_write_csv.cpp CSV Writer Example + * + * @par Example w/ std::tuple + * @snippet test_write_csv.cpp CSV Writer Tuple Example + */ + template + class DelimWriter { + public: + /** Construct a DelimWriter over the specified output stream + * + * @param _out Stream to write to + * @param _quote_minimal Limit field quoting to only when necessary + */ + + DelimWriter(OutputStream& _out, bool _quote_minimal = true) + : out(_out), quote_minimal(_quote_minimal) {}; + + /** Construct a DelimWriter over the file + * + * @param[out] filename File to write to + */ + DelimWriter(const std::string& filename) : DelimWriter(std::ifstream(filename)) {}; + + /** Destructor will flush remaining data + * + */ + ~DelimWriter() { + out.flush(); + } + + /** Format a sequence of strings and write to CSV according to RFC 4180 + * + * @warning This does not check to make sure row lengths are consistent + * + * @param[in] record Sequence of strings to be formatted + * + * @return The current DelimWriter instance (allowing for operator chaining) + */ + template + DelimWriter& operator<<(const std::array& record) { + for (size_t i = 0; i < Size; i++) { + out << csv_escape(record[i]); + if (i + 1 != Size) out << Delim; + } + + end_out(); + return *this; + } + + /** @copydoc operator<< */ + template + DelimWriter& operator<<(const std::tuple& record) { + this->write_tuple<0, T...>(record); + return *this; + } + + /** + * @tparam T A container such as std::vector, std::deque, or std::list + * + * @copydoc operator<< + */ + template< + typename T, typename Alloc, template class Container, + + // Avoid conflicting with tuples with two elements + csv::enable_if_t::value, int> = 0 + > + DelimWriter& operator<<(const Container& record) { + const size_t ilen = record.size(); + size_t i = 0; + for (const auto& field : record) { + out << csv_escape(field); + if (i + 1 != ilen) out << Delim; + i++; + } + + end_out(); + return *this; + } + + /** Flushes the written data + * + */ + void flush() { + out.flush(); + } + + private: + template< + typename T, + csv::enable_if_t< + !std::is_convertible::value + && !std::is_convertible::value + , int> = 0 + > + std::string csv_escape(T in) { + return internals::to_string(in); + } + + template< + typename T, + csv::enable_if_t< + std::is_convertible::value + || std::is_convertible::value + , int> = 0 + > + std::string csv_escape(T in) { + IF_CONSTEXPR(std::is_convertible::value) { + return _csv_escape(in); + } + + return _csv_escape(std::string(in)); + } + + std::string _csv_escape(csv::string_view in) { + /** Format a string to be RFC 4180-compliant + * @param[in] in String to be CSV-formatted + * @param[out] quote_minimal Only quote fields if necessary. + * If False, everything is quoted. + */ + + // Do we need a quote escape + bool quote_escape = false; + + for (auto ch : in) { + if (ch == Quote || ch == Delim || ch == '\r' || ch == '\n') { + quote_escape = true; + break; + } + } + + if (!quote_escape) { + if (quote_minimal) return std::string(in); + else { + std::string ret(1, Quote); + ret += in.data(); + ret += Quote; + return ret; + } + } + + // Start initial quote escape sequence + std::string ret(1, Quote); + for (auto ch: in) { + if (ch == Quote) ret += std::string(2, Quote); + else ret += ch; + } + + // Finish off quote escape + ret += Quote; + return ret; + } + + /** Recurisve template for writing std::tuples */ + template + typename std::enable_if::type write_tuple(const std::tuple& record) { + out << csv_escape(std::get(record)); + + IF_CONSTEXPR (Index + 1 < sizeof...(T)) out << Delim; + + this->write_tuple(record); + } + + /** Base case for writing std::tuples */ + template + typename std::enable_if::type write_tuple(const std::tuple& record) { + (void)record; + end_out(); + } + + /** Ends a line in 'out' and flushes, if Flush is true.*/ + void end_out() { + out << '\n'; + IF_CONSTEXPR(Flush) out.flush(); + } + + OutputStream & out; + bool quote_minimal; + }; + + /** An alias for csv::DelimWriter for writing standard CSV files + * + * @sa csv::DelimWriter::operator<<() + * + * @note Use `csv::make_csv_writer()` to in instatiate this class over + * an actual output stream. + */ + template + using CSVWriter = DelimWriter; + + /** Class for writing tab-separated values files + * + * @sa csv::DelimWriter::write_row() + * @sa csv::DelimWriter::operator<<() + * + * @note Use `csv::make_tsv_writer()` to in instatiate this class over + * an actual output stream. + */ + template + using TSVWriter = DelimWriter; + + /** Return a csv::CSVWriter over the output stream */ + template + inline CSVWriter make_csv_writer(OutputStream& out, bool quote_minimal=true) { + return CSVWriter(out, quote_minimal); + } + + /** Return a buffered csv::CSVWriter over the output stream (does not auto flush) */ + template + inline CSVWriter make_csv_writer_buffered(OutputStream& out, bool quote_minimal=true) { + return CSVWriter(out, quote_minimal); + } + + /** Return a csv::TSVWriter over the output stream */ + template + inline TSVWriter make_tsv_writer(OutputStream& out, bool quote_minimal=true) { + return TSVWriter(out, quote_minimal); + } + + /** Return a buffered csv::TSVWriter over the output stream (does not auto flush) */ + template + inline TSVWriter make_tsv_writer_buffered(OutputStream& out, bool quote_minimal=true) { + return TSVWriter(out, quote_minimal); + } + ///@} +} + + +namespace csv { + namespace internals { + CSV_INLINE size_t get_file_size(csv::string_view filename) { + std::ifstream infile(std::string(filename), std::ios::binary); + const auto start = infile.tellg(); + infile.seekg(0, std::ios::end); + const auto end = infile.tellg(); + + return end - start; + } + + CSV_INLINE std::string get_csv_head(csv::string_view filename) { + return get_csv_head(filename, get_file_size(filename)); + } + + CSV_INLINE std::string get_csv_head(csv::string_view filename, size_t file_size) { + const size_t bytes = 500000; + + std::error_code error; + size_t length = std::min((size_t)file_size, bytes); + auto mmap = mio::make_mmap_source(std::string(filename), 0, length, error); + + if (error) { + throw std::runtime_error("Cannot open file " + std::string(filename)); + } + + return std::string(mmap.begin(), mmap.end()); + } + +#ifdef _MSC_VER +#pragma region IBasicCVParser +#endif + CSV_INLINE IBasicCSVParser::IBasicCSVParser( + const CSVFormat& format, + const ColNamesPtr& col_names + ) : _col_names(col_names) { + if (format.no_quote) { + _parse_flags = internals::make_parse_flags(format.get_delim()); + } + else { + _parse_flags = internals::make_parse_flags(format.get_delim(), format.quote_char); + } + + _ws_flags = internals::make_ws_flags( + format.trim_chars.data(), format.trim_chars.size() + ); + } + + CSV_INLINE void IBasicCSVParser::end_feed() { + using internals::ParseFlags; + + bool empty_last_field = this->data_ptr + && this->data_ptr->_data + && !this->data_ptr->data.empty() + && parse_flag(this->data_ptr->data.back()) == ParseFlags::DELIMITER; + + // Push field + if (this->field_length > 0 || empty_last_field) { + this->push_field(); + } + + // Push row + if (this->current_row.size() > 0) + this->push_row(); + } + + CSV_INLINE void IBasicCSVParser::parse_field() noexcept { + using internals::ParseFlags; + auto& in = this->data_ptr->data; + + // Trim off leading whitespace + while (data_pos < in.size() && ws_flag(in[data_pos])) + data_pos++; + + if (field_start == UNINITIALIZED_FIELD) + field_start = (int)(data_pos - current_row_start()); + + // Optimization: Since NOT_SPECIAL characters tend to occur in contiguous + // sequences, use the loop below to avoid having to go through the outer + // switch statement as much as possible + while (data_pos < in.size() && compound_parse_flag(in[data_pos]) == ParseFlags::NOT_SPECIAL) + data_pos++; + + field_length = data_pos - (field_start + current_row_start()); + + // Trim off trailing whitespace, this->field_length constraint matters + // when field is entirely whitespace + for (size_t j = data_pos - 1; ws_flag(in[j]) && this->field_length > 0; j--) + this->field_length--; + } + + CSV_INLINE void IBasicCSVParser::push_field() + { + // Update + if (field_has_double_quote) { + fields->emplace_back( + field_start == UNINITIALIZED_FIELD ? 0 : (unsigned int)field_start, + field_length, + true + ); + field_has_double_quote = false; + + } + else { + fields->emplace_back( + field_start == UNINITIALIZED_FIELD ? 0 : (unsigned int)field_start, + field_length + ); + } + + current_row.row_length++; + + // Reset field state + field_start = UNINITIALIZED_FIELD; + field_length = 0; + } + + /** @return The number of characters parsed that belong to complete rows */ + CSV_INLINE size_t IBasicCSVParser::parse() + { + using internals::ParseFlags; + + this->quote_escape = false; + this->data_pos = 0; + this->current_row_start() = 0; + this->trim_utf8_bom(); + + auto& in = this->data_ptr->data; + while (this->data_pos < in.size()) { + switch (compound_parse_flag(in[this->data_pos])) { + case ParseFlags::DELIMITER: + this->push_field(); + this->data_pos++; + break; + + case ParseFlags::NEWLINE: + this->data_pos++; + + // Catches CRLF (or LFLF) + if (this->data_pos < in.size() && parse_flag(in[this->data_pos]) == ParseFlags::NEWLINE) + this->data_pos++; + + // End of record -> Write record + this->push_field(); + this->push_row(); + + // Reset + this->current_row = CSVRow(data_ptr, this->data_pos, fields->size()); + break; + + case ParseFlags::NOT_SPECIAL: + this->parse_field(); + break; + + case ParseFlags::QUOTE_ESCAPE_QUOTE: + if (data_pos + 1 == in.size()) return this->current_row_start(); + else if (data_pos + 1 < in.size()) { + auto next_ch = parse_flag(in[data_pos + 1]); + if (next_ch >= ParseFlags::DELIMITER) { + quote_escape = false; + data_pos++; + break; + } + else if (next_ch == ParseFlags::QUOTE) { + // Case: Escaped quote + data_pos += 2; + this->field_length += 2; + this->field_has_double_quote = true; + break; + } + } + + // Case: Unescaped single quote => not strictly valid but we'll keep it + this->field_length++; + data_pos++; + + break; + + default: // Quote (currently not quote escaped) + if (this->field_length == 0) { + quote_escape = true; + data_pos++; + if (field_start == UNINITIALIZED_FIELD && data_pos < in.size() && !ws_flag(in[data_pos])) + field_start = (int)(data_pos - current_row_start()); + break; + } + + // Case: Unescaped quote + this->field_length++; + data_pos++; + + break; + } + } + + return this->current_row_start(); + } + + CSV_INLINE void IBasicCSVParser::push_row() { + current_row.row_length = fields->size() - current_row.fields_start; + this->_records->push_back(std::move(current_row)); + } + + CSV_INLINE void IBasicCSVParser::reset_data_ptr() { + this->data_ptr = std::make_shared(); + this->data_ptr->parse_flags = this->_parse_flags; + this->data_ptr->col_names = this->_col_names; + this->fields = &(this->data_ptr->fields); + } + + CSV_INLINE void IBasicCSVParser::trim_utf8_bom() { + auto& data = this->data_ptr->data; + + if (!this->unicode_bom_scan && data.size() >= 3) { + if (data[0] == '\xEF' && data[1] == '\xBB' && data[2] == '\xBF') { + this->data_pos += 3; // Remove BOM from input string + this->_utf8_bom = true; + } + + this->unicode_bom_scan = true; + } + } +#ifdef _MSC_VER +#pragma endregion +#endif + +#ifdef _MSC_VER +#pragma region Specializations +#endif + CSV_INLINE void MmapParser::next(size_t bytes = ITERATION_CHUNK_SIZE) { + // Reset parser state + this->field_start = UNINITIALIZED_FIELD; + this->field_length = 0; + this->reset_data_ptr(); + + // Create memory map + size_t length = std::min(this->source_size - this->mmap_pos, bytes); + std::error_code error; + this->data_ptr->_data = std::make_shared>(mio::make_mmap_source(this->_filename, this->mmap_pos, length, error)); + this->mmap_pos += length; + if (error) throw error; + + auto mmap_ptr = (mio::basic_mmap_source*)(this->data_ptr->_data.get()); + + // Create string view + this->data_ptr->data = csv::string_view(mmap_ptr->data(), mmap_ptr->length()); + + // Parse + this->current_row = CSVRow(this->data_ptr); + size_t remainder = this->parse(); + + if (this->mmap_pos == this->source_size || no_chunk()) { + this->_eof = true; + this->end_feed(); + } + + this->mmap_pos -= (length - remainder); + } +#ifdef _MSC_VER +#pragma endregion +#endif + } +} + + +namespace csv { + namespace internals { + CSV_INLINE std::vector ColNames::get_col_names() const { + return this->col_names; + } + + CSV_INLINE void ColNames::set_col_names(const std::vector& cnames) { + this->col_names = cnames; + + for (size_t i = 0; i < cnames.size(); i++) { + this->col_pos[cnames[i]] = i; + } + } + + CSV_INLINE int ColNames::index_of(csv::string_view col_name) const { + auto pos = this->col_pos.find(col_name.data()); + if (pos != this->col_pos.end()) + return (int)pos->second; + + return CSV_NOT_FOUND; + } + + CSV_INLINE size_t ColNames::size() const noexcept { + return this->col_names.size(); + } + + } +} +/** @file + * Defines an object used to store CSV format settings + */ + +#include +#include + + +namespace csv { + CSV_INLINE CSVFormat& CSVFormat::delimiter(char delim) { + this->possible_delimiters = { delim }; + this->assert_no_char_overlap(); + return *this; + } + + CSV_INLINE CSVFormat& CSVFormat::delimiter(const std::vector & delim) { + this->possible_delimiters = delim; + this->assert_no_char_overlap(); + return *this; + } + + CSV_INLINE CSVFormat& CSVFormat::quote(char quote) { + this->no_quote = false; + this->quote_char = quote; + this->assert_no_char_overlap(); + return *this; + } + + CSV_INLINE CSVFormat& CSVFormat::trim(const std::vector & chars) { + this->trim_chars = chars; + this->assert_no_char_overlap(); + return *this; + } + + CSV_INLINE CSVFormat& CSVFormat::column_names(const std::vector& names) { + this->col_names = names; + this->header = -1; + return *this; + } + + CSV_INLINE CSVFormat& CSVFormat::header_row(int row) { + if (row < 0) this->variable_column_policy = VariableColumnPolicy::KEEP; + + this->header = row; + this->col_names = {}; + return *this; + } + + CSV_INLINE void CSVFormat::assert_no_char_overlap() + { + auto delims = std::set( + this->possible_delimiters.begin(), this->possible_delimiters.end()), + trims = std::set( + this->trim_chars.begin(), this->trim_chars.end()); + + // Stores intersection of possible delimiters and trim characters + std::vector intersection = {}; + + // Find which characters overlap, if any + std::set_intersection( + delims.begin(), delims.end(), + trims.begin(), trims.end(), + std::back_inserter(intersection)); + + // Make sure quote character is not contained in possible delimiters + // or whitespace characters + if (delims.find(this->quote_char) != delims.end() || + trims.find(this->quote_char) != trims.end()) { + intersection.push_back(this->quote_char); + } + + if (!intersection.empty()) { + std::string err_msg = "There should be no overlap between the quote character, " + "the set of possible delimiters " + "and the set of whitespace characters. Offending characters: "; + + // Create a pretty error message with the list of overlapping + // characters + for (size_t i = 0; i < intersection.size(); i++) { + err_msg += "'"; + err_msg += intersection[i]; + err_msg += "'"; + + if (i + 1 < intersection.size()) + err_msg += ", "; + } + + throw std::runtime_error(err_msg + '.'); + } + } +} +/** @file + * @brief Defines functionality needed for basic CSV parsing + */ + + +namespace csv { + namespace internals { + CSV_INLINE std::string format_row(const std::vector& row, csv::string_view delim) { + /** Print a CSV row */ + std::stringstream ret; + for (size_t i = 0; i < row.size(); i++) { + ret << row[i]; + if (i + 1 < row.size()) ret << delim; + else ret << '\n'; + } + ret.flush(); + + return ret.str(); + } + + /** Return a CSV's column names + * + * @param[in] filename Path to CSV file + * @param[in] format Format of the CSV file + * + */ + CSV_INLINE std::vector _get_col_names(csv::string_view head, CSVFormat format) { + // Parse the CSV + auto trim_chars = format.get_trim_chars(); + std::stringstream source(head.data()); + RowCollection rows; + + StreamParser parser(source, format); + parser.set_output(rows); + parser.next(); + + return CSVRow(std::move(rows[format.get_header()])); + } + + CSV_INLINE GuessScore calculate_score(csv::string_view head, CSVFormat format) { + // Frequency counter of row length + std::unordered_map row_tally = { { 0, 0 } }; + + // Map row lengths to row num where they first occurred + std::unordered_map row_when = { { 0, 0 } }; + + // Parse the CSV + std::stringstream source(head.data()); + RowCollection rows; + + StreamParser parser(source, format); + parser.set_output(rows); + parser.next(); + + for (size_t i = 0; i < rows.size(); i++) { + auto& row = rows[i]; + + // Ignore zero-length rows + if (row.size() > 0) { + if (row_tally.find(row.size()) != row_tally.end()) { + row_tally[row.size()]++; + } + else { + row_tally[row.size()] = 1; + row_when[row.size()] = i; + } + } + } + + double final_score = 0; + size_t header_row = 0; + + // Final score is equal to the largest + // row size times rows of that size + for (auto& pair : row_tally) { + auto row_size = pair.first; + auto row_count = pair.second; + double score = (double)(row_size * row_count); + if (score > final_score) { + final_score = score; + header_row = row_when[row_size]; + } + } + + return { + final_score, + header_row + }; + } + + /** Guess the delimiter used by a delimiter-separated values file */ + CSV_INLINE CSVGuessResult _guess_format(csv::string_view head, const std::vector& delims) { + /** For each delimiter, find out which row length was most common. + * The delimiter with the longest mode row length wins. + * Then, the line number of the header row is the first row with + * the mode row length. + */ + + CSVFormat format; + size_t max_score = 0, + header = 0; + char current_delim = delims[0]; + + for (char cand_delim : delims) { + auto result = calculate_score(head, format.delimiter(cand_delim)); + + if ((size_t)result.score > max_score) { + max_score = (size_t)result.score; + current_delim = cand_delim; + header = result.header; + } + } + + return { current_delim, (int)header }; + } + } + + /** Return a CSV's column names + * + * @param[in] filename Path to CSV file + * @param[in] format Format of the CSV file + * + */ + CSV_INLINE std::vector get_col_names(csv::string_view filename, CSVFormat format) { + auto head = internals::get_csv_head(filename); + + /** Guess delimiter and header row */ + if (format.guess_delim()) { + auto guess_result = guess_format(filename, format.get_possible_delims()); + format.delimiter(guess_result.delim).header_row(guess_result.header_row); + } + + return internals::_get_col_names(head, format); + } + + /** Guess the delimiter used by a delimiter-separated values file */ + CSV_INLINE CSVGuessResult guess_format(csv::string_view filename, const std::vector& delims) { + auto head = internals::get_csv_head(filename); + return internals::_guess_format(head, delims); + } + + /** Reads an arbitrarily large CSV file using memory-mapped IO. + * + * **Details:** Reads the first block of a CSV file synchronously to get information + * such as column names and delimiting character. + * + * @param[in] filename Path to CSV file + * @param[in] format Format of the CSV file + * + * \snippet tests/test_read_csv.cpp CSVField Example + * + */ + CSV_INLINE CSVReader::CSVReader(csv::string_view filename, CSVFormat format) : _format(format) { + auto head = internals::get_csv_head(filename); + using Parser = internals::MmapParser; + + /** Guess delimiter and header row */ + if (format.guess_delim()) { + auto guess_result = internals::_guess_format(head, format.possible_delimiters); + format.delimiter(guess_result.delim); + format.header = guess_result.header_row; + this->_format = format; + } + + if (!format.col_names.empty()) + this->set_col_names(format.col_names); + + this->parser = std::unique_ptr(new Parser(filename, format, this->col_names)); // For C++11 + this->initial_read(); + } + + /** Return the format of the original raw CSV */ + CSV_INLINE CSVFormat CSVReader::get_format() const { + CSVFormat new_format = this->_format; + + // Since users are normally not allowed to set + // column names and header row simulatenously, + // we will set the backing variables directly here + new_format.col_names = this->col_names->get_col_names(); + new_format.header = this->_format.header; + + return new_format; + } + + /** Return the CSV's column names as a vector of strings. */ + CSV_INLINE std::vector CSVReader::get_col_names() const { + if (this->col_names) { + return this->col_names->get_col_names(); + } + + return std::vector(); + } + + /** Return the index of the column name if found or + * csv::CSV_NOT_FOUND otherwise. + */ + CSV_INLINE int CSVReader::index_of(csv::string_view col_name) const { + auto _col_names = this->get_col_names(); + for (size_t i = 0; i < _col_names.size(); i++) + if (_col_names[i] == col_name) return (int)i; + + return CSV_NOT_FOUND; + } + + CSV_INLINE void CSVReader::trim_header() { + if (!this->header_trimmed) { + for (int i = 0; i <= this->_format.header && !this->records->empty(); i++) { + if (i == this->_format.header && this->col_names->empty()) { + this->set_col_names(this->records->pop_front()); + } + else { + this->records->pop_front(); + } + } + + this->header_trimmed = true; + } + } + + /** + * @param[in] names Column names + */ + CSV_INLINE void CSVReader::set_col_names(const std::vector& names) + { + this->col_names->set_col_names(names); + this->n_cols = names.size(); + } + + /** + * Read a chunk of CSV data. + * + * @note This method is meant to be run on its own thread. Only one `read_csv()` thread + * should be active at a time. + * + * @param[in] bytes Number of bytes to read. + * + * @see CSVReader::read_csv_worker + * @see CSVReader::read_row() + */ + CSV_INLINE bool CSVReader::read_csv(size_t bytes) { + // Tell read_row() to listen for CSV rows + this->records->notify_all(); + + this->parser->set_output(*this->records); + this->parser->next(bytes); + + if (!this->header_trimmed) { + this->trim_header(); + } + + // Tell read_row() to stop waiting + this->records->kill_all(); + + return true; + } + + /** + * Retrieve rows as CSVRow objects, returning true if more rows are available. + * + * @par Performance Notes + * - Reads chunks of data that are csv::internals::ITERATION_CHUNK_SIZE bytes large at a time + * - For performance details, read the documentation for CSVRow and CSVField. + * + * @param[out] row The variable where the parsed row will be stored + * @see CSVRow, CSVField + * + * **Example:** + * \snippet tests/test_read_csv.cpp CSVField Example + * + */ + CSV_INLINE bool CSVReader::read_row(CSVRow &row) { + while (true) { + if (this->records->empty()) { + if (this->records->is_waitable()) + // Reading thread is currently active => wait for it to populate records + this->records->wait(); + else if (this->parser->eof()) + // End of file and no more records + return false; + else { + // Reading thread is not active => start another one + if (this->read_csv_worker.joinable()) + this->read_csv_worker.join(); + + this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE); + } + } + else if (this->records->front().size() != this->n_cols && + this->_format.variable_column_policy != VariableColumnPolicy::KEEP) { + auto errored_row = this->records->pop_front(); + + if (this->_format.variable_column_policy == VariableColumnPolicy::THROW) { + if (errored_row.size() < this->n_cols) + throw std::runtime_error("Line too short " + internals::format_row(errored_row)); + + throw std::runtime_error("Line too long " + internals::format_row(errored_row)); + } + } + else { + row = this->records->pop_front(); + this->_n_rows++; + return true; + } + } + + return false; + } +} + +/** @file + * Defines an input iterator for csv::CSVReader + */ + + +namespace csv { + /** Return an iterator to the first row in the reader */ + CSV_INLINE CSVReader::iterator CSVReader::begin() { + if (this->records->empty()) { + this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE); + this->read_csv_worker.join(); + + // Still empty => return end iterator + if (this->records->empty()) return this->end(); + } + + CSVReader::iterator ret(this, this->records->pop_front()); + return ret; + } + + /** A placeholder for the imaginary past the end row in a CSV. + * Attempting to deference this will lead to bad things. + */ + CSV_INLINE HEDLEY_CONST CSVReader::iterator CSVReader::end() const noexcept { + return CSVReader::iterator(); + } + + ///////////////////////// + // CSVReader::iterator // + ///////////////////////// + + CSV_INLINE CSVReader::iterator::iterator(CSVReader* _daddy, CSVRow&& _row) : + daddy(_daddy) { + row = std::move(_row); + } + + /** Advance the iterator by one row. If this CSVReader has an + * associated file, then the iterator will lazily pull more data from + * that file until the end of file is reached. + * + * @note This iterator does **not** block the thread responsible for parsing CSV. + * + */ + CSV_INLINE CSVReader::iterator& CSVReader::iterator::operator++() { + if (!daddy->read_row(this->row)) { + this->daddy = nullptr; // this == end() + } + + return *this; + } + + /** Post-increment iterator */ + CSV_INLINE CSVReader::iterator CSVReader::iterator::operator++(int) { + auto temp = *this; + if (!daddy->read_row(this->row)) { + this->daddy = nullptr; // this == end() + } + + return temp; + } +} + +/** @file + * Defines the data type used for storing information about a CSV row + */ + +#include +#include + +namespace csv { + namespace internals { + CSV_INLINE RawCSVField& CSVFieldList::operator[](size_t n) const { + const size_t page_no = n / _single_buffer_capacity; + const size_t buffer_idx = (page_no < 1) ? n : n % _single_buffer_capacity; + return this->buffers[page_no][buffer_idx]; + } + + CSV_INLINE void CSVFieldList::allocate() { + RawCSVField * buffer = new RawCSVField[_single_buffer_capacity]; + buffers.push_back(buffer); + _current_buffer_size = 0; + _back = &(buffers.back()[0]); + } + } + + /** Return a CSVField object corrsponding to the nth value in the row. + * + * @note This method performs bounds checking, and will throw an + * `std::runtime_error` if n is invalid. + * + * @complexity + * Constant, by calling csv::CSVRow::get_csv::string_view() + * + */ + CSV_INLINE CSVField CSVRow::operator[](size_t n) const { + return CSVField(this->get_field(n)); + } + + /** Retrieve a value by its associated column name. If the column + * specified can't be round, a runtime error is thrown. + * + * @complexity + * Constant. This calls the other CSVRow::operator[]() after + * converting column names into indices using a hash table. + * + * @param[in] col_name The column to look for + */ + CSV_INLINE CSVField CSVRow::operator[](const std::string& col_name) const { + auto & col_names = this->data->col_names; + auto col_pos = col_names->index_of(col_name); + if (col_pos > -1) { + return this->operator[](col_pos); + } + + throw std::runtime_error("Can't find a column named " + col_name); + } + + CSV_INLINE CSVRow::operator std::vector() const { + std::vector ret; + for (size_t i = 0; i < size(); i++) + ret.push_back(std::string(this->get_field(i))); + + return ret; + } + + CSV_INLINE csv::string_view CSVRow::get_field(size_t index) const + { + using internals::ParseFlags; + + if (index >= this->size()) + throw std::runtime_error("Index out of bounds."); + + const size_t field_index = this->fields_start + index; + auto& field = this->data->fields[field_index]; + auto field_str = csv::string_view(this->data->data).substr(this->data_start + field.start); + + if (field.has_double_quote) { + auto& value = this->data->double_quote_fields[field_index]; + if (value.empty()) { + bool prev_ch_quote = false; + for (size_t i = 0; i < field.length; i++) { + if (this->data->parse_flags[field_str[i] + 128] == ParseFlags::QUOTE) { + if (prev_ch_quote) { + prev_ch_quote = false; + continue; + } + else { + prev_ch_quote = true; + } + } + + value += field_str[i]; + } + } + + return csv::string_view(value); + } + + return field_str.substr(0, field.length); + } + + CSV_INLINE bool CSVField::try_parse_hex(int& parsedValue) { + size_t start = 0, end = 0; + + // Trim out whitespace chars + for (; start < this->sv.size() && this->sv[start] == ' '; start++); + for (end = start; end < this->sv.size() && this->sv[end] != ' '; end++); + + unsigned long long int value = 0; + + size_t digits = (end - start); + size_t base16_exponent = digits - 1; + + if (digits == 0) return false; + + for (const auto& ch : this->sv.substr(start, digits)) { + int digit = 0; + + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + digit = static_cast(ch - '0'); + break; + case 'a': + case 'A': + digit = 10; + break; + case 'b': + case 'B': + digit = 11; + break; + case 'c': + case 'C': + digit = 12; + break; + case 'd': + case 'D': + digit = 13; + break; + case 'e': + case 'E': + digit = 14; + break; + case 'f': + case 'F': + digit = 15; + break; + default: + return false; + } + + value += digit * pow(16, base16_exponent); + base16_exponent--; + } + + parsedValue = value; + return true; + } + +#ifdef _MSC_VER +#pragma region CSVRow Iterator +#endif + /** Return an iterator pointing to the first field. */ + CSV_INLINE CSVRow::iterator CSVRow::begin() const { + return CSVRow::iterator(this, 0); + } + + /** Return an iterator pointing to just after the end of the CSVRow. + * + * @warning Attempting to dereference the end iterator results + * in dereferencing a null pointer. + */ + CSV_INLINE CSVRow::iterator CSVRow::end() const noexcept { + return CSVRow::iterator(this, (int)this->size()); + } + + CSV_INLINE CSVRow::reverse_iterator CSVRow::rbegin() const noexcept { + return std::reverse_iterator(this->end()); + } + + CSV_INLINE CSVRow::reverse_iterator CSVRow::rend() const { + return std::reverse_iterator(this->begin()); + } + + CSV_INLINE HEDLEY_NON_NULL(2) + CSVRow::iterator::iterator(const CSVRow* _reader, int _i) + : daddy(_reader), i(_i) { + if (_i < (int)this->daddy->size()) + this->field = std::make_shared( + this->daddy->operator[](_i)); + else + this->field = nullptr; + } + + CSV_INLINE CSVRow::iterator::reference CSVRow::iterator::operator*() const { + return *(this->field.get()); + } + + CSV_INLINE CSVRow::iterator::pointer CSVRow::iterator::operator->() const { + // Using CSVField * as pointer type causes segfaults in MSVC debug builds + #ifdef _MSC_BUILD + return this->field; + #else + return this->field.get(); + #endif + } + + CSV_INLINE CSVRow::iterator& CSVRow::iterator::operator++() { + // Pre-increment operator + this->i++; + if (this->i < (int)this->daddy->size()) + this->field = std::make_shared( + this->daddy->operator[](i)); + else // Reached the end of row + this->field = nullptr; + return *this; + } + + CSV_INLINE CSVRow::iterator CSVRow::iterator::operator++(int) { + // Post-increment operator + auto temp = *this; + this->operator++(); + return temp; + } + + CSV_INLINE CSVRow::iterator& CSVRow::iterator::operator--() { + // Pre-decrement operator + this->i--; + this->field = std::make_shared( + this->daddy->operator[](this->i)); + return *this; + } + + CSV_INLINE CSVRow::iterator CSVRow::iterator::operator--(int) { + // Post-decrement operator + auto temp = *this; + this->operator--(); + return temp; + } + + CSV_INLINE CSVRow::iterator CSVRow::iterator::operator+(difference_type n) const { + // Allows for iterator arithmetic + return CSVRow::iterator(this->daddy, i + (int)n); + } + + CSV_INLINE CSVRow::iterator CSVRow::iterator::operator-(difference_type n) const { + // Allows for iterator arithmetic + return CSVRow::iterator::operator+(-n); + } +#ifdef _MSC_VER +#pragma endregion CSVRow Iterator +#endif +} + +/** @file + * Implements JSON serialization abilities + */ + + +namespace csv { + /* + The implementations for json_extra_space() and json_escape_string() + were modified from source code for JSON for Modern C++. + + The respective license is below: + + The code is licensed under the [MIT + License](http://opensource.org/licenses/MIT): + + Copyright © 2013-2015 Niels Lohmann. + + 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. + */ + + namespace internals { + /*! + @brief calculates the extra space to escape a JSON string + + @param[in] s the string to escape + @return the number of characters required to escape string @a s + + @complexity Linear in the length of string @a s. + */ + static std::size_t json_extra_space(csv::string_view& s) noexcept + { + std::size_t result = 0; + + + for (const auto& c : s) + { + switch (c) + { + case '"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + { + // from c (1 byte) to \x (2 bytes) + result += 1; + break; + } + + + default: + { + if (c >= 0x00 && c <= 0x1f) + { + // from c (1 byte) to \uxxxx (6 bytes) + result += 5; + } + break; + } + } + } + + + return result; + } + + CSV_INLINE std::string json_escape_string(csv::string_view s) noexcept + { + const auto space = json_extra_space(s); + if (space == 0) + { + return std::string(s); + } + + // create a result string of necessary size + std::string result(s.size() + space, '\\'); + std::size_t pos = 0; + + for (const auto& c : s) + { + switch (c) + { + // quotation mark (0x22) + case '"': + { + result[pos + 1] = '"'; + pos += 2; + break; + } + + + // reverse solidus (0x5c) + case '\\': + { + // nothing to change + pos += 2; + break; + } + + + // backspace (0x08) + case '\b': + { + result[pos + 1] = 'b'; + pos += 2; + break; + } + + + // formfeed (0x0c) + case '\f': + { + result[pos + 1] = 'f'; + pos += 2; + break; + } + + + // newline (0x0a) + case '\n': + { + result[pos + 1] = 'n'; + pos += 2; + break; + } + + + // carriage return (0x0d) + case '\r': + { + result[pos + 1] = 'r'; + pos += 2; + break; + } + + + // horizontal tab (0x09) + case '\t': + { + result[pos + 1] = 't'; + pos += 2; + break; + } + + + default: + { + if (c >= 0x00 && c <= 0x1f) + { + // print character c as \uxxxx + sprintf(&result[pos + 1], "u%04x", int(c)); + pos += 6; + // overwrite trailing null character + result[pos] = '\\'; + } + else + { + // all other characters are added as-is + result[pos++] = c; + } + break; + } + } + } + + return result; + } + } + + /** Convert a CSV row to a JSON object, i.e. + * `{"col1":"value1","col2":"value2"}` + * + * @note All strings are properly escaped. Numeric values are not quoted. + * @param[in] subset A subset of columns to contain in the JSON. + * Leave empty for original columns. + */ + CSV_INLINE std::string CSVRow::to_json(const std::vector& subset) const { + std::vector col_names = subset; + if (subset.empty()) { + col_names = this->data ? this->get_col_names() : std::vector({}); + } + + const size_t _n_cols = col_names.size(); + std::string ret = "{"; + + for (size_t i = 0; i < _n_cols; i++) { + auto& col = col_names[i]; + auto field = this->operator[](col); + + // TODO: Possible performance enhancements by caching escaped column names + ret += '"' + internals::json_escape_string(col) + "\":"; + + // Add quotes around strings but not numbers + if (field.is_num()) + ret += internals::json_escape_string(field.get()); + else + ret += '"' + internals::json_escape_string(field.get()) + '"'; + + // Do not add comma after last string + if (i + 1 < _n_cols) + ret += ','; + } + + ret += '}'; + return ret; + } + + /** Convert a CSV row to a JSON array, i.e. + * `["value1","value2",...]` + * + * @note All strings are properly escaped. Numeric values are not quoted. + * @param[in] subset A subset of columns to contain in the JSON. + * Leave empty for all columns. + */ + CSV_INLINE std::string CSVRow::to_json_array(const std::vector& subset) const { + std::vector col_names = subset; + if (subset.empty()) + col_names = this->data ? this->get_col_names() : std::vector({}); + + const size_t _n_cols = col_names.size(); + std::string ret = "["; + + for (size_t i = 0; i < _n_cols; i++) { + auto field = this->operator[](col_names[i]); + + // Add quotes around strings but not numbers + if (field.is_num()) + ret += internals::json_escape_string(field.get()); + else + ret += '"' + internals::json_escape_string(field.get()) + '"'; + + // Do not add comma after last string + if (i + 1 < _n_cols) + ret += ','; + } + + ret += ']'; + return ret; + } +} +/** @file + * Calculates statistics from CSV files + */ + +#include + +namespace csv { + /** Calculate statistics for an arbitrarily large file. When this constructor + * is called, CSVStat will process the entire file iteratively. Once finished, + * methods like get_mean(), get_counts(), etc... can be used to retrieve statistics. + */ + CSV_INLINE CSVStat::CSVStat(csv::string_view filename, CSVFormat format) : + reader(filename, format) { + this->calc(); + } + + /** Calculate statistics for a CSV stored in a std::stringstream */ + CSV_INLINE CSVStat::CSVStat(std::stringstream& stream, CSVFormat format) : + reader(stream, format) { + this->calc(); + } + + /** Return current means */ + CSV_INLINE std::vector CSVStat::get_mean() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->rolling_means[i]); + } + return ret; + } + + /** Return current variances */ + CSV_INLINE std::vector CSVStat::get_variance() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->rolling_vars[i]/(this->n[i] - 1)); + } + return ret; + } + + /** Return current mins */ + CSV_INLINE std::vector CSVStat::get_mins() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->mins[i]); + } + return ret; + } + + /** Return current maxes */ + CSV_INLINE std::vector CSVStat::get_maxes() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->maxes[i]); + } + return ret; + } + + /** Get counts for each column */ + CSV_INLINE std::vector CSVStat::get_counts() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->counts[i]); + } + return ret; + } + + /** Get data type counts for each column */ + CSV_INLINE std::vector CSVStat::get_dtypes() const { + std::vector ret; + for (size_t i = 0; i < this->get_col_names().size(); i++) { + ret.push_back(this->dtypes[i]); + } + return ret; + } + + CSV_INLINE void CSVStat::calc_chunk() { + /** Only create stats counters the first time **/ + if (dtypes.empty()) { + /** Go through all records and calculate specified statistics */ + for (size_t i = 0; i < this->get_col_names().size(); i++) { + dtypes.push_back({}); + counts.push_back({}); + rolling_means.push_back(0); + rolling_vars.push_back(0); + mins.push_back(NAN); + maxes.push_back(NAN); + n.push_back(0); + } + } + + // Start threads + std::vector pool; + for (size_t i = 0; i < this->get_col_names().size(); i++) + pool.push_back(std::thread(&CSVStat::calc_worker, this, i)); + + // Block until done + for (auto& th : pool) + th.join(); + + this->records.clear(); + } + + CSV_INLINE void CSVStat::calc() { + constexpr size_t CALC_CHUNK_SIZE = 5000; + + for (auto& row : reader) { + this->records.push_back(std::move(row)); + + /** Chunk rows */ + if (this->records.size() == CALC_CHUNK_SIZE) { + calc_chunk(); + } + } + + if (!this->records.empty()) { + calc_chunk(); + } + } + + CSV_INLINE void CSVStat::calc_worker(const size_t &i) { + /** Worker thread for CSVStat::calc() which calculates statistics for one column. + * + * @param[in] i Column index + */ + + auto current_record = this->records.begin(); + + for (size_t processed = 0; current_record != this->records.end(); processed++) { + if (current_record->size() == this->get_col_names().size()) { + auto current_field = (*current_record)[i]; + + // Optimization: Don't count() if there's too many distinct values in the first 1000 rows + if (processed < 1000 || this->counts[i].size() <= 500) + this->count(current_field, i); + + this->dtype(current_field, i); + + // Numeric Stuff + if (current_field.is_num()) { + long double x_n = current_field.get(); + + // This actually calculates mean AND variance + this->variance(x_n, i); + this->min_max(x_n, i); + } + } + else if (this->reader.get_format().get_variable_column_policy() == VariableColumnPolicy::THROW) { + throw std::runtime_error("Line has different length than the others " + internals::format_row(*current_record)); + } + + ++current_record; + } + } + + CSV_INLINE void CSVStat::dtype(CSVField& data, const size_t &i) { + /** Given a record update the type counter + * @param[in] record Data observation + * @param[out] i The column index that should be updated + */ + + auto type = data.type(); + if (this->dtypes[i].find(type) != + this->dtypes[i].end()) { + // Increment count + this->dtypes[i][type]++; + } else { + // Initialize count + this->dtypes[i].insert(std::make_pair(type, 1)); + } + } + + CSV_INLINE void CSVStat::count(CSVField& data, const size_t &i) { + /** Given a record update the frequency counter + * @param[in] record Data observation + * @param[out] i The column index that should be updated + */ + + auto item = data.get(); + + if (this->counts[i].find(item) != + this->counts[i].end()) { + // Increment count + this->counts[i][item]++; + } else { + // Initialize count + this->counts[i].insert(std::make_pair(item, 1)); + } + } + + CSV_INLINE void CSVStat::min_max(const long double &x_n, const size_t &i) { + /** Update current minimum and maximum + * @param[in] x_n Data observation + * @param[out] i The column index that should be updated + */ + if (std::isnan(this->mins[i])) + this->mins[i] = x_n; + if (std::isnan(this->maxes[i])) + this->maxes[i] = x_n; + + if (x_n < this->mins[i]) + this->mins[i] = x_n; + else if (x_n > this->maxes[i]) + this->maxes[i] = x_n; + } + + CSV_INLINE void CSVStat::variance(const long double &x_n, const size_t &i) { + /** Given a record update rolling mean and variance for all columns + * using Welford's Algorithm + * @param[in] x_n Data observation + * @param[out] i The column index that should be updated + */ + long double& current_rolling_mean = this->rolling_means[i]; + long double& current_rolling_var = this->rolling_vars[i]; + long double& current_n = this->n[i]; + long double delta; + long double delta2; + + current_n++; + + if (current_n == 1) { + current_rolling_mean = x_n; + } else { + delta = x_n - current_rolling_mean; + current_rolling_mean += delta/current_n; + delta2 = x_n - current_rolling_mean; + current_rolling_var += delta*delta2; + } + } + + /** Useful for uploading CSV files to SQL databases. + * + * Return a data type for each column such that every value in a column can be + * converted to the corresponding data type without data loss. + * @param[in] filename The CSV file + * + * \return A mapping of column names to csv::DataType enums + */ + CSV_INLINE std::unordered_map csv_data_types(const std::string& filename) { + CSVStat stat(filename); + std::unordered_map csv_dtypes; + + auto col_names = stat.get_col_names(); + auto temp = stat.get_dtypes(); + + for (size_t i = 0; i < stat.get_col_names().size(); i++) { + auto& col = temp[i]; + auto& col_name = col_names[i]; + + if (col[DataType::CSV_STRING]) + csv_dtypes[col_name] = DataType::CSV_STRING; + else if (col[DataType::CSV_INT64]) + csv_dtypes[col_name] = DataType::CSV_INT64; + else if (col[DataType::CSV_INT32]) + csv_dtypes[col_name] = DataType::CSV_INT32; + else if (col[DataType::CSV_INT16]) + csv_dtypes[col_name] = DataType::CSV_INT16; + else if (col[DataType::CSV_INT8]) + csv_dtypes[col_name] = DataType::CSV_INT8; + else + csv_dtypes[col_name] = DataType::CSV_DOUBLE; + } + + return csv_dtypes; + } +} +#include +#include + + +namespace csv { + /** Shorthand function for parsing an in-memory CSV string + * + * @return A collection of CSVRow objects + * + * @par Example + * @snippet tests/test_read_csv.cpp Parse Example + */ + CSV_INLINE CSVReader parse(csv::string_view in, CSVFormat format) { + std::stringstream stream(in.data()); + return CSVReader(stream, format); + } + + /** Parses a CSV string with no headers + * + * @return A collection of CSVRow objects + */ + CSV_INLINE CSVReader parse_no_header(csv::string_view in) { + CSVFormat format; + format.header_row(-1); + + return parse(in, format); + } + + /** Parse a RFC 4180 CSV string, returning a collection + * of CSVRow objects + * + * @par Example + * @snippet tests/test_read_csv.cpp Escaped Comma + * + */ + CSV_INLINE CSVReader operator ""_csv(const char* in, size_t n) { + return parse(csv::string_view(in, n)); + } + + /** A shorthand for csv::parse_no_header() */ + CSV_INLINE CSVReader operator ""_csv_no_header(const char* in, size_t n) { + return parse_no_header(csv::string_view(in, n)); + } + + /** + * Find the position of a column in a CSV file or CSV_NOT_FOUND otherwise + * + * @param[in] filename Path to CSV file + * @param[in] col_name Column whose position we should resolve + * @param[in] format Format of the CSV file + */ + CSV_INLINE int get_col_pos( + csv::string_view filename, + csv::string_view col_name, + const CSVFormat& format) { + CSVReader reader(filename, format); + return reader.index_of(col_name); + } + + /** Get basic information about a CSV file + * @include programs/csv_info.cpp + */ + CSV_INLINE CSVFileInfo get_file_info(const std::string& filename) { + CSVReader reader(filename); + CSVFormat format = reader.get_format(); + for (auto it = reader.begin(); it != reader.end(); ++it); + + CSVFileInfo info = { + filename, + reader.get_col_names(), + format.get_delim(), + reader.n_rows(), + reader.get_col_names().size() + }; + + return info; + } +} + + +#endif diff --git a/3rdlib/csv/csv.pri b/3rdlib/csv/csv.pri new file mode 100644 index 0000000..1d82b4d --- /dev/null +++ b/3rdlib/csv/csv.pri @@ -0,0 +1,2 @@ +INCLUDEPATH += $${PWD} +DEPENDPATH += $${PWD} \ No newline at end of file diff --git a/Common.pri b/Common.pri new file mode 100644 index 0000000..466cad6 --- /dev/null +++ b/Common.pri @@ -0,0 +1,18 @@ +# 椤圭洰鍏叡寮曞叆鏂囦欢 + +# 椤圭洰宸ョ▼鐩綍 +PROJECT_DIR = $${PWD} + +# 鍙墽琛屾枃浠剁洰褰 +BUILD_BIN = $${PROJECT_DIR}/bin + +# 鎵╁睍鎻掍欢鐩綍 +BUILD_PLUGINS = $${PROJECT_DIR}/plugins +# 閾炬帴搴撶洰褰 +BUILD_LIB = $${PROJECT_DIR}/lib +# 涓存椂鐢熸垚鏂囦欢鐩綍 +BUILD_TMP = $${PROJECT_DIR}/tmp +BUILD_MOC = $${BUILD_TMP} +BUILD_OBJ = $${BUILD_TMP} +BUILD_RCC = $${BUILD_TMP} +BUILD_UI = $${BUILD_TMP} diff --git a/EnergySpectrumMeasureService.pro b/EnergySpectrumMeasureService.pro new file mode 100644 index 0000000..e82346c --- /dev/null +++ b/EnergySpectrumMeasureService.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs +CONFIG += ordered + +SUBDIRS += \ + src + +OTHER_FILES += \ + $${PWD}/Common.pri \ No newline at end of file diff --git a/src/MeasureDeviceController.cpp b/src/MeasureDeviceController.cpp new file mode 100644 index 0000000..2a9ff1b --- /dev/null +++ b/src/MeasureDeviceController.cpp @@ -0,0 +1,256 @@ +#include "MeasureDeviceController.h" +#include "DeviceManage.h" +#include "QsLog.h" +#include +#include +#include +#include + +typedef int* DeviceHandler; +static sDeviceList s_device_list; + +static DeviceHandler GetDeviceHandler(const QString &device_guid) { + DeviceHandler device_handler = nullptr; + for (int i = 0; i < s_device_list.totalNum; i++) { + sDeviceInfo di; + int ret = GetDeviceInfo(s_device_list.pHandle[i], &di,-1,-1); + if (ret == 0) { + if (QString(di.GUID) == device_guid) { + device_handler = s_device_list.pHandle[i]; + } + } else { + QLOG_ERROR() << QStringLiteral(u"澶勭悊鏌ユ壘娴嬮噺璁惧缁撴灉淇℃伅澶辫触: GetDeviceInfo璋冪敤澶辫触绱㈠紩%1 [%2]").arg(i).arg(ret); + } + } + return device_handler; +} + +MeasureDeviceController::InitFinishedHandler MeasureDeviceController::s_init_finished_handler = nullptr; +MeasureDeviceController* MeasureDeviceController::_s_instance = nullptr; + +MeasureDeviceController::MeasureDeviceController(QObject *parent) + : QObject{parent} +{ +} + +MeasureDeviceController *MeasureDeviceController::Instance() +{ + if (!_s_instance) { + _s_instance = new MeasureDeviceController(); + } + return _s_instance; +} + +MeasureDeviceController::~MeasureDeviceController() +{ + +} + +void MeasureDeviceController::InitFindMeasureDevice() +{ + sFindDeviceConfig fdc; + fdc.useDHCP = false; + fdc.USB20Enable = false; + fdc.USB30Enable = true; + fdc.ETHEnable = FALSE; + fdc.COMEnable = true; + + int ret = FindDeviceAsync(&fdc, onFindDeviceFinishedCallback); + if (ret != 1) { + QLOG_ERROR() << QStringLiteral(u"璋冪敤娴嬮噺璁惧澶辫触:[杩斿洖浠g爜%1]").arg(ret); + } +} + +void MeasureDeviceController::onFindDeviceFinishedCallback(int ret, sDeviceList* pDeviceList) +{ + bool init_ok = true; + if (ret == 0) { + s_device_list = *pDeviceList; + if ( s_device_list.totalNum == 0 ) { + init_ok &= false; + QLOG_WARN() << QStringLiteral(u"鏈壘鍒版祴閲忚澶!"); + } + } else { + init_ok &= false; + QLOG_ERROR() << QStringLiteral(u"鏌ユ壘娴嬮噺璁惧澶辫触:[杩斿洖浠g爜%1]").arg(ret); + } + s_init_finished_handler(init_ok); +} + +void MeasureDeviceController::SetInitFinishedHandler(InitFinishedHandler handler) +{ + s_init_finished_handler = handler; +} + +QStringList MeasureDeviceController::GetMeasureDeviceList() +{ + QStringList device_guid_list; + for (int i = 0; i < s_device_list.totalNum; i++) { + sDeviceInfo di; + int ret = GetDeviceInfo(s_device_list.pHandle[i], &di,-1,-1); + if (ret == 0) { + device_guid_list.append(QString(di.GUID)); + } else { + QLOG_ERROR() << QStringLiteral(u"澶勭悊鏌ユ壘娴嬮噺璁惧缁撴灉淇℃伅澶辫触: GetDeviceInfo璋冪敤澶辫触绱㈠紩%1,[杩斿洖浠g爜%2]").arg(i).arg(ret); + } + } + return device_guid_list; +} + +bool MeasureDeviceController::SetDeviceMeasureConfigParams(const QString &device_guid, int board_id, int channel_id, const QVariantMap &cfg_params) +{ + bool ok = true; + DeviceHandler device_handler = GetDeviceHandler(device_guid); + if (!device_handler) { + ok = false; + return ok; + } + OneChannelConfigInfo config; + int ret = GetDeviceBoardChannelConfiginfo(device_handler, &config, board_id, channel_id); + if (ret != 0) { + QLOG_ERROR() << QStringLiteral(u"鏉垮崱%1閫氶亾%2鑾峰彇鍙傛暟閰嶇疆澶辫触:杩斿洖浠g爜[%3]").arg(board_id).arg(channel_id).arg(ret); + ok = false; + return ok; + } + config.m_iDeviceGain = cfg_params.value("DeviceGain", 1).toInt(); + config.m_iDeviceGainSelectIndex = cfg_params.value("DeviceGainSelectIndex", 1).toInt(); + config.m_iSoftGain = cfg_params.value("SoftGain", 3000).toInt(); + config.m_iChannelNum = cfg_params.value("AddrCount", 256).toInt(); + config.m_dConstTime = cfg_params.value("TimeConst", 45).toInt(); + config.m_iCurrentOffset = cfg_params.value("DcOffset", 0).toInt(); + config.m_iRiseTime = cfg_params.value("RiseTime", 2).toInt(); + config.m_iTopTime = cfg_params.value("FlatTime", 2).toInt(); + config.m_iMaxEnergy = cfg_params.value("MaxEnergy", 1).toInt(); + config.m_iTotalMeasureTime = cfg_params.value("TotalMeasureTime", 10).toInt(); + + config.m_eTransferModel = eSpecturmMode; + config.m_iFormTime = 3; + config.m_iFastChannelTrigerValue = 10; + config.m_iHighVoltage = 0; + config.m_iInputVoltageDesc = 0; + config.m_iCRDivMode = 0; + config.m_iInputSignalPostive = 0; + config.m_iAMPeakDiv = 0; + config.m_iHVDelt = 0; + config.m_iHVCtrl = 0; + config.m_iGetSpecturmPeirod = 1; + config.m_iTotalMeasureCount = 0; + config.m_iTrapeTopShitBit = 0; + config.m_bICRCorrect = 0; + config.m_iCRZAValue = 0; + config.m_iZAEnable = 0; + memset(config.reserve, 0, 128); + + ret = DBCSoftParamConfig(device_handler, &config, board_id, channel_id); + if (ret == 0) { + QLOG_INFO() << QStringLiteral(u"鏉垮崱%1閫氶亾%2鍚姩娴嬮噺鎴愬姛:杩斿洖浠g爜[%3]").arg(board_id).arg(channel_id).arg(ret); + } else { + QLOG_ERROR() << QStringLiteral(u"鏉垮崱%1閫氶亾%2鍚姩娴嬮噺澶辫触:杩斿洖浠g爜[%3]").arg(board_id).arg(channel_id).arg(ret); + ok = false; + return ok; + } + return ok; +} + +bool MeasureDeviceController::StartMeasure(const QString& device_guid, int board_id, int channel_id) +{ + bool ok = true; + DeviceHandler device_handler = GetDeviceHandler(device_guid); + if (!device_handler) { + ok = false; + return ok; + } + int ret = DBCStartMeasure(device_handler, board_id, channel_id); + if (ret == 0) { + QLOG_INFO() << QStringLiteral(u"鏉垮崱%1閫氶亾%2鍚姩娴嬮噺鎴愬姛:杩斿洖浠g爜[%3]").arg(board_id).arg(channel_id).arg(ret); + } else { + QLOG_ERROR() << QStringLiteral(u"鏉垮崱%1閫氶亾%2鍚姩娴嬮噺澶辫触:杩斿洖浠g爜[%3]").arg(board_id).arg(channel_id).arg(ret); + ok = false; + return ok; + } + _measure_start_time = QDateTime::currentDateTime(); + return ok; +} + +bool MeasureDeviceController::StopMeasure(const QString& device_guid, int board_id, int channel_id) +{ + bool ok = true; + DeviceHandler device_handler = GetDeviceHandler(device_guid); + if (!device_handler) { + ok = false; + return ok; + } + int ret = DBCStopMeasure(device_handler, board_id, channel_id); + if (ret == 0) { + QLOG_INFO() << QStringLiteral(u"鏉垮崱%1閫氶亾%2鍋滄娴嬮噺鎴愬姛:杩斿洖浠g爜[%3]").arg(board_id).arg(channel_id).arg(ret); + } else { + QLOG_ERROR() << QStringLiteral(u"鏉垮崱%1閫氶亾%2鍋滄娴嬮噺澶辫触:杩斿洖浠g爜[%3]").arg(board_id).arg(channel_id).arg(ret); + ok = false; + return ok; + } + return ok; +} + +bool MeasureDeviceController::StopMeasure(const QString &device_guid) +{ + bool ok = true; + DeviceHandler device_handler = GetDeviceHandler(device_guid); + if (!device_handler) { + ok = false; + return ok; + } + int board_count = GetDeviceBoardTotalNum(device_handler); + for (int board_id = 0; board_id < board_count; ++board_id) { + int _board_channel_count = GetDeviceChTotalNum(device_handler, board_id); + for (int channel_id = 0; channel_id < _board_channel_count; ++channel_id) { + DBCStopMeasure(device_handler, board_id, channel_id); + } + } + return ok; +} + +bool MeasureDeviceController::ClearData(const QString &device_guid) +{ + bool ok = true; + DeviceHandler device_handler = GetDeviceHandler(device_guid); + if (!device_handler) { + ok = false; + return ok; + } + int board_count = GetDeviceBoardTotalNum(device_handler); + for (int board_id = 0; board_id < board_count; ++board_id) { + int _board_channel_count = GetDeviceChTotalNum(device_handler, board_id); + for (int channel_id = 0; channel_id < _board_channel_count; ++channel_id) { + int ret = DBCClearData(device_handler, board_id, channel_id); + if (ret != 0) { + QLOG_ERROR() << QStringLiteral(u"鏉垮崱%1閫氶亾%2娓呯悊鏁版嵁澶辫触:杩斿洖浠g爜[%3]").arg(board_id).arg(channel_id).arg(ret); + ok &= false; + } + } + } + return ok; +} + +QString MeasureDeviceController::GetMeasureGvfDataFilename() +{ + QString measure_gvf_data_filename; + QDir measure_gvf_data_dir(QDir(qApp->applicationDirPath()).filePath("HistoryData")); + if (!measure_gvf_data_dir.exists()) { + return QString(); + } + QList gvf_file_list = measure_gvf_data_dir.entryList(QDir::Files|QDir::NoSymLinks|QDir::NoDotAndDotDot, QDir::Time); + for (const QString& gvf_filename : gvf_file_list) { + QFileInfo measure_gvf_data_file_info(measure_gvf_data_dir.filePath(gvf_filename)); + if (measure_gvf_data_file_info.exists()) { + if ( measure_gvf_data_file_info.suffix() != QString("gvf") ) + continue; + QString measure_gvf_data_file_base_name = measure_gvf_data_file_info.baseName(); + QDateTime data_time = QDateTime::fromString(measure_gvf_data_file_base_name, "GVHD__YYYY_MM_dd_hh_mm_ss_zzz"); + if ( qAbs(_measure_start_time.toMSecsSinceEpoch() - data_time.toMSecsSinceEpoch()) < 1000 ) { + measure_gvf_data_filename = measure_gvf_data_file_info.absoluteFilePath(); + } + } + } + return measure_gvf_data_filename; +} + diff --git a/src/MeasureDeviceController.h b/src/MeasureDeviceController.h new file mode 100644 index 0000000..22f386f --- /dev/null +++ b/src/MeasureDeviceController.h @@ -0,0 +1,44 @@ +#ifndef MEASUREDEVICECONTROLLER_H +#define MEASUREDEVICECONTROLLER_H + +#include +#include +#include +#include + +class sDeviceList; + +class MeasureDeviceController : public QObject +{ + Q_OBJECT + +public: + typedef std::function InitFinishedHandler; + +private: + explicit MeasureDeviceController(QObject *parent = nullptr); + static MeasureDeviceController* _s_instance; +public: + static MeasureDeviceController* Instance(); + virtual ~MeasureDeviceController(); + + void InitFindMeasureDevice(); + bool SetDeviceMeasureConfigParams(const QString& device_guid, int board_id, int channel_id, const QVariantMap &cfg_params); + bool StartMeasure(const QString& device_guid, int board_id, int channel_id); + bool StopMeasure(const QString& device_guid, int board_id, int channel_id); + bool StopMeasure(const QString& device_guid); + bool ClearData(const QString& device_guid); + QString GetMeasureGvfDataFilename(); + + static QStringList GetMeasureDeviceList(); +public: + static void onFindDeviceFinishedCallback(int ret, sDeviceList* pDeviceList); + void SetInitFinishedHandler(InitFinishedHandler handler); +private: + static InitFinishedHandler s_init_finished_handler; + +private: + QDateTime _measure_start_time; +}; + +#endif // MEASUREDEVICECONTROLLER_H diff --git a/src/MeasureServer.cpp b/src/MeasureServer.cpp new file mode 100644 index 0000000..7c4105f --- /dev/null +++ b/src/MeasureServer.cpp @@ -0,0 +1,25 @@ +#include "MeasureServer.h" +#include "MeasureTask.h" + +MeasureServer::MeasureServer(QObject *parent) + : QTcpServer(parent) +{ +} + +bool MeasureServer::Start(quint16 port) +{ + return this->listen(QHostAddress::Any, port); +} + +void MeasureServer::Stop() +{ + this->close(); +} + +void MeasureServer::incomingConnection(qintptr socketDescriptor) +{ + MeasureTask * measure_thread = new MeasureTask(socketDescriptor); + connect(measure_thread, &MeasureTask::finished, measure_thread, &MeasureTask::deleteLater); + _measure_thread->start(); +} + diff --git a/src/MeasureServer.h b/src/MeasureServer.h new file mode 100644 index 0000000..8e74fe2 --- /dev/null +++ b/src/MeasureServer.h @@ -0,0 +1,26 @@ +#ifndef MEASURESERVER_H +#define MEASURESERVER_H + +#include +#include + +class MeasureTask; + +class MeasureServer : public QTcpServer +{ + Q_OBJECT + +public: + MeasureServer(QObject *parent = nullptr); + bool Start(quint16 port = 96966); + void Stop(); + +protected: + void incomingConnection(qintptr socketDescriptor) override; + +private: + MeasureTask* _measure_thread = nullptr; +}; + + +#endif diff --git a/src/MeasureTask.cpp b/src/MeasureTask.cpp new file mode 100644 index 0000000..8fd28cf --- /dev/null +++ b/src/MeasureTask.cpp @@ -0,0 +1,198 @@ +#include "MeasureTask.h" +#include +#include "MeasureDeviceController.h" +#include +#include +#include +#include + + +MeasureTask::MeasureTask(int socketDescriptor, QObject *parent) + : QThread(parent), socketDescriptor(socketDescriptor) +{ +} + +void MeasureTask::run() +{ + QTcpSocket tcpSocket; + if (!tcpSocket.setSocketDescriptor(socketDescriptor)) { + return; + } + QByteArray buffer; + while (tcpSocket.waitForReadyRead(500)) { + buffer.append(tcpSocket.readAll()); + } + if ( buffer.size() ) { + QDataStream requst_data_stream(buffer); + QString cmd_type, device_guid, cmd_data; + requst_data_stream >> cmd_type >> device_guid; + if ( cmd_type == "START" ) { + requst_data_stream >> cmd_data; + processStartMeasureCmd(&tcpSocket, device_guid, cmd_data); + } else if (cmd_type == "STOP") { + processStopMeasureCmd(&tcpSocket, device_guid); + } else if (cmd_type == "SET") { + requst_data_stream >> cmd_data; + processSetDeviceMeasureConfigParamsCmd(&tcpSocket, device_guid, cmd_data); + } else if (cmd_type == "CLEAR") { + processClearDataCmd(&tcpSocket, device_guid); + } else if (cmd_type == "DEVICE") { + processGetMeasureDeviceListCmd(&tcpSocket); + } else { + QByteArray replay_data; + QDataStream replay_data_stream(replay_data); + replay_data_stream << QString("UNKNOW") << false << QStringLiteral(u"鏈煡璇锋眰"); + tcpSocket.write(replay_data); + tcpSocket.flush(); + } + } else { + QByteArray replay_data; + QDataStream replay_data_stream(replay_data); + replay_data_stream << QString("UNKNOW") << false << QStringLiteral(u"鏈煡璇锋眰"); + tcpSocket.write(replay_data); + tcpSocket.flush(); + } + tcpSocket.disconnectFromHost(); + tcpSocket.waitForDisconnected(1000); +} + +void MeasureTask::processStartMeasureCmd(QTcpSocket* socket, const QString &device_guid, const QString &cmd_data) +{ + QByteArray json_data = cmd_data.toUtf8(); + QJsonDocument json_doc = QJsonDocument::fromJson(json_data); + if (json_doc.isNull()) { + return; + } + if (!json_doc.isObject()) { + return; + } + QVariantMap device_config_info = json_doc.object().toVariantMap(); + if (!device_config_info.contains(QStringLiteral(u"ChannelConfig"))) { + return; + } + QVariantList channel_config_list = device_config_info[QStringLiteral(u"ChannelConfig")].toList(); + if (channel_config_list.isEmpty()) { + return; + } + bool ok = true; + for (auto channel_config : channel_config_list) { + if (!channel_config.isValid()) + continue; + QVariantMap channel_config_info = channel_config.toMap(); + if (!channel_config_info.contains("BoardId") || !channel_config_info.contains("ChannelId")) + continue; + int board_id = channel_config_info["BoardId"].toInt(); + int channel_id = channel_config_info["ChannelId"].toInt(); + ok &= MeasureDeviceController::Instance()->SetDeviceMeasureConfigParams(device_guid, board_id, channel_id, channel_config_info); + QByteArray replay_data; + QDataStream replay_data_stream(replay_data); + if (!ok) { + replay_data_stream << QString("START") << ok << QStringLiteral(u"鍚姩璁剧疆鏉垮崱%1閫氶亾%2娴嬮噺鍙傛暟澶辫触").arg(board_id).arg(channel_id); + } else { + replay_data_stream << QString("START") << ok << QStringLiteral(u"鍚姩璁剧疆鏉垮崱%1閫氶亾%2娴嬮噺鍙傛暟瀹屾垚").arg(board_id).arg(channel_id); + } + socket->write(replay_data); + socket->flush(); + ok &= MeasureDeviceController::Instance()->StartMeasure(device_guid, board_id, channel_id); + if (!ok) { + replay_data_stream << QString("START") << ok << QStringLiteral(u"鍚姩鏉垮崱%1閫氶亾%2娴嬮噺澶辫触"); + } else { + replay_data_stream << QString("START") << ok << QStringLiteral(u"鍚姩鏉垮崱%1閫氶亾%2娴嬮噺瀹屾垚"); + } + socket->write(replay_data); + socket->flush(); + } + if (!ok) { + // MeasureDeviceController::Instance()->StopMeasure(device_guid); + return; + } + const QString& measure_data_gvf_filename = MeasureDeviceController::Instance()->GetMeasureGvfDataFilename(); + if (measure_data_gvf_filename.isEmpty()) { + return; + } + QByteArray replay_data; + QDataStream replay_data_stream(replay_data); + replay_data_stream << QString("DATA") << true << measure_data_gvf_filename; + socket->write(replay_data); + socket->flush(); +} + +void MeasureTask::processStopMeasureCmd(QTcpSocket* socket, const QString &device_guid) +{ + MeasureDeviceController::Instance()->StopMeasure(device_guid); + QByteArray replay_data; + QDataStream replay_data_stream(replay_data); + replay_data_stream << QString("STOP") << true << QStringLiteral(u"鍋滄娴嬮噺瀹屾垚"); + socket->write(replay_data); + socket->flush(); +} + +void MeasureTask::processSetDeviceMeasureConfigParamsCmd(QTcpSocket* socket, const QString &device_guid, const QString &cmd_data) +{ + bool ok = true; + QByteArray json_data = cmd_data.toUtf8(); + QJsonDocument json_doc = QJsonDocument::fromJson(json_data); + if (json_doc.isNull()) { + return; + } + if (!json_doc.isObject()) { + return; + } + QVariantMap device_config_info = json_doc.object().toVariantMap(); + if (!device_config_info.contains(QStringLiteral(u"ChannelConfig"))) { + return; + } + QVariantList channel_config_list = device_config_info[QStringLiteral(u"ChannelConfig")].toList(); + if (channel_config_list.isEmpty()) { + return; + } + for (auto channel_config : channel_config_list) { + if (!channel_config.isValid()) + continue; + QVariantMap channel_config_info = channel_config.toMap(); + if (!channel_config_info.contains("BoardId") || !channel_config_info.contains("ChannelId")) + continue; + int board_id = channel_config_info["BoardId"].toInt(); + int channel_id = channel_config_info["ChannelId"].toInt(); + ok &= MeasureDeviceController::Instance()->SetDeviceMeasureConfigParams(device_guid, board_id, channel_id, channel_config_info); + QByteArray replay_data; + QDataStream replay_data_stream(replay_data); + if (!ok) { + replay_data_stream << QString("SET") << ok << QStringLiteral(u"璁剧疆鏉垮崱%1閫氶亾%2娴嬮噺鍙傛暟澶辫触").arg(board_id).arg(channel_id); + } else { + replay_data_stream << QString("SET") << ok << QStringLiteral(u"璁剧疆鏉垮崱%1閫氶亾%2娴嬮噺鍙傛暟瀹屾垚").arg(board_id).arg(channel_id); + } + socket->write(replay_data); + socket->flush(); + } +} + +void MeasureTask::processClearDataCmd(QTcpSocket* socket, const QString &device_guid) +{ + MeasureDeviceController::Instance()->ClearData(device_guid); + QByteArray replay_data; + QDataStream replay_data_stream(replay_data); + replay_data_stream << QString("CLEAR") << true << QStringLiteral(u"娓呴櫎鏁版嵁瀹屾垚"); + socket->write(replay_data); + socket->flush(); +} + +void MeasureTask::processGetMeasureDeviceListCmd(QTcpSocket* socket) +{ + QStringList device_list = MeasureDeviceController::Instance()->GetMeasureDeviceList(); + bool ok = !device_list.isEmpty(); + + QByteArray replay_data; + QDataStream replay_data_stream(replay_data); + replay_data_stream << QString("DEVICE"); + if ( ok ) { + replay_data_stream << ok << device_list.size(); + foreach (const QString& device_id, device_list) { + replay_data_stream << device_id; + } + } else { + replay_data_stream << ok; + } + socket->write(replay_data); + socket->flush(); +} diff --git a/src/MeasureTask.h b/src/MeasureTask.h new file mode 100644 index 0000000..a97608a --- /dev/null +++ b/src/MeasureTask.h @@ -0,0 +1,30 @@ +#ifndef MEASURETASK_H +#define MEASURETASK_H + +#include +#include + +class MeasureTask : public QThread +{ + Q_OBJECT + +public: + MeasureTask(int socketDescriptor, QObject *parent = nullptr); + + void run() override; + +private: + void processStartMeasureCmd(QTcpSocket* socket, const QString& device_guid, const QString& cmd_data); + void processStopMeasureCmd(QTcpSocket* socket, const QString& device_guid); + void processSetDeviceMeasureConfigParamsCmd(QTcpSocket* socket, const QString& device_guid, const QString& cmd_data); + void processClearDataCmd(QTcpSocket* socket, const QString& device_guid); + void processGetMeasureDeviceListCmd(QTcpSocket* socket); + +signals: + void error(QTcpSocket::SocketError socketError); + +private: + int socketDescriptor; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d14a368 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,24 @@ +#include "MeasureDeviceController.h" +#include "MeasureServer.h" +#include "QsLogManage.h" +#include + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + // 鍒涘缓鏃ュ織璁板綍鍣 + QsLogManage::createLogger(); + MeasureDeviceController::Instance()->SetInitFinishedHandler( + [&](bool init_ok) { + if (init_ok) { + MeasureServer measure_server; + if (!measure_server.Start()) { + QLOG_ERROR() << measure_server.errorString(); + } + } else { + app.exit(EXIT_FAILURE); + } + }); + MeasureDeviceController::Instance()->InitFindMeasureDevice(); + return app.exec(); +} diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000..de16ac8 --- /dev/null +++ b/src/src.pro @@ -0,0 +1,45 @@ +TARGET = EnergySpectrumMeasureService + +QT += core concurrent network sql + +CONFIG += c++17 release +msvc { + QMAKE_CFLAGS += /utf-8 + QMAKE_CXXFLAGS += /utf-8 +} + + +include($${PWD}/../Common.pri) +include($${PROJECT_DIR}/3rdlib/QsLog/QsLog.pri) +include($${PROJECT_DIR}/3rdlib/csv/csv.pri) +include($${PROJECT_DIR}/3rdlib/DeviceManage/DeviceManage.pri) + +DESTDIR = $${BUILD_BIN} +OBJECTS_DIR = $${BUILD_OBJ}/$${TARGET}/objs +MOC_DIR = $${BUILD_MOC}/$${TARGET}/moc +UI_DIR = $${BUILD_UI}/$${TARGET}/ui + + +SOURCES += \ + $${PWD}/MeasureServer.cpp \ + $${PWD}/MeasureTask.cpp \ + $${PWD}/main.cpp \ + MeasureDeviceController.cpp + +HEADERS += \ + $${PWD}/MeasureServer.h \ + $${PWD}/MeasureTask.h \ + MeasureDeviceController.h + + +DEFINES += ENABLE_DEBUG +contains(DEFINES, ENABLE_DEBUG) { + CONFIG += console + win32-msvc* { + QMAKE_CXXFLAGS_RELEASE -= -O2 + QMAKE_CXXFLAGS_RELEASE += /MD /Zi /O + QMAKE_LFLAGS_RELEASE += /DEBUG + } +} + +