• Redis源代码分析(五)--- sparkline微线图


                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结构体事实上“信息点”元素的意思了。里面非常easy。一个文字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,"
    ",1);
            }
        }
        zfree(chars);
        return output;
    }

    因为本人能力有限,有点不太懂里面的详细细节,大概看了下,把变量用到的地方稍稍看了下,上面的代码都是很优秀的代码,值得我们学习。今天至少让我知道了什么叫sparkline叫什么 了,哈哈。

  • 相关阅读:
    leetcode 买卖股票的最佳时机3
    leetcode 买卖股票的最佳时机Ⅱ
    leetcode 最长有效括号
    C++中的%lld和%I64d区别
    Ural 1095 Nikifor 3 思维+同余性质的利用
    博弈基础
    ural 1091. Tmutarakan Exams
    容斥原理
    一些易错的地方
    codeforces911D Inversion Counting 求逆序数+小trick
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/6781555.html
Copyright © 2020-2023  润新知