Redis源碼分析(五)--- sparkline微線圖,redissparkline

來源:互聯網
上載者:User

Redis源碼分析(五)--- sparkline微線圖,redissparkline

            sparkline這個單詞,我第一次看的時候,也不知道這什麼意思啊,以前根本沒聽過啊,但是這真真實實的出現在了redis的代碼中了,剛剛開始以為這也是屬於普通的隊列嘛,就把他分在了struct包裡了。好來分析完了,與原本我所想的差太大了。sparkline英文中的意思“微線圖”,這麼說吧,類似於折線圖,由一個一個資訊點構成。所以看到這個意思,你或許就明白了sparkline.c是幹什麼用的了吧,就是畫圖用的。我們看看這個畫圖的內部結構是什麼,畫圖需要的元素是哪些:

/* sparkline.h -- ASCII Sparklines header file * * --------------------------------------------------------------------------- * * Copyright(C) 2011-2014 Salvatore Sanfilippo <antirez@gmail.com> * 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. * * 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 OWNER 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 __SPARKLINE_H#define __SPARKLINE_H/* sparkline是一類資訊體積小和資料密度高的圖表。目前它被用作一些測量, *相關的變化的資訊呈現的方式,如平均溫度,股市交投活躍。sparkline常常以一組多條的形式出現在柱狀圖,折線圖當中。 *可以理解為一個圖線資訊 *//* A sequence is represented of many "samples" *//* 可以理解為映像上的一個資訊點,有文字,有值的大小 */struct sample {    double value;    char *label;};/* 圖線資訊結構體,包括n個元素點,可以據此描述出圖,繪圖的可不是直接按點和值直接繪製的 */struct sequence {//當前元素點個數    int length;    //總共的文字個數,有些點沒有label描述,為NULL    int labels;    //元素點列表    struct sample *samples;    //元素中的最大值,最小值    double min, max;};/* 定義了一些渲染圖時候一些屬性操作設定 */#define SPARKLINE_NO_FLAGS 0#define SPARKLINE_FILL 1      /* Fill the area under the curve. */#define SPARKLINE_LOG_SCALE 2 /* Use logarithmic scale. */struct sequence *createSparklineSequence(void);  //建立圖線序列結構體void sparklineSequenceAddSample(struct sequence *seq, double value, char *label); //在圖線序列中添加一個資訊點void freeSparklineSequence(struct sequence *seq);   //釋放圖線序列sds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags); //渲染圖線序列為一個圖,其實就是得到一個字串組成的圖sds sparklineRender(sds output, struct sequence *seq, int columns, int rows, int flags); //方法同上,只是少可一個位移量#endif /* __SPARKLINE_H */

我們看到上面的sample結構體其實“資訊點”元素的意思了,裡面很簡單,一個文字label,一個value值,簡潔明了,然後sequence就自然是圖線了,裡面就定義了元素點列表了,裡面還有線長度,和最大值,最小值,資訊還挺全面的線。後序的畫圖操作都是根據這個圖線序列結構體操作的。結構體一點都不複雜。但是畫圖的實現一點都不簡單,如何根據給定的一些點資訊畫出一個類似折線的圖線呢,可別忘了,這是要在命令列視窗的圖線哦,所以不會像進階語言中的GUI的操作那樣很方便,我們看看redis代碼中是怎麼寫的。

/* sparkline.c -- ASCII Sparklines * This code is modified from http://github.com/antirez/aspark and adapted * in order to return SDS strings instead of outputting directly to * the terminal. * * ---------------------------------------------------------------------------

在sparkline.c中的注釋聲明,此代碼修改自 http://github.com/antirez/aspark,原來是開源的代碼實現,但是一開始真的不知道還有這麼個叫aspark的東西,都跟BigData裡的spark搞混了,然後我點擊此地址,官方解釋來了:

aspark is a C program to display ASCII Sparklines.It is completely useless in 2011.

不錯,意思就是說aspark就是用來在C程式上顯示圖線效果的。後來,我看了下,的確代碼差不多,redis的代碼在上面加了自己的東西,稍稍修改,aspark的圖線展現有幾種形式,第一種,最簡單的展示:

$ ./aspark 1,2,3,4,10,7,6,5    `-_ __-`   `
第二張把行數擴充為更多行,展示更多的資料,上面的這個為2行展示,資料多的時候,調節行數,預設輸出2行展示

$ ./aspark 1,2,3,4,5,6,7,8,9,10,10,8,5,3,1 --rows 4       _-``_        _`           -`       `  _-`          `_

當然可以更加可視化,在空白處填充字元,看起來更舒服:

$ ./aspark 1,2,3,4,5,6,7,8,9,10,10,8,5,3,1 --rows 4 --fill       _o##_        _#|||||      o#|||||||#  _o#||||||||||#_
用了"|"符號,很有想象力的哦,最最關鍵的我們看如何?這樣的效果呢,核心代碼如下:

/* Render part of a sequence, so that render_sequence() call call this function * with differnent parts in order to create the full output without overflowing * the current terminal columns. *//* 渲染出這個圖線資訊 */sds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags) {    int j;    double relmax = seq->max - seq->min;    int steps = charset_len*rows;    int row = 0;    char *chars = zmalloc(len);    int loop = 1;    int opt_fill = flags & SPARKLINE_FILL;    int opt_log = flags & SPARKLINE_LOG_SCALE;    if (opt_log) {        relmax = log(relmax+1);    } else if (relmax == 0) {        relmax = 1;    }    while(loop) {        loop = 0;        memset(chars,' ',len);        for (j = 0; j < len; j++) {            struct sample *s = &seq->samples[j+offset];            //value派上用處了            double relval = s->value - seq->min;            int step;            if (opt_log) relval = log(relval+1);            //最後會算出相關的step            step = (int) (relval*steps)/relmax;            if (step < 0) step = 0;            if (step >= steps) step = steps-1;            if (row < rows) {                /* Print the character needed to create the sparkline */                /* step控制輸出的字元是哪一個 */                int charidx = step-((rows-row-1)*charset_len);                loop = 1;                if (charidx >= 0 && charidx < charset_len) {                    chars[j] = opt_fill ? charset_fill[charidx] :                                          charset[charidx];                } else if(opt_fill && charidx >= charset_len) {                //用"|"填充內容,更加可視化                    chars[j] = '|';                }            } else {                /* Labels spacing */                if (seq->labels && row-rows < label_margin_top) {                    loop = 1;                    break;                }                /* Print the label if needed. */                if (s->label) {                    int label_len = strlen(s->label);                    int label_char = row - rows - label_margin_top;                    if (label_len > label_char) {                        loop = 1;                        chars[j] = s->label[label_char];                    }                }            }        }        if (loop) {            row++;            output = sdscatlen(output,chars,len);            output = sdscatlen(output,"\n",1);        }    }    zfree(chars);    return output;}

由於本人能力有限,有點不太懂裡面的具體細節,大概看了下,把變數用到的地方稍稍看了下,上面的代碼都是非常優秀的代碼,值得我們學習,今天至少讓我知道了什麼叫sparkline叫什麼 了,哈哈。




相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.