1*01826a49SYabin Cui /* ****************************************************************** 2*01826a49SYabin Cui * huff0 huffman codec, 3*01826a49SYabin Cui * part of Finite State Entropy library 4*01826a49SYabin Cui * Copyright (c) Meta Platforms, Inc. and affiliates. 5*01826a49SYabin Cui * 6*01826a49SYabin Cui * You can contact the author at : 7*01826a49SYabin Cui * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy 8*01826a49SYabin Cui * 9*01826a49SYabin Cui * This source code is licensed under both the BSD-style license (found in the 10*01826a49SYabin Cui * LICENSE file in the root directory of this source tree) and the GPLv2 (found 11*01826a49SYabin Cui * in the COPYING file in the root directory of this source tree). 12*01826a49SYabin Cui * You may select, at your option, one of the above-listed licenses. 13*01826a49SYabin Cui ****************************************************************** */ 14*01826a49SYabin Cui 15*01826a49SYabin Cui #if defined (__cplusplus) 16*01826a49SYabin Cui extern "C" { 17*01826a49SYabin Cui #endif 18*01826a49SYabin Cui 19*01826a49SYabin Cui #ifndef HUF_H_298734234 20*01826a49SYabin Cui #define HUF_H_298734234 21*01826a49SYabin Cui 22*01826a49SYabin Cui /* *** Dependencies *** */ 23*01826a49SYabin Cui #include "zstd_deps.h" /* size_t */ 24*01826a49SYabin Cui #include "mem.h" /* U32 */ 25*01826a49SYabin Cui #define FSE_STATIC_LINKING_ONLY 26*01826a49SYabin Cui #include "fse.h" 27*01826a49SYabin Cui 28*01826a49SYabin Cui 29*01826a49SYabin Cui /* *** Tool functions *** */ 30*01826a49SYabin Cui #define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ 31*01826a49SYabin Cui size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ 32*01826a49SYabin Cui 33*01826a49SYabin Cui /* Error Management */ 34*01826a49SYabin Cui unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */ 35*01826a49SYabin Cui const char* HUF_getErrorName(size_t code); /**< provides error code string (useful for debugging) */ 36*01826a49SYabin Cui 37*01826a49SYabin Cui 38*01826a49SYabin Cui #define HUF_WORKSPACE_SIZE ((8 << 10) + 512 /* sorting scratch space */) 39*01826a49SYabin Cui #define HUF_WORKSPACE_SIZE_U64 (HUF_WORKSPACE_SIZE / sizeof(U64)) 40*01826a49SYabin Cui 41*01826a49SYabin Cui /* *** Constants *** */ 42*01826a49SYabin Cui #define HUF_TABLELOG_MAX 12 /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_TABLELOG_ABSOLUTEMAX */ 43*01826a49SYabin Cui #define HUF_TABLELOG_DEFAULT 11 /* default tableLog value when none specified */ 44*01826a49SYabin Cui #define HUF_SYMBOLVALUE_MAX 255 45*01826a49SYabin Cui 46*01826a49SYabin Cui #define HUF_TABLELOG_ABSOLUTEMAX 12 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ 47*01826a49SYabin Cui #if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX) 48*01826a49SYabin Cui # error "HUF_TABLELOG_MAX is too large !" 49*01826a49SYabin Cui #endif 50*01826a49SYabin Cui 51*01826a49SYabin Cui 52*01826a49SYabin Cui /* **************************************** 53*01826a49SYabin Cui * Static allocation 54*01826a49SYabin Cui ******************************************/ 55*01826a49SYabin Cui /* HUF buffer bounds */ 56*01826a49SYabin Cui #define HUF_CTABLEBOUND 129 57*01826a49SYabin Cui #define HUF_BLOCKBOUND(size) (size + (size>>8) + 8) /* only true when incompressible is pre-filtered with fast heuristic */ 58*01826a49SYabin Cui #define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ 59*01826a49SYabin Cui 60*01826a49SYabin Cui /* static allocation of HUF's Compression Table */ 61*01826a49SYabin Cui /* this is a private definition, just exposed for allocation and strict aliasing purpose. never EVER access its members directly */ 62*01826a49SYabin Cui typedef size_t HUF_CElt; /* consider it an incomplete type */ 63*01826a49SYabin Cui #define HUF_CTABLE_SIZE_ST(maxSymbolValue) ((maxSymbolValue)+2) /* Use tables of size_t, for proper alignment */ 64*01826a49SYabin Cui #define HUF_CTABLE_SIZE(maxSymbolValue) (HUF_CTABLE_SIZE_ST(maxSymbolValue) * sizeof(size_t)) 65*01826a49SYabin Cui #define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \ 66*01826a49SYabin Cui HUF_CElt name[HUF_CTABLE_SIZE_ST(maxSymbolValue)] /* no final ; */ 67*01826a49SYabin Cui 68*01826a49SYabin Cui /* static allocation of HUF's DTable */ 69*01826a49SYabin Cui typedef U32 HUF_DTable; 70*01826a49SYabin Cui #define HUF_DTABLE_SIZE(maxTableLog) (1 + (1<<(maxTableLog))) 71*01826a49SYabin Cui #define HUF_CREATE_STATIC_DTABLEX1(DTable, maxTableLog) \ 72*01826a49SYabin Cui HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = { ((U32)((maxTableLog)-1) * 0x01000001) } 73*01826a49SYabin Cui #define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) \ 74*01826a49SYabin Cui HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = { ((U32)(maxTableLog) * 0x01000001) } 75*01826a49SYabin Cui 76*01826a49SYabin Cui 77*01826a49SYabin Cui /* **************************************** 78*01826a49SYabin Cui * Advanced decompression functions 79*01826a49SYabin Cui ******************************************/ 80*01826a49SYabin Cui 81*01826a49SYabin Cui /** 82*01826a49SYabin Cui * Huffman flags bitset. 83*01826a49SYabin Cui * For all flags, 0 is the default value. 84*01826a49SYabin Cui */ 85*01826a49SYabin Cui typedef enum { 86*01826a49SYabin Cui /** 87*01826a49SYabin Cui * If compiled with DYNAMIC_BMI2: Set flag only if the CPU supports BMI2 at runtime. 88*01826a49SYabin Cui * Otherwise: Ignored. 89*01826a49SYabin Cui */ 90*01826a49SYabin Cui HUF_flags_bmi2 = (1 << 0), 91*01826a49SYabin Cui /** 92*01826a49SYabin Cui * If set: Test possible table depths to find the one that produces the smallest header + encoded size. 93*01826a49SYabin Cui * If unset: Use heuristic to find the table depth. 94*01826a49SYabin Cui */ 95*01826a49SYabin Cui HUF_flags_optimalDepth = (1 << 1), 96*01826a49SYabin Cui /** 97*01826a49SYabin Cui * If set: If the previous table can encode the input, always reuse the previous table. 98*01826a49SYabin Cui * If unset: If the previous table can encode the input, reuse the previous table if it results in a smaller output. 99*01826a49SYabin Cui */ 100*01826a49SYabin Cui HUF_flags_preferRepeat = (1 << 2), 101*01826a49SYabin Cui /** 102*01826a49SYabin Cui * If set: Sample the input and check if the sample is uncompressible, if it is then don't attempt to compress. 103*01826a49SYabin Cui * If unset: Always histogram the entire input. 104*01826a49SYabin Cui */ 105*01826a49SYabin Cui HUF_flags_suspectUncompressible = (1 << 3), 106*01826a49SYabin Cui /** 107*01826a49SYabin Cui * If set: Don't use assembly implementations 108*01826a49SYabin Cui * If unset: Allow using assembly implementations 109*01826a49SYabin Cui */ 110*01826a49SYabin Cui HUF_flags_disableAsm = (1 << 4), 111*01826a49SYabin Cui /** 112*01826a49SYabin Cui * If set: Don't use the fast decoding loop, always use the fallback decoding loop. 113*01826a49SYabin Cui * If unset: Use the fast decoding loop when possible. 114*01826a49SYabin Cui */ 115*01826a49SYabin Cui HUF_flags_disableFast = (1 << 5) 116*01826a49SYabin Cui } HUF_flags_e; 117*01826a49SYabin Cui 118*01826a49SYabin Cui 119*01826a49SYabin Cui /* **************************************** 120*01826a49SYabin Cui * HUF detailed API 121*01826a49SYabin Cui * ****************************************/ 122*01826a49SYabin Cui #define HUF_OPTIMAL_DEPTH_THRESHOLD ZSTD_btultra 123*01826a49SYabin Cui 124*01826a49SYabin Cui /*! HUF_compress() does the following: 125*01826a49SYabin Cui * 1. count symbol occurrence from source[] into table count[] using FSE_count() (exposed within "fse.h") 126*01826a49SYabin Cui * 2. (optional) refine tableLog using HUF_optimalTableLog() 127*01826a49SYabin Cui * 3. build Huffman table from count using HUF_buildCTable() 128*01826a49SYabin Cui * 4. save Huffman table to memory buffer using HUF_writeCTable() 129*01826a49SYabin Cui * 5. encode the data stream using HUF_compress4X_usingCTable() 130*01826a49SYabin Cui * 131*01826a49SYabin Cui * The following API allows targeting specific sub-functions for advanced tasks. 132*01826a49SYabin Cui * For example, it's possible to compress several blocks using the same 'CTable', 133*01826a49SYabin Cui * or to save and regenerate 'CTable' using external methods. 134*01826a49SYabin Cui */ 135*01826a49SYabin Cui unsigned HUF_minTableLog(unsigned symbolCardinality); 136*01826a49SYabin Cui unsigned HUF_cardinality(const unsigned* count, unsigned maxSymbolValue); 137*01826a49SYabin Cui unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, void* workSpace, 138*01826a49SYabin Cui size_t wkspSize, HUF_CElt* table, const unsigned* count, int flags); /* table is used as scratch space for building and testing tables, not a return value */ 139*01826a49SYabin Cui size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize); 140*01826a49SYabin Cui size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags); 141*01826a49SYabin Cui size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue); 142*01826a49SYabin Cui int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue); 143*01826a49SYabin Cui 144*01826a49SYabin Cui typedef enum { 145*01826a49SYabin Cui HUF_repeat_none, /**< Cannot use the previous table */ 146*01826a49SYabin Cui HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, 4}X_repeat */ 147*01826a49SYabin Cui HUF_repeat_valid /**< Can use the previous table and it is assumed to be valid */ 148*01826a49SYabin Cui } HUF_repeat; 149*01826a49SYabin Cui 150*01826a49SYabin Cui /** HUF_compress4X_repeat() : 151*01826a49SYabin Cui * Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. 152*01826a49SYabin Cui * If it uses hufTable it does not modify hufTable or repeat. 153*01826a49SYabin Cui * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. 154*01826a49SYabin Cui * If preferRepeat then the old table will always be used if valid. 155*01826a49SYabin Cui * If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */ 156*01826a49SYabin Cui size_t HUF_compress4X_repeat(void* dst, size_t dstSize, 157*01826a49SYabin Cui const void* src, size_t srcSize, 158*01826a49SYabin Cui unsigned maxSymbolValue, unsigned tableLog, 159*01826a49SYabin Cui void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ 160*01826a49SYabin Cui HUF_CElt* hufTable, HUF_repeat* repeat, int flags); 161*01826a49SYabin Cui 162*01826a49SYabin Cui /** HUF_buildCTable_wksp() : 163*01826a49SYabin Cui * Same as HUF_buildCTable(), but using externally allocated scratch buffer. 164*01826a49SYabin Cui * `workSpace` must be aligned on 4-bytes boundaries, and its size must be >= HUF_CTABLE_WORKSPACE_SIZE. 165*01826a49SYabin Cui */ 166*01826a49SYabin Cui #define HUF_CTABLE_WORKSPACE_SIZE_U32 ((4 * (HUF_SYMBOLVALUE_MAX + 1)) + 192) 167*01826a49SYabin Cui #define HUF_CTABLE_WORKSPACE_SIZE (HUF_CTABLE_WORKSPACE_SIZE_U32 * sizeof(unsigned)) 168*01826a49SYabin Cui size_t HUF_buildCTable_wksp (HUF_CElt* tree, 169*01826a49SYabin Cui const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, 170*01826a49SYabin Cui void* workSpace, size_t wkspSize); 171*01826a49SYabin Cui 172*01826a49SYabin Cui /*! HUF_readStats() : 173*01826a49SYabin Cui * Read compact Huffman tree, saved by HUF_writeCTable(). 174*01826a49SYabin Cui * `huffWeight` is destination buffer. 175*01826a49SYabin Cui * @return : size read from `src` , or an error Code . 176*01826a49SYabin Cui * Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */ 177*01826a49SYabin Cui size_t HUF_readStats(BYTE* huffWeight, size_t hwSize, 178*01826a49SYabin Cui U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr, 179*01826a49SYabin Cui const void* src, size_t srcSize); 180*01826a49SYabin Cui 181*01826a49SYabin Cui /*! HUF_readStats_wksp() : 182*01826a49SYabin Cui * Same as HUF_readStats() but takes an external workspace which must be 183*01826a49SYabin Cui * 4-byte aligned and its size must be >= HUF_READ_STATS_WORKSPACE_SIZE. 184*01826a49SYabin Cui * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0. 185*01826a49SYabin Cui */ 186*01826a49SYabin Cui #define HUF_READ_STATS_WORKSPACE_SIZE_U32 FSE_DECOMPRESS_WKSP_SIZE_U32(6, HUF_TABLELOG_MAX-1) 187*01826a49SYabin Cui #define HUF_READ_STATS_WORKSPACE_SIZE (HUF_READ_STATS_WORKSPACE_SIZE_U32 * sizeof(unsigned)) 188*01826a49SYabin Cui size_t HUF_readStats_wksp(BYTE* huffWeight, size_t hwSize, 189*01826a49SYabin Cui U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr, 190*01826a49SYabin Cui const void* src, size_t srcSize, 191*01826a49SYabin Cui void* workspace, size_t wkspSize, 192*01826a49SYabin Cui int flags); 193*01826a49SYabin Cui 194*01826a49SYabin Cui /** HUF_readCTable() : 195*01826a49SYabin Cui * Loading a CTable saved with HUF_writeCTable() */ 196*01826a49SYabin Cui size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned *hasZeroWeights); 197*01826a49SYabin Cui 198*01826a49SYabin Cui /** HUF_getNbBitsFromCTable() : 199*01826a49SYabin Cui * Read nbBits from CTable symbolTable, for symbol `symbolValue` presumed <= HUF_SYMBOLVALUE_MAX 200*01826a49SYabin Cui * Note 1 : If symbolValue > HUF_readCTableHeader(symbolTable).maxSymbolValue, returns 0 201*01826a49SYabin Cui * Note 2 : is not inlined, as HUF_CElt definition is private 202*01826a49SYabin Cui */ 203*01826a49SYabin Cui U32 HUF_getNbBitsFromCTable(const HUF_CElt* symbolTable, U32 symbolValue); 204*01826a49SYabin Cui 205*01826a49SYabin Cui typedef struct { 206*01826a49SYabin Cui BYTE tableLog; 207*01826a49SYabin Cui BYTE maxSymbolValue; 208*01826a49SYabin Cui BYTE unused[sizeof(size_t) - 2]; 209*01826a49SYabin Cui } HUF_CTableHeader; 210*01826a49SYabin Cui 211*01826a49SYabin Cui /** HUF_readCTableHeader() : 212*01826a49SYabin Cui * @returns The header from the CTable specifying the tableLog and the maxSymbolValue. 213*01826a49SYabin Cui */ 214*01826a49SYabin Cui HUF_CTableHeader HUF_readCTableHeader(HUF_CElt const* ctable); 215*01826a49SYabin Cui 216*01826a49SYabin Cui /* 217*01826a49SYabin Cui * HUF_decompress() does the following: 218*01826a49SYabin Cui * 1. select the decompression algorithm (X1, X2) based on pre-computed heuristics 219*01826a49SYabin Cui * 2. build Huffman table from save, using HUF_readDTableX?() 220*01826a49SYabin Cui * 3. decode 1 or 4 segments in parallel using HUF_decompress?X?_usingDTable() 221*01826a49SYabin Cui */ 222*01826a49SYabin Cui 223*01826a49SYabin Cui /** HUF_selectDecoder() : 224*01826a49SYabin Cui * Tells which decoder is likely to decode faster, 225*01826a49SYabin Cui * based on a set of pre-computed metrics. 226*01826a49SYabin Cui * @return : 0==HUF_decompress4X1, 1==HUF_decompress4X2 . 227*01826a49SYabin Cui * Assumption : 0 < dstSize <= 128 KB */ 228*01826a49SYabin Cui U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize); 229*01826a49SYabin Cui 230*01826a49SYabin Cui /** 231*01826a49SYabin Cui * The minimum workspace size for the `workSpace` used in 232*01826a49SYabin Cui * HUF_readDTableX1_wksp() and HUF_readDTableX2_wksp(). 233*01826a49SYabin Cui * 234*01826a49SYabin Cui * The space used depends on HUF_TABLELOG_MAX, ranging from ~1500 bytes when 235*01826a49SYabin Cui * HUF_TABLE_LOG_MAX=12 to ~1850 bytes when HUF_TABLE_LOG_MAX=15. 236*01826a49SYabin Cui * Buffer overflow errors may potentially occur if code modifications result in 237*01826a49SYabin Cui * a required workspace size greater than that specified in the following 238*01826a49SYabin Cui * macro. 239*01826a49SYabin Cui */ 240*01826a49SYabin Cui #define HUF_DECOMPRESS_WORKSPACE_SIZE ((2 << 10) + (1 << 9)) 241*01826a49SYabin Cui #define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32)) 242*01826a49SYabin Cui 243*01826a49SYabin Cui 244*01826a49SYabin Cui /* ====================== */ 245*01826a49SYabin Cui /* single stream variants */ 246*01826a49SYabin Cui /* ====================== */ 247*01826a49SYabin Cui 248*01826a49SYabin Cui size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags); 249*01826a49SYabin Cui /** HUF_compress1X_repeat() : 250*01826a49SYabin Cui * Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. 251*01826a49SYabin Cui * If it uses hufTable it does not modify hufTable or repeat. 252*01826a49SYabin Cui * If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. 253*01826a49SYabin Cui * If preferRepeat then the old table will always be used if valid. 254*01826a49SYabin Cui * If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */ 255*01826a49SYabin Cui size_t HUF_compress1X_repeat(void* dst, size_t dstSize, 256*01826a49SYabin Cui const void* src, size_t srcSize, 257*01826a49SYabin Cui unsigned maxSymbolValue, unsigned tableLog, 258*01826a49SYabin Cui void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */ 259*01826a49SYabin Cui HUF_CElt* hufTable, HUF_repeat* repeat, int flags); 260*01826a49SYabin Cui 261*01826a49SYabin Cui size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); 262*01826a49SYabin Cui #ifndef HUF_FORCE_DECOMPRESS_X1 263*01826a49SYabin Cui size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); /**< double-symbols decoder */ 264*01826a49SYabin Cui #endif 265*01826a49SYabin Cui 266*01826a49SYabin Cui /* BMI2 variants. 267*01826a49SYabin Cui * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0. 268*01826a49SYabin Cui */ 269*01826a49SYabin Cui size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags); 270*01826a49SYabin Cui #ifndef HUF_FORCE_DECOMPRESS_X2 271*01826a49SYabin Cui size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); 272*01826a49SYabin Cui #endif 273*01826a49SYabin Cui size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags); 274*01826a49SYabin Cui size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); 275*01826a49SYabin Cui #ifndef HUF_FORCE_DECOMPRESS_X2 276*01826a49SYabin Cui size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags); 277*01826a49SYabin Cui #endif 278*01826a49SYabin Cui #ifndef HUF_FORCE_DECOMPRESS_X1 279*01826a49SYabin Cui size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags); 280*01826a49SYabin Cui #endif 281*01826a49SYabin Cui 282*01826a49SYabin Cui #endif /* HUF_H_298734234 */ 283*01826a49SYabin Cui 284*01826a49SYabin Cui #if defined (__cplusplus) 285*01826a49SYabin Cui } 286*01826a49SYabin Cui #endif 287