zoukankan      html  css  js  c++  java
  • SQLite 自定义函数,聚合,排序规则

    SQLite 自定义函数,聚合,排序规则

    1.使用自定义函数, 聚合以及排序规则的基本方法是使用回调函数.
    这些注册的函数的生命周期只存在于应用程序中, 并不存储在数据库文件中, 因此需要在每个连接建立时注册才可以在 SQL 中进行使用.

    2.排序规则
    SQLite 对结果集中的字段进行排序时, SQLite 使用比较操作符如 < 或 >= 在字段内对值进行比较, 第一件事就是根据存储类对字段值进行排列.
    然后在每种存储类中, 根据该类指定的方法进行排序. 存储类进行排序的顺序从前往后如下所示:
    NULL 值 < INTEGER 和 REAL 值 < TEXT 值 < BLOB 值
    (1) NULL 值具有最低的类值, 一个具有 NULL 存储类的值比所有其他的值都小(包括其他具有 NULL 存储类的值). 在 NULL 值之间, 没有具体的排序顺序.
    (2) INTEGER 或 REAL 存储类值高于 NULL, 它们的类值相等. INTEGER 值和 REAL 值通过其数值进行比较.
    (3) TEXT 存储类的值比 INTEGER 和 REAL 髙. 数值永远比字符串的值低. 当两个 TEXT 值进行比较时, 其值大小由该值中定义的 “排序法” 决定.
    (4) BLOB 存储类具有最高的类值. 具有 BLOB 类的值大于其他所有类的值. BLOB 值之间在比较时使用 C 函数 memcmp.
    对 TEXT 存储类的数据, 可以在创建表时指定字段排序规则, 也可以直接在查询中指定它们
    CREATE TABLE Foo(x TEXT COLLATE NOCASE);
    SELECT * FROM Foo ORDER BY x COLLATE NOCASE;

    3.相关 API
    (1) 注册自定义函数, 聚合函数
    int sqlite3_create_function_v2(
    sqlite3* db,
    const char* zFunctionName,
    int nArg,
    int eTextRep,
    void* pApp,
    void (*xFunc)(sqlite3_context* ctx, int argc, sqlite3_value** argv),
    void (*xStep)(sqlite3_context* ctx, int argc, sqlite3_value** argv),
    void (*xFinal)(sqlite3_context* ctx),
    void (*xDestroy)(void* pApp)
    );
    db: 数据库连接句柄, 函数和聚合的指定连接. 若要使用函数, 必须在连接上注册
    zFunctionName: 在 SQL 语句中使用的函数,聚合名称, 长度限制 255 字节
    nArg: 函数参数个数, 如果为-1, 表示可变长参数. SQLite 会强制检查参数个数, 确保传给自定义函数的参数格式是正确的
    eTextRep: 首先文本编码格式, 如 SQLITE_UTF8, SQLITE_UTF16
    pApp: 传递给回调函数的应用程序数据, 可供在 xFunc, xStep, xFinal 指定的回调函数中使用, 但必须使用特殊的 API 函数获取数据
    xFunc: 回调函数
    xStep: 聚合步骤函数, SQLite 处理聚合结果集中的每一行都要调用 xStep, 此函数内部处理聚合逻辑, 并保存到结果值中
    xFinal: finalize 聚合函数, 处理完所有的行后 SQLite 调用该函数进行整个聚合汇总处理, 此函数内部一般设置聚合功能完成的结果
    在自定义函数中,指定 xFunc, 同时 xStep 和 xFinal 设置为 nullptr. 自定义聚合函数则与前者刚好相反, 需设置 xStep 和 xFinal, 同时设置 xFunc 为 nullptr.
    只要 nArg 指定参数的个数不同, 或 eTextRep 指定的编码不同, 同一个 zFunctionName 指定的函数就可以注册多个版本, SQLite 会自动选择最佳版本
    xDestroy: 清理函数, 用于释放应用程序数据 pApp.

    (2) int sqlite3_create_collation_v2(
    sqlite3* db,
    const char* zFunctionName,
    int eTextRep,
    void* pApp,
    int (*xCompare)(void* pApp, int lLen, const void* lData, int rLen, const void* rData),
    void (*xDestroy)(void* pApp)
    );
    各个参数的意义同 sqlite3_create_function_v2. 唯一不同的是比较函数指针 xCompare, 此参数指定用于比较的函数.

    (3) void xFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv);
    ctx: 函数/聚合的上下文环境. 它保持特殊函数调用的实例状态, 通过它可以获得 sqlite3_create_function_v2 供的应用程序数据参数 pApp.
    要获取该数据可以使用 void* sqlite3_user_data(sqlite3_context* ctx);
    argc: 参数个数
    argv: 参数值数组, 使用前需配合 argc 验证是否越界.

    (4) int xCompare(void* pApp, int lLen, const void* lData, int rLen, const void* rData);
    pApp: 在 sqlite3_create_collation_v2 设置的应用程序数据
    lLen: 参数1的长度
    lData: 参数1的指针
    rLen: 参数2的长度
    rData: 参数2的指针
    PS: 参数1和参数2传入 const void*, 转换为 const char* 后, 字符串不一定是以 '' 结束. 因此类似以下的操作结果值不确定:
    string lText{static_cast<const char*>(lData)};
    应使用 string 的另一个构造函数
    string lText{static_cast<const char*>(lData), 0, lLen};

    (5) void *sqlite3_user_data(sqlite3_context* ctx);
    取得 sqlite3_create_function_v2 调用时传入的 pApp 参数值. 对所有的回调函数, 此函数返回的参数是共享的.

    (6) void* sqlite3_aggregate_context(sqlite3_context* ctx, int nBytes);
    此函数为每个特定的实例分配状态, 第一次调用该函数时执行一个特殊的聚合函数, 分配 nBytes 字节内存, 并赋值为0.
    注意, 分配内存后会赋值为0, 了解这一点非常重要, 这要求我们设置自定义类型保存聚合数据时, 必须是 POD 类型.
    此后同一个 ctx 指定的上下文环境中的调用(同一个聚合的实例), 返回相同的数据, 即地址相同.
    聚合函数必须要使用此函数, 因此聚合的目前, 就是可以在调用之间存储状态以便堆积数据.
    当聚合完成 finalize()回调时, 由 SQLite 自动释放内存, 无须手动释放.

    (7) Type sqlite3_value_Type(sqlite3_value* value);
    返回 Type 类型的数据. 获取标量值:
    double sqlite3_value_double(sqlite3_value*);
    int sqlite3_value_int(sqlite3_value*);
    sqlite3_int64 sqlite3_value_int64(sqlite3_value*);
    获取数组值:
    int sqlite3_value_bytes(sqlite3_value*); // 返回 blob 缓冲区中数据长度
    const void* sqlite3_value_blob(sqlite3_value*); // 返回 blob 缓冲区中数据指针
    const unsigned char* sqlite3_value_text(sqlite3_value*); // 注意返回值不是 const char*
    检查数据类型的函数:
    int sqlite3_value_type(sqlite3_value*); // SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT
    int sqlite3_value_numeric_type(sqlite3_value*);
    配对使用 sqlite3_value_bytes 和 sqlite3_value_blob 函数, 就可以从结果中复制数据. 如:
    int valueLen = sqlite3_value_bytes(argv[0]);
    char* buf = static_cast<char*>(sqlite3_malloc(valueLen + 1));

    (8) 设置结果, SQL 查询语句中返回的值
    void sqlite3_result_text(sqlite3_context* ctx, const char* data, int len, void(*free)(void* data));
    void sqlite3_result_blob(sqlite3_context* ctx, const void* data, int bytes, void(*free)(void* data));
    void sqlite3_result_double(sqlite3_context*, double);
    void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
    void sqlite3_result_null(sqlite3_context*);
    其中 pCleaner 指向释放内存的函数指针
    设置出错信息
    void sqlite3_result_error(sqlite3_context*, const char*, int);
    void sqlite3_result_error_toobig(sqlite3_context*);
    void sqlite3_result_error_nomem(sqlite3_context*);
    void sqlite3_result_error_code(sqlite3_context*, int);
    清理内存
    void free(void* p); 用户自定义函数调用完成后, 调用指定的清理函数释放内存. 即 sqlite3_result_text 和 sqlite3_result_blob 的实参 data
    预定义清理函数
    #define SQLITE_STATIC ((void(*)(void *))0)
    #define SQLITE_TRANSIENT ((void(*)(void*))-l)
    SQLITE_STATIC 意味着数组内存驻留在非托管空间, SQLite 不需要数据副本, 也不会试图清理它.
    SQLITE_TRANSIENT 提示数组数据有可能改变, SQLite 使用 sqlite3_malloc 为自己复制一份数据.

    (9) 内存分配与释放
    void* sqlite3_malloc(int);
    void* sqlite3_realloc(void*, int);
    void sqlite3_free(void*);

    //////////////////////////////////////////////////////////////////////////
    // 测试代码
    #include <cassert>
    #include <fstream>
    #include <iostream>
    #include <regex>
    #include <sstream>
    #include <string>
    #include "Memory.h"
    #include "Rand.h"
    #include "sqlite3.h"
    
    using namespace std;
    
    
    //#define OUTPUT_FILE
    
    #if defined(OUTPUT_FILE)
    #define ERR fout << "[" << __func__ << ":" << __LINE__ << "] Error! "
    #define INFO fout
    #define DEBUG fout
    #else
    #define ERR cerr << "[" << __func__ << ":" << __LINE__ << "] Error! "
    #define INFO cout
    #define DEBUG cerr
    #endif
    #define MEMINFO(msg) Memory::print(msg)
    
    
    
    #if defined(OUTPUT_FILE)
    fstream fout;
    #endif
    
    void initOutputStream()
    {
    #if defined(OUTPUT_FILE)
        fout.open("F:/Sqlite/mysql/log.txt", fstream::ios_base::out | fstream::ios_base::trunc);
    #endif
    }
    
    void testClean(void* p)
    {
        DEBUG << "clean:" << reinterpret_cast<int>(p) << endl;
    }
    
    string getString(sqlite3_value* value)
    {
        const char* text = reinterpret_cast<const char*>(sqlite3_value_text(value));
        return text ? string{text} : "";
    }
    
    /**
     * 将字符串拆分成非全数字组成的前缀,和全数字组成的后缀
     */
    bool splitName(const string& text, string& prefix, string& suffix)
    {
        regex reg("^(.*[^\d]+)(\d+)$|^(\d+)$");
        smatch match;
        if (regex_match(text, match, reg))
        {
            prefix = match.str(1);
            suffix = prefix.empty() ? match.str(3) : match.str(2);
            return true;
        }
        prefix = text;
        return false;
    }
    
    /**
     * 去掉字符串前面的0
     */
    string trimPrefixZero(const string& text)
    {
        size_t pos = text.find_first_not_of('0');
        if (string::npos != pos && 0 != pos)
        {
            return text.substr(pos);
        }
        return text;
    }
    
    /**
     * 比较两个数字字符串序列的大小
     */
    int compareDigit(const string& lhs, const string& rhs)
    {
        string lText = trimPrefixZero(lhs);
        string rText = trimPrefixZero(rhs);
        if (lText.length() != rText.length())
        {
            return lText.length() - rText.length();
        }
        for (size_t i = 0; i < lText.length(); ++i)
        {
            if (lText.at(i) != rText.at(i))
            {
                return lText.at(i) - rText.at(i);
            }
        }
        return 0;
    }
    
    sqlite3* openDB(const string& file)
    {
        sqlite3* db{};
        int ret = sqlite3_open_v2(file.c_str(), &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, 0);
        if (SQLITE_OK != ret)
        {
            ERR << "sqlite3_open_v2 failed." << endl;
            return nullptr;
        }
        return db;
    }
    
    bool closeDB(sqlite3* db)
    {
        int ret = sqlite3_close_v2(db);
        if (SQLITE_OK != ret)
        {
            ERR << "sqlite3_close_v2 failed." << endl;
            return false;
        }
        return true;
    }
    
    bool execSQL(sqlite3* db, const string& sql)
    {
        char* errmsg{};
        int ret = sqlite3_exec(db, sql.c_str(), 0, 0, &errmsg);
        if (SQLITE_OK != ret)
        {
            ERR << "sqlite3_exec failed." << errmsg << endl;
        }
        sqlite3_free(errmsg);
        return SQLITE_OK == ret;
    }
    
    bool printSQL(sqlite3* db, const string& sql)
    {
        int ret{};
        sqlite3_stmt* stmt{};
        ret = sqlite3_prepare_v2(db, sql.c_str(), sql.length(), &stmt, 0);
        if (SQLITE_OK != ret)
        {
            ERR << "sqlite3_prepare_v2 failed." << sqlite3_errmsg(db) << endl;
            return false;
        }
    
        INFO << sql << endl;
        int colCount = sqlite3_column_count(stmt);
        if (colCount > 0)
        {
            for (int col = 0; col < colCount; )
            {
                INFO << sqlite3_column_name(stmt, col);
                if (++col != colCount)
                {
                    INFO << '	';
                }
            }
            INFO << endl;
        }
    
        do
        {
            ret = sqlite3_step(stmt);
            if (SQLITE_ROW != ret)
            {
                break;
            }
            for (int col = 0; col < colCount; )
            {
                const char* text = reinterpret_cast<const char*>(sqlite3_column_text(stmt, col));
                INFO << (text ? text : "");
                if (++col != colCount)
                {
                    INFO << '	';
                }
            }
            INFO << endl;
        } while (true);
        if (SQLITE_DONE != ret)
        {
            ERR << "sqlite3_step failed." << sqlite3_errmsg(db) << endl;
        }
    
        ret = sqlite3_finalize(stmt);
        if (SQLITE_OK != ret)
        {
            ERR << "sqlite3_finalize failed." << sqlite3_errmsg(db) << endl;
            return false;
        }
    
        return true;
    }
    
    // SQLite 函数
    void echo(sqlite3_context* ctx, int argc, sqlite3_value** argv)
    {
        assert(1 == argc);
        // DEBUG << "user data:" << reinterpret_cast<int>(sqlite3_user_data(ctx)) << endl;
        const char* text{reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))};
        sqlite3_result_text(ctx, text, -1, 0);
    }
    
    void strcat_column(sqlite3_context* ctx, int argc, sqlite3_value** argv)
    {
        const char* delimiter{"	"};
        const int DELIMITER_LEN = strlen(delimiter);
        int len = 0;
        for (int i = 0; i < argc; ++i)
        {
            len += sqlite3_value_bytes(argv[i]);
            len += DELIMITER_LEN;
        }
        char* buf = static_cast<char*>(sqlite3_malloc(len));
        int pos = 0;
        for (int i = 0; i < argc; )
        {
            int bytes = sqlite3_value_bytes(argv[i]);
            memcpy(buf + pos, sqlite3_value_blob(argv[i]), bytes);
            pos += bytes;
    
            if (++i != argc)
            {
                memcpy(buf + pos, delimiter, DELIMITER_LEN);
                pos += DELIMITER_LEN;
            }
        }
        buf[pos] = 0;
        sqlite3_result_text(ctx, buf, -1, 0);
    }
    
    /**
     * 找出值为匹配前缀,并且后缀在指定范围内的值
     */
    void peekName(sqlite3_context* ctx, int argc, sqlite3_value** argv)
    {
        assert(4 == argc);
    
        bool isFound{false};
        string vPrefix;
        string vSuffix;
        string value = getString(argv[0]);
        if (splitName(value, vPrefix, vSuffix))
        {
            string prefix = getString(argv[1]);
            if (vPrefix == prefix)
            {
                string suffixFrom = getString(argv[2]);
                string suffixTo = getString(argv[3]);
                if (compareDigit(vSuffix, suffixFrom) >= 0 && compareDigit(vSuffix, suffixTo) <= 0)
                {
                    isFound = true;
                }
            }
        }
    
        sqlite3_result_int(ctx, isFound);
    }
    
    
    // SQLite 聚合函数
    struct AggregateCharData
    {
        int len{};
        char* buf{};
    };
    
    void strcat_step(sqlite3_context* ctx, int argc, sqlite3_value** argv)
    {
        assert(2 == argc);
        AggregateCharData* pData{static_cast<AggregateCharData*>(sqlite3_aggregate_context(ctx, sizeof(AggregateCharData)))};
    
        if (!pData)
        {
            ERR << "Alloc AggregateData failed!" << endl;
            return;
        }
    
        int valueLen = sqlite3_value_bytes(argv[0]);
        if (!pData->buf)
        {
            pData->buf = static_cast<char*>(sqlite3_malloc(valueLen + 1));
        }
        else
        {
            int delimiterLen = sqlite3_value_bytes(argv[1]);
            int len = valueLen + delimiterLen + pData->len + 1;
            pData->buf = static_cast<char*>(sqlite3_realloc(pData->buf, len));
            memcpy(pData->buf + pData->len, sqlite3_value_blob(argv[1]), delimiterLen);
            pData->len += delimiterLen;
        }
        memcpy(pData->buf + pData->len, sqlite3_value_blob(argv[0]), valueLen);
        pData->len += valueLen;
    }
    
    void strcat_final(sqlite3_context* ctx)
    {
        AggregateCharData* pData{static_cast<AggregateCharData*>(sqlite3_aggregate_context(ctx, sizeof(AggregateCharData)))};
        if (!pData || !pData->buf)
        {
            sqlite3_result_text(ctx, pData->buf, pData->len, sqlite3_free);
        }
    }
    
    
    /**
     * SQLite 排序示例,将值拆分成非全数字组成的前缀,和全数字组成的后缀,前缀不同时,按字典排序;前缀相同时,后缀按数字大小排序
    **/
    int compareName(void*, int lhsLen, const void* lhsData, int rhsLen, const void* rhsData)
    {
        string lText(static_cast<const char*>(lhsData), 0, lhsLen);
        string lPrefix;
        string lSuffix;
        if (!splitName(lText, lPrefix, lSuffix))
        {
            return 0;
        }
    
        string rText(static_cast<const char*>(rhsData), 0, rhsLen);
        string rPrefix;
        string rSuffix;
        if (!splitName(rText, rPrefix, rSuffix))
        {
            return 0;
        }
    
        if (lPrefix != rPrefix)
        {
            return lPrefix.compare(rPrefix);
        }
    
        return compareDigit(lSuffix, rSuffix);
    }
    
    string randNumString()
    {
        static const string NUMBERS{"012345678900"};
        string ret;
        int len = Rand::rand(3, 10);
        for (int i = 0; i < len; ++i)
        {
            ret.push_back(NUMBERS.at(Rand::rand(0, NUMBERS.length() - 1)));
        }
        return ret;
    }
    
    string randPrefix()
    {
        static const string PREFIX_STR{"01234567890123456789012345678901234567890123456789"
                                       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-"};
        string ret;
        int len = Rand::rand(3, 8);
        for (int i = 0; i < len; ++i)
        {
            ret.push_back(PREFIX_STR.at(Rand::rand(0, PREFIX_STR.length() - 1)));
        }
        return ret;
    }
    
    void insertTestData(sqlite3* db)
    {
        execSQL(db, "INSERT INTO Foo VALUES(null, 1, 2);");
        execSQL(db, "INSERT INTO Foo VALUES(null, 10.5, 20.1);");
        execSQL(db, "INSERT INTO Foo VALUES(null, 'abc', 'hehe');");
    
        vector<string> prefixs{randPrefix(), randPrefix(), randPrefix(), randPrefix()};
        ostringstream stm;
        for (int i = 0; i < 30; ++i)
        {
            string prefix = prefixs.at(Rand::rand(0, prefixs.size() - 1));
            stm.str("");
            stm << "INSERT INTO Foo VALUES(null, '" << prefix << randNumString() << "', "
                << Rand::rand(1, 999) << ");";
            execSQL(db, stm.str());
    
            stm.str("");
            stm << "INSERT INTO Foo VALUES(null, '" << prefix << randNumString() << "', "
                << Rand::rand(1, 999) << ");";
            execSQL(db, stm.str());
    
            stm.str("");
            stm << "INSERT INTO Foo VALUES(null, '" << prefix << randNumString() << "', "
                << Rand::rand(1, 999) << ");";
            execSQL(db, stm.str());
        }
    }
    
    int main(int, char**)
    {
        initOutputStream();
    
        MEMINFO("start");
        sqlite3* db = openDB("Foo.db");
    
        int ret{};
        MEMINFO("create table");
        execSQL(db, "CREATE TABLE IF NOT EXISTS Foo"
                    "(_id INTEGER PRIMARY KEY, name TEXT, info TEXT);");
    
        // insertTestData(db);
    
        MEMINFO("query all");
        printSQL(db, "SELECT * FROM Foo;");
    
        MEMINFO("set echo");
        ret = sqlite3_create_function_v2(db, "echo", 1, SQLITE_UTF8, reinterpret_cast<void*>(1), echo, 0, 0, testClean);
        if (SQLITE_OK != ret)
        {
            ERR << "sqlite3_create_function_v2 -> echo." << sqlite3_errmsg(db) << endl;
        }
        MEMINFO("query using echo");
        printSQL(db, "SELECT echo('Hello SQLite!') AS replay;");
        printSQL(db, "SELECT _id, echo(name) AS name FROM Foo;");
    
        MEMINFO("set strcat_column");
        ret = sqlite3_create_function_v2(db, "strcat_column", -1, SQLITE_UTF8, 0, strcat_column, 0, 0, 0);
        if (SQLITE_OK != ret)
        {
            ERR << "sqlite3_create_function_v2 -> strcat_column." << sqlite3_errmsg(db) << endl;
        }
        MEMINFO("query using strcat_column");
        printSQL(db, "SELECT strcat_column(_id, name, info) AS list FROM Foo;");
    
        MEMINFO("set strcat");
        ret = sqlite3_create_function_v2(db, "strcat", 2, SQLITE_UTF8, 0, 0, strcat_step, strcat_final, 0);
        MEMINFO("query using strcat");
        if (SQLITE_OK != ret)
        {
            ERR << "sqlite3_create_function_v2 -> strcat_column." << sqlite3_errmsg(db) << endl;
        }
        printSQL(db, "SELECT strcat(_id, ',') AS ids FROM Foo;");
        printSQL(db, "SELECT strcat(name, ',') AS names FROM Foo;");
        printSQL(db, "SELECT strcat(info, ',') AS infos FROM Foo;");
    
        MEMINFO("set peekName");
        ret = sqlite3_create_function_v2(db, "peekName", 4, SQLITE_UTF8, 0, peekName, 0, 0, 0);
        MEMINFO("query using peekName");
        if (SQLITE_OK != ret)
        {
            ERR << "sqlite3_create_function_v2 -> peekName." << sqlite3_errmsg(db) << endl;
        }
        printSQL(db, "SELECT * FROM Foo WHERE peekName(name, '9Y58h', 293, 62804);");
    
        MEMINFO("set compareName");
        ret = sqlite3_create_collation_v2(db, "compareName", SQLITE_UTF8, reinterpret_cast<void*>(2), compareName, testClean);
        MEMINFO("query using compareName");
        if (SQLITE_OK != ret)
        {
            ERR << "sqlite3_create_collation_v2 -> compareName." << sqlite3_errmsg(db) << endl;
        }
        printSQL(db, "SELECT * FROM Foo ORDER BY name collate compareName;");
    
        MEMINFO("close db");
        closeDB(db);
    
        MEMINFO("end");
        return 0;
    }
    //////////////////////////////////////////////////////////////////////////
    // 随机数
    
    #ifndef RAND_H
    #define RAND_H
    
    #include <random>
    
    
    class Rand
    {
    public:
        static int rand(int minValue, int maxValue);
    
    private:
        static std::default_random_engine DEFAULT_ENGINE;
    };
    
    #endif // RAND_H
        
        
    #include "Rand.h"
    #include <ctime>
    
    
    std::default_random_engine Rand::DEFAULT_ENGINE(time(0));
    
    int Rand::rand(int minValue, int maxValue)
    {
        std::uniform_int_distribution<int> d(minValue, maxValue);
        return d(DEFAULT_ENGINE);
    }
    //////////////////////////////////////////////////////////////////////////
    // 内存打印工具
    
    #ifndef MEMORY_H
    #define MEMORY_H
    
    #include <string>
    
    using std::string;
    
    
    class Memory
    {
    public:
        static void print(const string& tag = "");
    
    private:
        static string format(long long);
        static long long m_prevAvaiPhys;
    };
    
    #endif // MEMORY_H
    
    
    #include "Memory.h"
    #include <iostream>
    #include <sstream>
    #include "windows.h"
    
    
    long long Memory::m_prevAvaiPhys{};
    
    void Memory::print(const string& tag)
    {
        MEMORYSTATUSEX mem{};
        mem.dwLength = sizeof(MEMORYSTATUSEX);
        GlobalMemoryStatusEx(&mem);
        std::cout << "virtual:" << format(mem.ullAvailVirtual) << "/" << format(mem.ullTotalVirtual)
                  << " physical:" << format(mem.ullAvailPhys) << "/" << format(mem.ullTotalPhys);
    
        if (0 != m_prevAvaiPhys)
        {
            std::cout << ", reduce physical:" << (long long)(m_prevAvaiPhys - mem.ullAvailPhys) << "B";
        }
        m_prevAvaiPhys = mem.ullAvailPhys;
    
        std::cout << " [" << tag <<  "]" << std::endl;
    }
    
    string Memory::format(long long bytes)
    {
        std::ostringstream stm;
        //stm << (bytes >> 20) << "MB";
        stm << (bytes >> 10) << "KB";
        return stm.str();
    }

     

  • 相关阅读:
    每日总结3.8
    Go中定时器实现原理及源码解析
    Go语言实现布谷鸟过滤器
    详解Go语言调度循环源码实现
    Go语言中时间轮的实现
    详解Go语言I/O多路复用netpoller模型
    详解Go中内存分配源码实现
    Go中由WaitGroup引发对内存对齐思考
    【分享】java精品实战教程
    nginx实战教程
  • 原文地址:https://www.cnblogs.com/diysoul/p/8052201.html
Copyright © 2011-2022 走看看