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

sparkline这个单词,我第一次看的时候。也不知道这什么意思啊,曾经根本没听过啊,可是这真真实实的出如今了redis的代码中了,刚刚開始以为这也是属于普通的队列嘛。就把他分在了struct包里了。

好来分析完了。与原本我所想的差太大了。sparkline英文中的意思“微线图”,这么说吧。类似于折线图,由一个一个信息点构成。所以看到这个意思。你也许就明确了sparkline.c是干什么用的了吧,就是绘图用的。

我们看看这个绘图的内部结构是什么,绘图须要的元素是哪些:

/* sparkline.h -- ASCII Sparklines header file
 *
 * ---------------------------------------------------------------------------
 *
 * Copyright(C) 2011-2014 Salvatore Sanfilippo <[email protected]>
 * 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,"\n",1);
        }
    }
    zfree(chars);
    return output;
}

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

时间: 2024-08-01 21:47:05

Redis源代码分析(五)--- sparkline微线图的相关文章

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

sparkline这个单词,我第一次看的时候,也不知道这什么意思啊,以前根本没听过啊,但是这真真实实的出现在了redis的代码中了,刚刚开始以为这也是属于普通的队列嘛,就把他分在了struct包里了.好来分析完了,与原本我所想的差太大了.sparkline英文中的意思"微线图",这么说吧,类似于折线图,由一个一个信息点构成.所以看到这个意思,你或许就明白了sparkline.c是干什么用的了吧,就是画图用的.我们看看这个画图的内部结构是什么,画图需要的元素是哪些: /* sparkli

redis学习笔记(8)---微线图sparkline

sparkline 微线图,即用一个一个信息点形成的图,类似于折线图.在表示时延的时候,就可以用微线图的形式来表示. 示例: 微线图中的信息点可以有两种形式:"_-`" 和 "_o#" 两种情况最后形成的微线图分别如下所示: 可以看到,用一个一个的信息点,非常形象明了的形成了所需的折线图 sparkline的实现 sparkline中每个信息点的定义为: struct sample { double value; char *label; }; value为该点的值

redis 源代码分析(一) 内存管理

一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中统一使用zmalloc,zfree一系列函数,其相应的源代码在src/zmalloc.h和src/zmalloc.c两个文件里,源代码点这里. 二,redis内存管理源代码分析 redis封装是为了屏蔽底层平台的差异,同一时候方便自己实现相关的函数,我们能够通过src/zmalloc.h 文件里的相

[Android] Volley源代码分析(五岁以下儿童)Q \\ u0026一个

Volley源代码分析系列那里一段时间,告诉我,有许多私人留言,同时一些问题抛出.对于一些简单的问题,我们跳,这两天被连接到朋友@smali提出的问题.告诉我你不得不赞叹查看源代码时的详细程度,大家一块思考一下. Q:在写入文件头数据的时候为何不直接写入Int而是通过移位的方式来完毕? 我们来看一下相应的源代码: writeInt(os, CACHE_MAGIC); static void writeInt(OutputStream os, int n) throws IOException {

Redis源代码分析(二十八)--- object创建和释放redisObject物

今天的学习更有效率.该Rio分析过,学习之间的另一种方式RedisObject文件,只想说RedisObject有些生成和转换.都是很类似的.列出里面长长的API列表: /* ------------ API --------------------- */ robj *createObject(int type, void *ptr) /* 最初的创建robj对象方法,后面的创建方法与此相似 */ robj *createStringObject(char *ptr, size_t len)

Redis源代码分析(八)--- t_hash哈希转换

在上次的zipmap分析完之后,事实上关于redis源码结构体部分的内容事实上已经所有结束了.由于以下还有几个和结构体相关的操作类,就页把他们归并到struct包下了.这类的文件有:t_hash.c,z_list,z_set.c,t_string.c,t_zset.c,这些文件的功能事实上都差点儿相同,就是用来实现Client和Server之间的命令处理的操作类,通过robj的形式,把dict,ziplist等存入robj中,进行各个转换.实现命令操作.避开了结构体原先的复杂结构,相当于是封装了

Redis源代码分析(二十四)--- tool工具类(2)

在上篇文章中初步的分析了一下,Redis工具类文件里的一些使用方法,包含2个随机算法和循环冗余校验算法,今天,继续学习Redis中的其它的一些辅助工具类的使用方法.包含里面的大小端转换算法,sha算法在Redis中的实现和通用工具类算法util.c. 先来看看大小端转换算法,大小端学习过操作系统的人一定知道是什么意思,在不同的操作系统中,高位数字的存储方式存在,高位在前,低位在后,或是高位在后,低位在前,所以这里面就涉及到转换,依据不同的操作系统,有不同的转换方式,所以Redis在这方面就开放了

Redis源代码分析(十七)--- multi事务操作

redis作为一非关系型数据库,居然相同拥有与RDBMS的事务操作,不免让我认为比較吃惊.在redis就专门有文件就是运行事务的相关操作的.也能够让我们领略一下.在Redis的代码中是怎样实现事务操作.首先亮出mulic.c以下的一些API. /* ================================ MULTI/EXEC ============================== */ void initClientMultiState(redisClient *c) /* 初始

Redis源代码分析(二十七)--- rio制I/O包裹

I/O每个操作系统,它的一个组成部分.和I/O业务质量,在一定程度上也影响了系统的效率. 今天,我在了解了Redis中间I/O的,相同的,Redis在他自己的系统中.也封装了一个I/O层.简称RIO.得先看看RIO中有什么东西喽: struct _rio { /* Backend functions. * Since this functions do not tolerate short writes or reads the return * value is simplified to: